Use worker threads instead of subprocesses, bump to 2026.8.3
Convert each Worker from a subprocess to a thread running the tool in a ThreadPoolExecutor, so eris can run free-threaded under CPython 3.14t. Bump fill3 and lscolors deps to 2026.8.3 for the GIL-free cwcwidth chain.
This commit is contained in:
parent
527d2acd23
commit
eebb226b21
5 changed files with 28 additions and 78 deletions
|
|
@ -61,7 +61,7 @@ Options:
|
|||
-h, --help Show the full help.
|
||||
-i, --info Show information about the available tools.
|
||||
-a, --install-all-tools Install all tools.
|
||||
-w COUNT, --workers=COUNT The number of processes working in parallel.
|
||||
-w COUNT, --workers=COUNT The number of threads working in parallel.
|
||||
By default it is the number of cpus minus 1.
|
||||
-e "COMMAND", --editor="COMMAND" The command used to start the editor, in
|
||||
the *edit command. It may contain options.
|
||||
|
|
@ -1266,9 +1266,6 @@ def inotify_watches_exceeded():
|
|||
|
||||
|
||||
def entry_point():
|
||||
if "--worker" in sys.argv:
|
||||
worker.main()
|
||||
return
|
||||
(root_path, worker_count, editor_command, theme, compression,
|
||||
use_window) = check_arguments()
|
||||
manage_cache(root_path)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
|
||||
import asyncio
|
||||
import contextlib
|
||||
import os
|
||||
import signal
|
||||
import concurrent.futures
|
||||
import sys
|
||||
|
||||
import fill3
|
||||
|
|
@ -11,12 +9,6 @@ import eris.tools as tools
|
|||
import eris.paged_list
|
||||
|
||||
|
||||
def worker_command():
|
||||
if getattr(sys, "frozen", False):
|
||||
return [sys.executable, "--worker"]
|
||||
return [sys.executable, os.path.abspath(sys.argv[0]), "--worker"]
|
||||
|
||||
|
||||
class Worker:
|
||||
|
||||
AUTOSAVE_MESSAGE = "Auto-saving…"
|
||||
|
|
@ -25,33 +17,20 @@ class Worker:
|
|||
def __init__(self, compression):
|
||||
self.compression = compression
|
||||
self.result = None
|
||||
self.process = None
|
||||
self.child_pgid = None
|
||||
self._executor = concurrent.futures.ThreadPoolExecutor(max_workers=1)
|
||||
|
||||
async def create_process(self, command=None):
|
||||
create = asyncio.create_subprocess_exec(
|
||||
*(command or worker_command()), stdin=asyncio.subprocess.PIPE,
|
||||
stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE,
|
||||
preexec_fn=os.setsid)
|
||||
self.process = await create
|
||||
pid_line = await self.process.stdout.readline()
|
||||
self.child_pgid = int(pid_line.strip())
|
||||
os.setpriority(os.PRIO_PGRP, self.child_pgid, 19)
|
||||
self.process.stdin.write(f"{self.compression}\n".encode("utf-8"))
|
||||
def _run_job(self, path, tool):
|
||||
result = tools.Result(path, tool)
|
||||
result.compression = self.compression
|
||||
status, text = tools.run_tool_no_error(path, tool)
|
||||
result.result = make_result_widget(text, result, self.compression)
|
||||
return status
|
||||
|
||||
async def run_tool(self, path, tool):
|
||||
while True:
|
||||
self.process.stdin.write(f"{tool.__qualname__}\n{path}\n"
|
||||
.encode("utf-8", "surrogatepass"))
|
||||
data = await self.process.stdout.readline()
|
||||
if data == b"":
|
||||
await self.create_process()
|
||||
else:
|
||||
break
|
||||
return tools.Status(int(data))
|
||||
return await asyncio.get_running_loop().run_in_executor(
|
||||
self._executor, self._run_job, path, tool)
|
||||
|
||||
async def job_runner(self, screen, summary, log, jobs_added_event):
|
||||
await self.create_process()
|
||||
while True:
|
||||
await jobs_added_event.wait()
|
||||
while True:
|
||||
|
|
@ -76,9 +55,7 @@ class Worker:
|
|||
jobs_added_event.clear()
|
||||
|
||||
def kill(self):
|
||||
if self.child_pgid is not None:
|
||||
with contextlib.suppress(ProcessLookupError):
|
||||
os.killpg(self.child_pgid, signal.SIGKILL)
|
||||
self._executor.shutdown(wait=False, cancel_futures=True)
|
||||
|
||||
|
||||
def make_result_widget(text, result, compression):
|
||||
|
|
@ -89,23 +66,3 @@ def make_result_widget(text, result, compression):
|
|||
appearance, result.get_pages_dir(), page_size, cache_size=2,
|
||||
exist_ok=True, open_func=tools.compression_open_func(compression))
|
||||
return fill3.Fixed(appearance)
|
||||
|
||||
|
||||
def main():
|
||||
print(os.getpgid(os.getpid()), flush=True)
|
||||
compression = input()
|
||||
try:
|
||||
while True:
|
||||
tool_name, path = input(), input()
|
||||
tool = getattr(tools, tool_name)
|
||||
result = tools.Result(path, tool)
|
||||
result.compression = compression
|
||||
status, text = tools.run_tool_no_error(path, tool)
|
||||
result.result = make_result_widget(text, result, compression)
|
||||
print(status.value, flush=True)
|
||||
except Exception:
|
||||
tools.log_error()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ PYO3_USE_ABI3_FORWARD_COMPATIBILITY = { value = "1" }
|
|||
|
||||
[project]
|
||||
name = "eris"
|
||||
version = "2026.7.28"
|
||||
version = "2026.8.3"
|
||||
description = "Eris maintains an up-to-date set of reports for every file in a codebase."
|
||||
readme = "README.md"
|
||||
authors = [
|
||||
|
|
@ -15,8 +15,8 @@ authors = [
|
|||
requires-python = ">=3.14"
|
||||
dependencies = [
|
||||
"docopt-ng==0.9.0",
|
||||
"fill3 @ git+https://forgejo.tail556789.ts.net/ahamilton/fill3.git@2026.7.28",
|
||||
"lscolors @ git+https://forgejo.tail556789.ts.net/ahamilton/lscolors.git@2026.7.27",
|
||||
"fill3 @ git+https://forgejo.tail556789.ts.net/ahamilton/fill3.git@2026.8.3",
|
||||
"lscolors @ git+https://forgejo.tail556789.ts.net/ahamilton/lscolors.git@2026.8.3",
|
||||
"pexpect==4.9.0",
|
||||
"pillow==12.3.0",
|
||||
"pygments==2.20.0",
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
import asyncio
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
|
|
@ -29,8 +28,6 @@ class WorkerTestCase(unittest.TestCase):
|
|||
loop = asyncio.new_event_loop()
|
||||
compression = "none"
|
||||
self.worker_ = worker.Worker(compression)
|
||||
command = [sys.executable, "-m", "eris.worker", "--worker"]
|
||||
loop.run_until_complete(self.worker_.create_process(command=command))
|
||||
future = self.worker_.run_tool("foo", tools.metadata)
|
||||
status = loop.run_until_complete(future)
|
||||
self.assertEqual(status, tools.Status.ok)
|
||||
|
|
|
|||
27
uv.lock
generated
27
uv.lock
generated
|
|
@ -25,9 +25,8 @@ wheels = [
|
|||
|
||||
[[package]]
|
||||
name = "cwcwidth"
|
||||
version = "0.1.5"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/38/17/aadd0c6190dca91aa27c4d7e84d69d30fdf4966e7764247cdc395f8fe7d9/cwcwidth-0.1.5.tar.gz", hash = "sha256:2c840e7d85f6de45c45986b416d79312c91882e1121b78d4c347e49c4238c09d", size = 11071, upload-time = "2021-10-10T16:46:56.825Z" }
|
||||
version = "0.1.12"
|
||||
source = { git = "https://forgejo.tail556789.ts.net/ahamilton/cwcwidth.git?rev=v0.1.12#6ac7d0e3c70535a454094e5e7e51e637d05a29c9" }
|
||||
|
||||
[[package]]
|
||||
name = "docopt-ng"
|
||||
|
|
@ -40,7 +39,7 @@ wheels = [
|
|||
|
||||
[[package]]
|
||||
name = "eris"
|
||||
version = "2026.7.28"
|
||||
version = "2026.8.3"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "docopt-ng" },
|
||||
|
|
@ -61,8 +60,8 @@ dev = [
|
|||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "docopt-ng", specifier = "==0.9.0" },
|
||||
{ name = "fill3", git = "https://forgejo.tail556789.ts.net/ahamilton/fill3.git?rev=2026.7.28" },
|
||||
{ name = "lscolors", git = "https://forgejo.tail556789.ts.net/ahamilton/lscolors.git?rev=2026.7.27" },
|
||||
{ name = "fill3", git = "https://forgejo.tail556789.ts.net/ahamilton/fill3.git?rev=2026.8.3" },
|
||||
{ name = "lscolors", git = "https://forgejo.tail556789.ts.net/ahamilton/lscolors.git?rev=2026.8.3" },
|
||||
{ name = "pexpect", specifier = "==4.9.0" },
|
||||
{ name = "pillow", specifier = "==12.3.0" },
|
||||
{ name = "pygments", specifier = "==2.20.0" },
|
||||
|
|
@ -77,8 +76,8 @@ dev = [
|
|||
|
||||
[[package]]
|
||||
name = "fill3"
|
||||
version = "2026.7.28"
|
||||
source = { git = "https://forgejo.tail556789.ts.net/ahamilton/fill3.git?rev=2026.7.28#faacf3614fa458d311b2f5ab87653f6385e74ee6" }
|
||||
version = "2026.8.3"
|
||||
source = { git = "https://forgejo.tail556789.ts.net/ahamilton/fill3.git?rev=2026.8.3#ed3d0923ea26fa7dcdaa1639f5f8493458a84a0c" }
|
||||
dependencies = [
|
||||
{ name = "termstr" },
|
||||
{ name = "textgrid" },
|
||||
|
|
@ -95,8 +94,8 @@ wheels = [
|
|||
|
||||
[[package]]
|
||||
name = "lscolors"
|
||||
version = "2026.7.27"
|
||||
source = { git = "https://forgejo.tail556789.ts.net/ahamilton/lscolors.git?rev=2026.7.27#288d88c9aea9deda54662d6fd5fb0d4dfe9e80fe" }
|
||||
version = "2026.8.3"
|
||||
source = { git = "https://forgejo.tail556789.ts.net/ahamilton/lscolors.git?rev=2026.8.3#ff650469c5c2d3cfeeeacb3f17afba2ee57f97ba" }
|
||||
dependencies = [
|
||||
{ name = "termstr" },
|
||||
]
|
||||
|
|
@ -255,13 +254,13 @@ wheels = [
|
|||
|
||||
[[package]]
|
||||
name = "termstr"
|
||||
version = "2026.7.27"
|
||||
source = { git = "https://forgejo.tail556789.ts.net/ahamilton/termstr.git?rev=2026.7.27#7d7a8e9ecd52774dc474c6cad08f26f06b274889" }
|
||||
version = "2026.8.3"
|
||||
source = { git = "https://forgejo.tail556789.ts.net/ahamilton/termstr.git?rev=2026.8.3#12cf0e42691fb241932bdfbb17a7e794978a45cf" }
|
||||
dependencies = [
|
||||
{ name = "cwcwidth" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textgrid"
|
||||
version = "0.1.0"
|
||||
source = { git = "https://forgejo.tail556789.ts.net/ahamilton/textgrid-rust.git?rev=2026.7.28#49125c7a38cf2beae45579bb4dc79b8215c1c5ab" }
|
||||
version = "2026.8.3"
|
||||
source = { git = "https://forgejo.tail556789.ts.net/ahamilton/textgrid-rust.git?rev=2026.8.3#9d2af4e58f689502e6f7ff47dd1a45c01a76f9c0" }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue