From 9d70e1dd696413432f449f510feb9ad4612ca0c7 Mon Sep 17 00:00:00 2001 From: Xihuai Wang Date: Tue, 23 Jul 2024 23:13:31 +0800 Subject: [PATCH 1/5] add preexec_fn for Popen --- pysc2/lib/sc_process.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/pysc2/lib/sc_process.py b/pysc2/lib/sc_process.py index 5d4962d7..7e80ef2d 100644 --- a/pysc2/lib/sc_process.py +++ b/pysc2/lib/sc_process.py @@ -16,6 +16,7 @@ import os import platform import shutil +import signal import subprocess import tempfile import time @@ -190,10 +191,15 @@ def _check_exists(self, exec_path): def _launch(self, run_config, args, **kwargs): """Launch the process and return the process object.""" + def set_pdeathsig(): + os.setpgrp() # Create a new process group, become its leader + signal.signal(signal.SIGTERM, signal.SIG_DFL) # Ensure SIGTERM uses the default handler + os.prctl(os.PR_SET_PDEATHSIG, signal.SIGTERM) # Set the parent death signal to SIGTERM + try: with sw("popen"): return subprocess.Popen( - args, cwd=run_config.cwd, env=run_config.env, **kwargs) + args, cwd=run_config.cwd, env=run_config.env, preexec_fn=set_pdeathsig, **kwargs) except OSError as e: logging.exception("Failed to launch") raise SC2LaunchError("Failed to launch: %s" % args) from e From 03434692a5dae9871948bb0ba16a818068693944 Mon Sep 17 00:00:00 2001 From: Xihuai Wang Date: Tue, 23 Jul 2024 23:20:23 +0800 Subject: [PATCH 2/5] update version to 4.0.1 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 28cad83e..b25889ce 100755 --- a/setup.py +++ b/setup.py @@ -44,7 +44,7 @@ def initialize_options(self): setup( name='PySC2', - version='4.0.0', + version='4.0.1', description='Starcraft II environment and library for training agents.', long_description=description, author='DeepMind', From cacc3f5a8616104f59b9cb299b4e5281ecb43e4b Mon Sep 17 00:00:00 2001 From: xihuai18 Date: Tue, 23 Jul 2024 23:31:48 +0800 Subject: [PATCH 3/5] feat: :sparkles: handling popen sub process --- pysc2/lib/sc_process.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pysc2/lib/sc_process.py b/pysc2/lib/sc_process.py index 7e80ef2d..4f28f3ee 100644 --- a/pysc2/lib/sc_process.py +++ b/pysc2/lib/sc_process.py @@ -20,6 +20,7 @@ import subprocess import tempfile import time +import prctl from absl import flags from absl import logging @@ -194,8 +195,7 @@ def _launch(self, run_config, args, **kwargs): def set_pdeathsig(): os.setpgrp() # Create a new process group, become its leader signal.signal(signal.SIGTERM, signal.SIG_DFL) # Ensure SIGTERM uses the default handler - os.prctl(os.PR_SET_PDEATHSIG, signal.SIGTERM) # Set the parent death signal to SIGTERM - + prctl.set_pdeathsig(signal.SIGTERM) # Set the parent death signal to SIGTERM try: with sw("popen"): return subprocess.Popen( From 71cf23b9b031742c435b49375b20e6fa9d00c335 Mon Sep 17 00:00:00 2001 From: xihuai18 Date: Wed, 24 Jul 2024 00:16:09 +0800 Subject: [PATCH 4/5] feat: :sparkles: handle parent process deadth for popen --- .gitignore | 1 + pysc2/lib/sc_process.py | 10 +++++++--- setup.py | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index c8c86ef7..5a9ada2b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ *.pyc *_pb2.py +*.egg-info \ No newline at end of file diff --git a/pysc2/lib/sc_process.py b/pysc2/lib/sc_process.py index 4f28f3ee..e16d726b 100644 --- a/pysc2/lib/sc_process.py +++ b/pysc2/lib/sc_process.py @@ -20,7 +20,10 @@ import subprocess import tempfile import time -import prctl +try: + import prctl +except: + prctl = None from absl import flags from absl import logging @@ -46,7 +49,6 @@ class SC2LaunchError(Exception): pass - class StarcraftProcess(object): """Launch a starcraft server, initialize a controller, and later, clean up. @@ -195,7 +197,9 @@ def _launch(self, run_config, args, **kwargs): def set_pdeathsig(): os.setpgrp() # Create a new process group, become its leader signal.signal(signal.SIGTERM, signal.SIG_DFL) # Ensure SIGTERM uses the default handler - prctl.set_pdeathsig(signal.SIGTERM) # Set the parent death signal to SIGTERM + if prctl: + prctl.set_pdeathsig(signal.SIGTERM) # Set the parent death signal to SIGTERM + try: with sw("popen"): return subprocess.Popen( diff --git a/setup.py b/setup.py index b25889ce..28cad83e 100755 --- a/setup.py +++ b/setup.py @@ -44,7 +44,7 @@ def initialize_options(self): setup( name='PySC2', - version='4.0.1', + version='4.0.0', description='Starcraft II environment and library for training agents.', long_description=description, author='DeepMind', From 4e8e23303113fd4316e83eaf1f63e11f638f8796 Mon Sep 17 00:00:00 2001 From: xihuai18 Date: Wed, 24 Jul 2024 00:52:52 +0800 Subject: [PATCH 5/5] docs: :memo: update readme --- README.md | 12 +++++ setup.py | 129 +++++++++++++++++++++++++++++++----------------------- 2 files changed, 87 insertions(+), 54 deletions(-) diff --git a/README.md b/README.md index 3bb87284..a8c68c37 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,18 @@ You can reach us at [pysc2@deepmind.com](mailto:pysc2@deepmind.com). ## Get PySC2 +#### Optional Dependencies for Handling Zombies (**Only for Linux**) + +We need `libcap-dev` to control the sub-processes created by `subprocess.Popen`. + +For example, in Debian / Ubuntu, you can use: + +``` +sudo apt-get install libcap-dev +``` + +After that, `python-prctl` will be added as a dependency when installing `pysc2`, which helps control the terminations of sub-processes created by `subprocess.Popen`. + ### PyPI The easiest way to get PySC2 is to use pip: diff --git a/setup.py b/setup.py index 28cad83e..ae5e8c04 100755 --- a/setup.py +++ b/setup.py @@ -14,6 +14,9 @@ """Module setuptools script.""" import distutils.command.build +import os +import subprocess + from setuptools import setup description = """PySC2 - StarCraft II Learning Environment @@ -36,69 +39,87 @@ class BuildCommand(distutils.command.build.build): - def initialize_options(self): - distutils.command.build.build.initialize_options(self) - # To avoid conflicting with the Bazel BUILD file. - self.build_base = '_build' + def initialize_options(self): + distutils.command.build.build.initialize_options(self) + # To avoid conflicting with the Bazel BUILD file. + self.build_base = "_build" + + +if os.name == "posix" and "linux" in os.uname().sysname.lower(): + # Try to find the libcap development headers + try: + subprocess.check_call(["dpkg", "-s", "libcap-dev"]) + install_prctl = True + except subprocess.CalledProcessError: + print("libcap-dev not found, skipping installation of python-prctl") + install_prctl = False +else: + install_prctl = False +install_requires = [ + "absl-py>=0.1.0", + "deepdiff", + "dm_env", + "enum34", + "mock", + "mpyq", + "numpy>=1.10", + "portpicker>=1.2.0", + "protobuf>=2.6", + "pygame", + "requests", + "s2clientprotocol>=4.10.1.75800.0", + "s2protocol", + "sk-video", + "websocket-client", +] + +if install_prctl: + install_requires += ["python-prctl"] setup( - name='PySC2', - version='4.0.0', - description='Starcraft II environment and library for training agents.', + name="PySC2", + version="4.0.0", + description="Starcraft II environment and library for training agents.", long_description=description, - author='DeepMind', - author_email='pysc2@deepmind.com', - cmdclass={'build': BuildCommand}, - license='Apache License, Version 2.0', - keywords='StarCraft AI', - url='https://github.com/deepmind/pysc2', + author="DeepMind", + author_email="pysc2@deepmind.com", + cmdclass={ + "build": BuildCommand, + }, + license="Apache License, Version 2.0", + keywords="StarCraft AI", + url="https://github.com/deepmind/pysc2", packages=[ - 'pysc2', - 'pysc2.agents', - 'pysc2.bin', - 'pysc2.env', - 'pysc2.lib', - 'pysc2.maps', - 'pysc2.run_configs', - 'pysc2.tests', - ], - install_requires=[ - 'absl-py>=0.1.0', - 'deepdiff', - 'dm_env', - 'enum34', - 'mock', - 'mpyq', - 'numpy>=1.10', - 'portpicker>=1.2.0', - 'protobuf>=2.6', - 'pygame', - 'requests', - 's2clientprotocol>=4.10.1.75800.0', - 's2protocol', - 'sk-video', - 'websocket-client', + "pysc2", + "pysc2.agents", + "pysc2.bin", + "pysc2.env", + "pysc2.lib", + "pysc2.maps", + "pysc2.run_configs", + "pysc2.tests", ], + install_requires=install_requires, entry_points={ - 'console_scripts': [ - 'pysc2_agent = pysc2.bin.agent:entry_point', - 'pysc2_play = pysc2.bin.play:entry_point', - 'pysc2_replay_info = pysc2.bin.replay_info:entry_point', + "console_scripts": [ + "pysc2_agent = pysc2.bin.agent:entry_point", + "pysc2_play = pysc2.bin.play:entry_point", + "pysc2_replay_info = pysc2.bin.replay_info:entry_point", ], }, classifiers=[ - 'Development Status :: 4 - Beta', - 'Environment :: Console', - 'Intended Audience :: Science/Research', - 'License :: OSI Approved :: Apache Software License', - 'Operating System :: POSIX :: Linux', - 'Operating System :: Microsoft :: Windows', - 'Operating System :: MacOS :: MacOS X', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9', - 'Programming Language :: Python :: 3.10', - 'Programming Language :: Python :: 3.11', - 'Topic :: Scientific/Engineering :: Artificial Intelligence', + "Development Status :: 4 - Beta", + "Environment :: Console", + "Intended Audience :: Science/Research", + "License :: OSI Approved :: Apache Software License", + "Operating System :: POSIX :: Linux", + "Operating System :: Microsoft :: Windows", + "Operating System :: MacOS :: MacOS X", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Topic :: Scientific/Engineering :: Artificial Intelligence", ], )