Skip to content
This repository was archived by the owner on Dec 21, 2025. It is now read-only.

Commit 751b0d3

Browse files
committed
Removed subprocess
1 parent c2c2f4f commit 751b0d3

File tree

6 files changed

+61
-239
lines changed

6 files changed

+61
-239
lines changed

CHANGES

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,28 @@
11
CHANGES
22
-------
3+
April 10, 2024: Note: I've decided to take Curio in a different
4+
direction. Initially, Curio was developed with variety of
5+
"experimental" features with the expectation that people might play
6+
around with them and we'd learn together. However, this never really
7+
materialized. As such, I'm going to be trimming the feature set. If
8+
this affects you, please look at the "examples" directory because I
9+
may have moved code there. Many features of Curio were simply
10+
higher-level modules implemented on top of an existing core and can be
11+
added back to your code without much effort. -- Dave
12+
13+
04/10/2024 Removed subprocess module. Use the Python built-in instead.
14+
Old code found in examples/curio_subprocess.py
15+
16+
04/10/2024 Removed dependency on telnetlib. Removed commands from the
17+
monitor that allowed changes to the running environment,
18+
specifically the `cancel` and `signal` commands. It's
19+
not clear that cancelling tasks from the monitor would
20+
be all that useful to begin with. If you need to send
21+
a Unix signal, use `kill` at the command line.
22+
23+
04/10/2024 Eliminated flaky tests that were already marked for
24+
skipping in pytest.
25+
326
Version 1.6 - October 25, 2022
427
------------------------------
528

curio/kernel.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -817,8 +817,8 @@ def run(corofunc, *args, with_monitor=False, selector=None,
817817
if with_monitor or 'CURIOMONITOR' in os.environ:
818818
from .monitor import Monitor
819819
m = Monitor(kernel)
820+
m.start()
820821
kernel._call_at_shutdown(m.close)
821-
kernel.run(m.start)
822822

823823
with kernel:
824824
return kernel.run(corofunc, *args)

curio/monitor.py

Lines changed: 29 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -25,37 +25,28 @@
2525
# Where host and port configure the network address on which the monitor
2626
# operates.
2727
#
28-
# To connect to the monitor, run python3 -m curio.monitor -H [host] -p [port]. For example:
28+
# To connect to the monitor, run python3 -m curio.monitor -H [host] -p [port].
2929
#
3030
# Theory of operation:
3131
# --------------------
3232
# The monitor works by opening up a loopback socket on the local
33-
# machine and allowing connections via telnet. By default, it only
34-
# allows a connection originating from the local machine. Only a
35-
# single monitor connection is allowed at any given time.
33+
# machine and allowing connections via a tool like telnet or nc. By
34+
# default, it only allows a connection originating from the local
35+
# machine. Only a single monitor connection is allowed at any given
36+
# time.
3637
#
37-
# There are two parts to the monitor itself: a user interface and an
38-
# internal loop that runs on curio itself. The user interface part
39-
# runs in a completely separate execution thread. The reason for this
40-
# is that it allows curio to be monitored even if the curio kernel is
41-
# completely deadlocked, occupied with a large CPU-bound task, or
42-
# otherwise hosed in the some way. At a minimum, you can connect,
43-
# look at the task table, and see what the tasks are doing.
44-
#
45-
# The internal monitor loop implemented on curio itself is presently
46-
# used to implement external task cancellation. Manipulating any part
47-
# of the kernel state or task status is unsafe from an outside thread.
48-
# To make it safe, the user-interface thread of the monitor hands over
49-
# requests requiring the involvement of the kernel to the monitor
50-
# loop. Since this loop runs on curio, it can safely make
51-
# cancellation requests and perform other kernel-related actions.
38+
# The monitor is implemented externally to Curio using threads. The
39+
# reason for this is that it allows curio to be monitored even if the
40+
# curio kernel is completely deadlocked, occupied with a large
41+
# CPU-bound task, or otherwise hosed in the some way. At a minimum,
42+
# you can connect, look at the task table, and see what the tasks are
43+
# doing.
5244

5345
import os
5446
import signal
5547
import time
5648
import socket
5749
import threading
58-
import telnetlib
5950
import argparse
6051
import logging
6152
import sys
@@ -121,7 +112,6 @@ class Monitor(object):
121112
def __init__(self, kern, host=MONITOR_HOST, port=MONITOR_PORT):
122113
self.kernel = kern
123114
self.address = (host, port)
124-
self.monitor_queue = queue.UniversalQueue()
125115
self._closing = None
126116
self._ui_thread = None
127117

@@ -131,35 +121,22 @@ def close(self):
131121
if self._ui_thread:
132122
self._ui_thread.join()
133123

134-
async def monitor_task(self):
135-
'''
136-
Asynchronous task loop for carrying out task cancellation.
137-
'''
138-
while True:
139-
task = await self.monitor_queue.get()
140-
await task.cancel()
141-
142-
async def start(self):
124+
def start(self):
143125
'''
144126
Function to start the monitor
145127
'''
146-
# The monitor launches both a separate thread and helper task
147-
# that runs inside curio itself to manage cancellation events
148-
149128
log.info('Starting Curio monitor at %s', self.address)
150-
129+
self._closing = threading.Event()
151130
self._ui_thread = threading.Thread(target=self.server, args=(), daemon=True)
152-
self._closing = threading.Event()
153131
self._ui_thread.start()
154-
await spawn(self.monitor_task, daemon=True)
155132

156133
def server(self):
157134
'''
158135
Synchronous kernel for the monitor. This runs in a separate thread
159136
from curio itself.
160137
'''
161138
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
162-
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
139+
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
163140

164141
# set the timeout to prevent the server loop from
165142
# blocking indefinitaly on sock.accept()
@@ -172,7 +149,6 @@ def server(self):
172149
client, addr = sock.accept()
173150
with client:
174151
client.settimeout(0.5)
175-
176152
# This bit of magic is for reading lines of input while still allowing timeouts
177153
# and the ability for the monitor to die when curio exits. See Issue #108.
178154
def readlines():
@@ -193,6 +169,7 @@ def readlines():
193169

194170
sout = client.makefile('w', encoding='latin-1')
195171
self.interactive_loop(sout, readlines())
172+
sout.close()
196173
except socket.timeout:
197174
continue
198175

@@ -224,14 +201,6 @@ def interactive_loop(self, sout, input_lines):
224201
self.command_exit(sout)
225202
return
226203

227-
elif resp.startswith('cancel'):
228-
_, taskid_s = resp.split()
229-
self.command_cancel(sout, int(taskid_s))
230-
231-
elif resp.startswith('signal'):
232-
_, signame = resp.split()
233-
self.command_signal(sout, signame)
234-
235204
elif resp.startswith('w'):
236205
_, taskid_s = resp.split()
237206
self.command_where(sout, int(taskid_s))
@@ -248,8 +217,6 @@ def command_help(self, sout):
248217
'''Commands:
249218
ps : Show task table
250219
where taskid : Show stack frames for a task
251-
cancel taskid : Cancel an indicated task
252-
signal signame : Send a Unix signal
253220
parents taskid : List task parents
254221
quit : Leave the monitor
255222
''')
@@ -260,18 +227,6 @@ def command_ps(self, sout):
260227
def command_where(self, sout, taskid):
261228
where(taskid, self.kernel, sout)
262229

