Skip to content

Commit 00767d7

Browse files
authored
Merge pull request #47 from kmaork/py11
* Support python3.11 * Add fail-fast=False * Tests refactor * Bump
2 parents 749d7a9 + 9b02eae commit 00767d7

9 files changed

Lines changed: 97 additions & 93 deletions

File tree

.github/workflows/tests.yml

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,39 +2,38 @@ name: Tests
22

33
on:
44
workflow_dispatch:
5+
inputs:
6+
deploy:
7+
description: 'Release this branch'
8+
required: false
9+
type: boolean
510
release:
6-
types: [created]
11+
types: [ created ]
712
push:
813
pull_request:
914

1015
jobs:
1116
test_manylinux:
1217
runs-on: ubuntu-latest
1318
strategy:
19+
fail-fast: false
1420
matrix:
15-
python-version: ["3.7", "3.8", "3.9", "3.10"]
21+
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"]
1622
steps:
1723
- uses: actions/checkout@v2
1824
- name: Set up Python ${{ matrix.python-version }}
1925
uses: actions/setup-python@v2
2026
with:
2127
python-version: ${{ matrix.python-version }}
2228
- name: Install dependencies
23-
run: pip install tox tox-gh-actions tox-wheel
24-
- name: Test sdist with tox
29+
run: pip install tox==4.6.3 tox-gh==1.2.0
30+
- name: Test with tox
2531
run: python -m tox
26-
- name: Upload sdist
27-
uses: actions/upload-artifact@v2
28-
with:
29-
name: dist
30-
path: .tox/dist/*
31-
- name: Test wheel with tox
32-
run: python -m tox --wheel
33-
- name: Upload wheel
32+
- name: Upload dists
3433
uses: actions/upload-artifact@v2
3534
with:
3635
name: dist
37-
path: .tox/dist/*
36+
path: .tox/.pkg/dist/*
3837

3938
test_alpine:
4039
runs-on: ubuntu-latest
@@ -47,6 +46,7 @@ jobs:
4746
- "python:3.8-alpine"
4847
- "python:3.9-alpine"
4948
- "python:3.10-alpine"
49+
- "python:3.11-alpine"
5050
steps:
5151
- name: Install packages
5252
# git needed for checkout
@@ -55,12 +55,12 @@ jobs:
5555
with:
5656
submodules: true
5757
- name: Install dependencies
58-
run: pip install tox tox-gh-actions
59-
- name: Test sdist with tox
58+
run: pip install tox==4.6.3 tox-gh==1.2.0
59+
- name: Test with tox
6060
run: python -m tox
6161

6262
publish:
63-
if: github.event_name == 'release' && github.event.action == 'created'
63+
if: (github.event_name == 'release' && github.event.action == 'created') || inputs.deploy
6464
needs: [test_manylinux, test_alpine]
6565
runs-on: ubuntu-latest
6666
steps:

madbg/__main__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ def cli():
2222
def connect(ip, port, timeout):
2323
try:
2424
connect_to_debugger(ip, port, timeout=timeout)
25-
except ConnectionRefusedError:
25+
except (ConnectionRefusedError, TimeoutError):
2626
raise ClickException('Connection refused - did you use the right port?')
2727

2828

setup.cfg

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ install_requires =
99

1010
[metadata]
1111
name = madbg
12-
version = 1.3.1
12+
version = 1.3.2
1313
description = A fully-featured remote debugger for python
1414
author = Maor Kleinberger
1515
author_email = kmaork@gmail.com
@@ -27,6 +27,7 @@ classifiers =
2727
Programming Language :: Python :: 3.8
2828
Programming Language :: Python :: 3.9
2929
Programming Language :: Python :: 3.10
30+
Programming Language :: Python :: 3.11
3031
Operating System :: POSIX :: Linux
3132

3233
[options.entry_points]

tests/system/test_post_mortem.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import madbg
22

3-
from .utils import run_in_process, run_script_in_process, JOIN_TIMEOUT, run_client
3+
from .utils import run_in_process, run_script_in_process, run_client
44

55

66
def post_mortem_script(port):
@@ -11,8 +11,5 @@ def post_mortem_script(port):
1111

1212

1313
def test_post_mortem(port, start_debugger_with_ctty):
14-
debugger_future = run_script_in_process(post_mortem_script, start_debugger_with_ctty, port)
15-
assert not debugger_future.done()
16-
client_future = run_in_process(run_client, port, b'c\n')
17-
debugger_future.result(JOIN_TIMEOUT)
18-
client_future.result(JOIN_TIMEOUT)
14+
with run_script_in_process(post_mortem_script, start_debugger_with_ctty, port):
15+
run_in_process(run_client, port, b'c\n').finish()
Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
import time
21
from pytest import raises
32
from madbg import run_with_debugging
43

5-
from .utils import run_script_in_process, JOIN_TIMEOUT, SCRIPTS_PATH, run_in_process, run_client
4+
from .utils import run_script_in_process, SCRIPTS_PATH, run_in_process, run_client
65

76

87
def run_divide_with_zero_with_debugging_script(port, post_mortem, set_trace):
@@ -11,20 +10,13 @@ def run_divide_with_zero_with_debugging_script(port, post_mortem, set_trace):
1110

1211

1312
def test_run_with_debugging_with_post_mortem(port, start_debugger_with_ctty):
14-
debugger_future = run_script_in_process(run_divide_with_zero_with_debugging_script, start_debugger_with_ctty, port,
15-
set_trace=False, post_mortem=True)
16-
time.sleep(1)
17-
assert not debugger_future.done()
18-
client_future = run_in_process(run_client, port, b'c\n')
1913
with raises(ZeroDivisionError):
20-
debugger_future.result(JOIN_TIMEOUT)
21-
client_future.result(JOIN_TIMEOUT)
14+
with run_script_in_process(run_divide_with_zero_with_debugging_script, start_debugger_with_ctty, port,
15+
set_trace=False, post_mortem=True):
16+
run_in_process(run_client, port, b'c\n').finish()
2217

2318

2419
def test_run_with_debugging_with_set_trace(port, start_debugger_with_ctty):
25-
debugger_future = run_script_in_process(run_divide_with_zero_with_debugging_script, start_debugger_with_ctty, port,
26-
set_trace=True, post_mortem=False)
27-
assert not debugger_future.done()
28-
client_future = run_in_process(run_client, port, b'n\nn\nyo = 0\nc\n')
29-
debugger_future.result(JOIN_TIMEOUT)
30-
client_future.result(JOIN_TIMEOUT)
20+
with run_script_in_process(run_divide_with_zero_with_debugging_script, start_debugger_with_ctty, port,
21+
set_trace=True, post_mortem=False):
22+
run_in_process(run_client, port, b'n\nn\nyo = 0\nc\n').finish()

tests/system/test_set_trace.py

Lines changed: 15 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import madbg
2-
from unittest.mock import Mock
32
from pytest import raises
43

54
from madbg.debugger import RemoteIPythonDebugger
65

7-
from .utils import run_in_process, run_script_in_process, JOIN_TIMEOUT, run_client
6+
from .utils import run_in_process, run_script_in_process, run_client
87

98

10-
def set_trace_script(port, times=1):
9+
def set_trace_script(port, times=1, debugger_fails=False):
10+
if debugger_fails:
11+
RemoteIPythonDebugger.__init__ = lambda *a, **k: 1 / 0
1112
for _ in range(times):
1213
madbg.set_trace(port=port)
1314

@@ -22,38 +23,27 @@ def set_trace_and_expect_var_to_change_script(port) -> bool:
2223

2324

2425
def test_set_trace(port, start_debugger_with_ctty):
25-
debugger_future = run_script_in_process(set_trace_and_expect_var_to_change_script, start_debugger_with_ctty, port)
26-
client_future = run_in_process(run_client, port, b'value_to_change += 1\nc\n')
27-
assert debugger_future.result(JOIN_TIMEOUT)
28-
client_output = client_future.result(JOIN_TIMEOUT)
29-
assert b'Closing connection' in client_output
26+
with run_script_in_process(set_trace_and_expect_var_to_change_script, start_debugger_with_ctty, port):
27+
assert b'Closing connection' in run_in_process(run_client, port, b'value_to_change += 1\nc\n').finish().get(0)
3028

3129

3230
def test_set_trace_and_connect_twice(port, start_debugger_with_ctty):
33-
debugger_future = run_script_in_process(set_trace_script, start_debugger_with_ctty, port, 2)
34-
assert b'Closing connection' in run_in_process(run_client, port, b'q\n').result(JOIN_TIMEOUT)
35-
assert b'Closing connection' in run_in_process(run_client, port, b'q\n').result(JOIN_TIMEOUT)
36-
debugger_future.result(JOIN_TIMEOUT)
31+
with run_script_in_process(set_trace_script, start_debugger_with_ctty, port, 2):
32+
assert b'Closing connection' in run_in_process(run_client, port, b'q\n').finish().get(0)
33+
assert b'Closing connection' in run_in_process(run_client, port, b'q\n').finish().get(0)
3734

3835

3936
def test_set_trace_twice_and_continue(port, start_debugger_with_ctty):
40-
debugger_future = run_script_in_process(set_trace_script, start_debugger_with_ctty, port, 2)
41-
assert b'Closing connection' in run_in_process(run_client, port, b'c\nq\n').result(JOIN_TIMEOUT)
42-
debugger_future.result(JOIN_TIMEOUT)
37+
with run_script_in_process(set_trace_script, start_debugger_with_ctty, port, 2):
38+
assert b'Closing connection' in run_in_process(run_client, port, b'c\nq\n').finish().get(0)
4339

4440

4541
def test_set_trace_and_quit_debugger(port, start_debugger_with_ctty):
46-
debugger_future = run_script_in_process(set_trace_script, start_debugger_with_ctty, port)
47-
client_future = run_in_process(run_client, port, b'q\n')
48-
debugger_future.result(JOIN_TIMEOUT)
49-
client_future.result(JOIN_TIMEOUT)
42+
with run_script_in_process(set_trace_script, start_debugger_with_ctty, port):
43+
run_in_process(run_client, port, b'q\n').finish()
5044

5145

5246
def test_set_trace_with_failing_debugger(port, start_debugger_with_ctty, monkeypatch):
53-
monkeypatch.setattr(RemoteIPythonDebugger, '__init__', Mock(side_effect=lambda *a, **k: 1 / 0))
54-
debugger_future = run_script_in_process(set_trace_script, start_debugger_with_ctty, port)
55-
client_future = run_in_process(run_client, port, b'bla\n')
5647
with raises(ZeroDivisionError):
57-
debugger_future.result(JOIN_TIMEOUT)
58-
client_output = client_future.result(JOIN_TIMEOUT)
59-
assert ZeroDivisionError.__name__.encode() in client_output
48+
with run_script_in_process(set_trace_script, start_debugger_with_ctty, port, debugger_fails=True) as script_result:
49+
assert ZeroDivisionError.__name__.encode() in run_in_process(run_client, port, b'bla\n').finish().get(0)
Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import time
22
import madbg
33

4-
from .utils import run_in_process, run_script_in_process, JOIN_TIMEOUT, run_client
4+
from .utils import run_in_process, run_script_in_process, run_client
55

66

77
def set_trace_on_connect_script(port) -> bool:
@@ -16,16 +16,12 @@ def set_trace_on_connect_script(port) -> bool:
1616

1717

1818
def test_set_trace_on_connect(port, start_debugger_with_ctty):
19-
debugger_future = run_script_in_process(set_trace_on_connect_script, start_debugger_with_ctty, port)
20-
# let the loop run a little
21-
time.sleep(0.5)
22-
assert not debugger_future.done()
23-
# Test we can connect twice
24-
run_in_process(run_client, port, b'q\n').result(JOIN_TIMEOUT)
25-
client_future = run_in_process(run_client, port, b'conti = False\nc\n')
26-
assert debugger_future.result(JOIN_TIMEOUT)
27-
client_future.result(JOIN_TIMEOUT)
19+
with run_script_in_process(set_trace_on_connect_script, start_debugger_with_ctty, port) as script_result:
20+
# Test we can connect twice
21+
run_in_process(run_client, port, b'q\n').finish()
22+
run_in_process(run_client, port, b'conti = False\nc\n').finish()
23+
assert script_result.get(0)
2824

2925

3026
def test_set_trace_on_connect_can_exit(port, start_debugger_with_ctty):
31-
run_script_in_process(madbg.set_trace_on_connect, start_debugger_with_ctty, port=port).result(JOIN_TIMEOUT)
27+
run_script_in_process(madbg.set_trace_on_connect, start_debugger_with_ctty, port=port).finish()

tests/system/utils.py

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,59 @@
1-
import atexit
21
import os
32
import pty
43
import select
54
import socket
6-
from concurrent.futures import ProcessPoolExecutor
7-
from contextlib import closing
5+
import multiprocessing as mp
6+
from contextlib import closing, _GeneratorContextManager
7+
from functools import wraps
88
from pathlib import Path
99

1010
from madbg import client
1111
from madbg.consts import STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO
1212
from madbg.tty_utils import PTY
1313

14-
JOIN_TIMEOUT = 5
14+
JOIN_TIMEOUT = 10
1515
CONNECT_TIMEOUT = 5
1616
SCRIPTS_PATH = Path(__file__).parent / 'scripts'
1717

18+
# forked subprocesses don't run exitfuncs
19+
mp_context = mp.get_context("spawn")
1820

21+
22+
class FinishableGeneratorContextManager(_GeneratorContextManager):
23+
def finish(self):
24+
with self as result:
25+
return result
26+
27+
28+
def finishable_contextmanager(func):
29+
@wraps(func)
30+
def helper(*args, **kwds):
31+
return FinishableGeneratorContextManager(func, args, kwds)
32+
return helper
33+
34+
35+
@finishable_contextmanager
1936
def run_in_process(func, *args, **kwargs):
20-
return ProcessPoolExecutor(1).submit(func, *args, **kwargs)
37+
pool = mp_context.Pool(1)
38+
apply_result = pool.apply_async(func, args, kwargs)
39+
pool.close()
40+
try:
41+
yield apply_result
42+
except:
43+
pool.terminate()
44+
raise
45+
else:
46+
# Wait for the result and raise an error if failed
47+
apply_result.get(JOIN_TIMEOUT)
48+
pool.join()
2149

2250

2351
def _run_script(script, start_with_ctty, args, kwargs):
2452
"""
2553
Meant to be called inside a python subprocess, do NOT call directly.
2654
"""
2755
enter_pty(start_with_ctty)
28-
result = script(*args, **kwargs)
29-
# Python-spawned subprocesses do not call exit funcs - https://stackoverflow.com/q/34506638/2907819
30-
atexit._run_exitfuncs()
31-
return result
56+
return script(*args, **kwargs)
3257

3358

3459
def run_script_in_process(script, start_with_ctty, *args, **kwargs):

tox.ini

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
[tox]
2-
envlist = py37,py38,py39,py310
2+
envlist = py37,py38,py39,py310,py311,sdist
33

4-
[gh-actions]
4+
[gh]
55
python =
6-
3.7: py37
7-
3.8: py38
8-
3.9: py39
9-
3.10: py310
6+
3.11 = py311, sdist
7+
3.10 = py310
8+
3.9 = py39
9+
3.8 = py38
10+
3.7 = py37
1011

1112
[testenv]
12-
deps =
13-
pytest
14-
pytest-xdist
13+
package = wheel
14+
deps = pytest
1515
commands = pytest tests {posargs}
16+
17+
[testenv:sdist]
18+
package = sdist

0 commit comments

Comments
 (0)