diff --git a/cyaron/io.py b/cyaron/io.py index bf8fadc..3bee2ee 100644 --- a/cyaron/io.py +++ b/cyaron/io.py @@ -7,10 +7,10 @@ from __future__ import absolute_import import os import re +import signal import subprocess import tempfile -import psutil -from typing import Union, overload, Optional, List +from typing import Union, overload, Optional, List, cast from io import IOBase from . import log from .utils import list_like, make_unicode @@ -91,6 +91,7 @@ def __init__( # type: ignore # if the dir "./io" not found it will be created """ self.__closed = False + self.input_file = cast(IOBase, None) self.output_file = None if file_prefix is not None: # legacy mode @@ -230,20 +231,13 @@ def __clear(self, file: IOBase, pos: int = 0): file.seek(pos) @staticmethod - def _kill_process_and_children(pid: int): - try: - parent = psutil.Process(pid) - while True: - children = parent.children() - if not children: - break - for child in children: - IO._kill_process_and_children(child.pid) - parent.kill() - except psutil.NoSuchProcess: - pass - except psutil.AccessDenied: - pass + def _kill_process_and_children(proc: subprocess.Popen): + if os.name == "posix": + os.killpg(os.getpgid(proc.pid), signal.SIGKILL) + elif os.name == "nt": + os.system(f"TASKKILL /F /T /PID {proc.pid} > nul") + else: + proc.kill() # Not currently supported def input_write(self, *args, **kwargs): """ @@ -304,13 +298,14 @@ def output_gen(self, stdin=self.input_file.fileno(), stdout=subprocess.PIPE, universal_newlines=replace_EOL, + preexec_fn=os.setsid if os.name == "posix" else None, ) try: output, _ = proc.communicate(timeout=time_limit) except subprocess.TimeoutExpired: - # proc.kill() # didn't work because `shell=True`. - self._kill_process_and_children(proc.pid) + # proc.kill() # didn't work because `shell=True`. + self._kill_process_and_children(proc) raise else: if replace_EOL: diff --git a/cyaron/tests/io_test.py b/cyaron/tests/io_test.py index 86330dc..6a0032d 100644 --- a/cyaron/tests/io_test.py +++ b/cyaron/tests/io_test.py @@ -76,10 +76,12 @@ def test_output_gen(self): def test_output_gen_time_limit_exceeded(self): with captured_output(): + TIMEOUT = 0.02 + WAIT_TIME = 0.4 # If the wait time is too short, an error may occur with open("long_time.py", "w", encoding="utf-8") as f: f.write("import time, os\n" "fn = input()\n" - "time.sleep(0.1)\n" + f"time.sleep({WAIT_TIME})\n" "os.remove(fn)\n") with IO("test_gen.in", "test_gen.out") as test: @@ -89,8 +91,8 @@ def test_output_gen_time_limit_exceeded(self): with self.assertRaises(subprocess.TimeoutExpired): test.input_writeln(abs_input_filename) test.output_gen(f'"{sys.executable}" long_time.py', - time_limit=0.05) - time.sleep(0.1) + time_limit=TIMEOUT) + time.sleep(WAIT_TIME) try: os.remove(input_filename) except FileNotFoundError: @@ -133,10 +135,7 @@ def test_make_dirs(self): mkdir_false = False try: - with IO( - "./automkdir_false/data.in", - "./automkdir_false/data.out", - ): + with IO("./automkdir_false/data.in", "./automkdir_false/data.out"): pass except FileNotFoundError: mkdir_false = True diff --git a/poetry.lock b/poetry.lock index 7b703db..e9bf565 100644 --- a/poetry.lock +++ b/poetry.lock @@ -25,36 +25,6 @@ files = [ [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} -[[package]] -name = "psutil" -version = "6.1.0" -description = "Cross-platform lib for process and system monitoring in Python." -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" -files = [ - {file = "psutil-6.1.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:ff34df86226c0227c52f38b919213157588a678d049688eded74c76c8ba4a5d0"}, - {file = "psutil-6.1.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:c0e0c00aa18ca2d3b2b991643b799a15fc8f0563d2ebb6040f64ce8dc027b942"}, - {file = "psutil-6.1.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:000d1d1ebd634b4efb383f4034437384e44a6d455260aaee2eca1e9c1b55f047"}, - {file = "psutil-6.1.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:5cd2bcdc75b452ba2e10f0e8ecc0b57b827dd5d7aaffbc6821b2a9a242823a76"}, - {file = "psutil-6.1.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:045f00a43c737f960d273a83973b2511430d61f283a44c96bf13a6e829ba8fdc"}, - {file = "psutil-6.1.0-cp27-none-win32.whl", hash = "sha256:9118f27452b70bb1d9ab3198c1f626c2499384935aaf55388211ad982611407e"}, - {file = "psutil-6.1.0-cp27-none-win_amd64.whl", hash = "sha256:a8506f6119cff7015678e2bce904a4da21025cc70ad283a53b099e7620061d85"}, - {file = "psutil-6.1.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:6e2dcd475ce8b80522e51d923d10c7871e45f20918e027ab682f94f1c6351688"}, - {file = "psutil-6.1.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:0895b8414afafc526712c498bd9de2b063deaac4021a3b3c34566283464aff8e"}, - {file = "psutil-6.1.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9dcbfce5d89f1d1f2546a2090f4fcf87c7f669d1d90aacb7d7582addece9fb38"}, - {file = "psutil-6.1.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:498c6979f9c6637ebc3a73b3f87f9eb1ec24e1ce53a7c5173b8508981614a90b"}, - {file = "psutil-6.1.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d905186d647b16755a800e7263d43df08b790d709d575105d419f8b6ef65423a"}, - {file = "psutil-6.1.0-cp36-cp36m-win32.whl", hash = "sha256:6d3fbbc8d23fcdcb500d2c9f94e07b1342df8ed71b948a2649b5cb060a7c94ca"}, - {file = "psutil-6.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:1209036fbd0421afde505a4879dee3b2fd7b1e14fee81c0069807adcbbcca747"}, - {file = "psutil-6.1.0-cp37-abi3-win32.whl", hash = "sha256:1ad45a1f5d0b608253b11508f80940985d1d0c8f6111b5cb637533a0e6ddc13e"}, - {file = "psutil-6.1.0-cp37-abi3-win_amd64.whl", hash = "sha256:a8fb3752b491d246034fa4d279ff076501588ce8cbcdbb62c32fd7a377d996be"}, - {file = "psutil-6.1.0.tar.gz", hash = "sha256:353815f59a7f64cdaca1c0307ee13558a0512f6db064e92fe833784f08539c7a"}, -] - -[package.extras] -dev = ["black", "check-manifest", "coverage", "packaging", "pylint", "pyperf", "pypinfo", "pytest-cov", "requests", "rstcheck", "ruff", "sphinx", "sphinx_rtd_theme", "toml-sort", "twine", "virtualenv", "wheel"] -test = ["pytest", "pytest-xdist", "setuptools"] - [[package]] name = "xeger" version = "0.4.0" @@ -68,4 +38,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = ">=3.6" -content-hash = "3b7d7552aab0b4bfb77c96178a91816d9a53ba6eda49c7bd46f008583f7c3ae0" +content-hash = "f61b42d8bd0c6814638b0f4d9b5afa1b049f7ab03fb55dbdceaceba39936d21c" diff --git a/pyproject.toml b/pyproject.toml index 6c8b023..59ae302 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,6 @@ readme = "README.md" python = ">=3.6" xeger = "^0.4.0" colorful = "^0.5.6" -psutil = "^6.1.0" [build-system] diff --git a/tox.ini b/tox.ini index 4e4efc5..581148d 100644 --- a/tox.ini +++ b/tox.ini @@ -7,6 +7,5 @@ isolated_build = true deps = xeger colorful - psutil commands = python unit_test.py allowlist_externals = poetry