263-
def command_signal(self, sout, signame):
264-
if hasattr(signal, signame):
265-
os.kill(os.getpid(), getattr(signal, signame))
266-
else:
267-
sout.write('Unknown signal %s\n' % signame)
268-
269-
def command_cancel(self, sout, taskid):
270-
task = self.kernel._tasks.get(taskid)
271-
if task:
272-
sout.write('Cancelling task %d\n' % taskid)
273-
self.monitor_queue.put(task)
274-
275230
def command_parents(self, sout, taskid):
276231
while taskid:
277232
task = self.kernel._tasks.get(taskid)
@@ -282,22 +237,25 @@ def command_parents(self, sout, taskid):
282237
break
283238

284239
def command_exit(self, sout):
285-
sout.write('Leaving monitor. Hit Ctrl-C to exit\n')
240+
sout.write('Leaving monitor.\n')
286241
sout.flush()
287242

288243
def monitor_client(host, port):
289244
'''
290-
Client to connect to the monitor via "telnet"
245+
Client to connect to the monitor via a socket
291246
'''
292-
tn = telnetlib.Telnet()
293-
tn.open(host, port, timeout=0.5)
294-
try:
295-
tn.interact()
296-
except KeyboardInterrupt:
297-
pass
298-
finally:
299-
tn.close()
300-
247+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
248+
sock.connect((host, port))
249+
def display(sock):
250+
while (chunk := sock.recv(1000)):
251+
sys.stdout.write(chunk.decode('utf-8'))
252+
sys.stdout.flush()
253+
os._exit(0)
254+
threading.Thread(target=display, args=[sock], daemon=True).start()
255+
while True:
256+
line = sys.stdin.readline()
257+
sock.sendall(line.encode('utf-8'))
258+
sock.close()
301259

302260
def main():
303261
parser = argparse.ArgumentParser("usage: python -m curio.monitor [options]")
Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# curio/subprocess.py
1+
# curio_subprocess.py
22
#
33
# A curio-compatible standin for the subprocess module. Provides
44
# asynchronous compatible versions of Popen(), check_output(),
@@ -24,15 +24,15 @@
2424

2525
# -- Curio
2626

27-
from .task import spawn
28-
from .time import sleep
29-
from .errors import CancelledError
30-
from .io import FileStream
31-
from . import thread
32-
from .workers import run_in_thread
27+
from curio.task import spawn
28+
from curio.time import sleep
29+
from curio.errors import CancelledError
30+
from curio.io import FileStream
31+
from curio import thread
32+
from curio.workers import run_in_thread
3333

3434
if sys.platform.startswith('win'):
35-
from .file import AsyncFile as FileStream
35+
from curio.file import AsyncFile as FileStream
3636

3737
class Popen(object):
3838
'''

0 commit comments

Comments
 (0)