We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Can't use any installers
When I try to install Epic Games Store, for example https://lutris.net/games/epic-games-store/
the installer eventually fails on this:
```
2019-12-29 10:35:48,329: Error while completing task <bound method CommandsMixin.execute of <lutris.installer.interpreter.ScriptInterpreter object at 0x7f3f726e59a0>>: sequence item 1: expected str instance, list found
<class 'TypeError'> sequence item 1: expected str instance, list found
  File "/usr/lib/python3.8/site-packages/lutris/util/jobs.py", line 30, in target
    result = self.function(*args, **kwargs)
  File "/usr/lib/python3.8/site-packages/lutris/installer/commands.py", line 152, in execute
    command.start()
  File "/usr/lib/python3.8/site-packages/lutris/command.py", line 116, in start
    logger.debug("Running %s", " ".join(self.wrapper_command))
```
It seems to affect all installers though, not just this one.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `lutris/command.py`
Content:
```
1 """Threading module, used to launch games while monitoring them."""
2 
3 import io
4 import os
5 import sys
6 import fcntl
7 import shlex
8 import subprocess
9 import contextlib
10 from textwrap import dedent
11 
12 from gi.repository import GLib
13 
14 from lutris import settings
15 from lutris import runtime
16 from lutris.util.log import logger
17 from lutris.util import system
18 
19 WRAPPER_SCRIPT = os.path.join(os.path.dirname(os.path.abspath(sys.argv[0])), "lutris-wrapper")
20 
21 
22 class MonitoredCommand:
23     """Exexcutes a commmand while keeping track of its state"""
24 
25     fallback_cwd = "/tmp"
26 
27     def __init__(
28             self,
29             command,
30             runner=None,
31             env=None,
32             term=None,
33             cwd=None,
34             include_processes=None,
35             exclude_processes=None,
36             log_buffer=None,
37             title=None,
38     ):  # pylint: disable=too-many-arguments
39         self.ready_state = True
40         self.env = self.get_environment(env)
41 
42         self.command = command
43         self.runner = runner
44         self.stop_func = lambda: True
45         self.game_process = None
46         self.prevent_on_stop = False
47         self.return_code = None
48         self.terminal = system.find_executable(term)
49         self.is_running = True
50         self.error = None
51         self.log_handlers = [
52             self.log_handler_stdout,
53             self.log_handler_console_output,
54         ]
55         self.set_log_buffer(log_buffer)
56         self.stdout_monitor = None
57         self.include_processes = include_processes or []
58         self.exclude_processes = exclude_processes or []
59 
60         self.cwd = self.get_cwd(cwd)
61 
62         self._stdout = io.StringIO()
63 
64         self._title = title if title else command
65 
66     @property
67     def stdout(self):
68         return self._stdout.getvalue()
69 
70     @property
71     def wrapper_command(self):
72         """Return launch arguments for the wrapper script"""
73 
74         return [
75             WRAPPER_SCRIPT,
76             self._title,
77             str(len(self.include_processes)),
78             str(len(self.exclude_processes)),
79         ] + self.include_processes + self.exclude_processes + self.command
80 
81     def set_log_buffer(self, log_buffer):
82         """Attach a TextBuffer to this command enables the buffer handler"""
83         if not log_buffer:
84             return
85         self.log_buffer = log_buffer
86         if self.log_handler_buffer not in self.log_handlers:
87             self.log_handlers.append(self.log_handler_buffer)
88 
89     def get_cwd(self, cwd):
90         """Return the current working dir of the game"""
91         if not cwd:
92             cwd = self.runner.working_dir if self.runner else None
93         return os.path.expanduser(cwd or "~")
94 
95     @staticmethod
96     def get_environment(user_env):
97         """Process the user provided environment variables for use as self.env"""
98         env = user_env or {}
99         # not clear why this needs to be added, the path is already added in
100         # the wrappper script.
101         env['PYTHONPATH'] = ':'.join(sys.path)
102         # Drop bad values of environment keys, those will confuse the Python
103         # interpreter.
104         return {
105             key: value for key, value in env.items() if "=" not in key
106         }
107 
108     def get_child_environment(self):
109         """Returns the calculated environment for the child process."""
110         env = os.environ.copy()
111         env.update(self.env)
112         return env
113 
114     def start(self):
115         """Run the thread."""
116         logger.debug("Running %s", " ".join(self.wrapper_command))
117         for key, value in self.env.items():
118             logger.debug("ENV: %s=\"%s\"", key, value)
119 
120         if self.terminal:
121             self.game_process = self.run_in_terminal()
122         else:
123             env = self.get_child_environment()
124             self.game_process = self.execute_process(self.wrapper_command, env)
125 
126         if not self.game_process:
127             logger.warning("No game process available")
128             return
129 
130         GLib.child_watch_add(self.game_process.pid, self.on_stop)
131 
132         # make stdout nonblocking.
133         fileno = self.game_process.stdout.fileno()
134         fcntl.fcntl(
135             fileno,
136             fcntl.F_SETFL,
137             fcntl.fcntl(fileno, fcntl.F_GETFL) | os.O_NONBLOCK
138         )
139 
140         self.stdout_monitor = GLib.io_add_watch(
141             self.game_process.stdout,
142             GLib.IO_IN | GLib.IO_HUP,
143             self.on_stdout_output,
144         )
145 
146     def log_handler_stdout(self, line):
147         """Add the line to this command's stdout attribute"""
148         self._stdout.write(line)
149 
150     def log_handler_buffer(self, line):
151         """Add the line to the associated LogBuffer object"""
152         self.log_buffer.insert(self.log_buffer.get_end_iter(), line, -1)
153 
154     def log_handler_console_output(self, line):  # pylint: disable=no-self-use
155         """Print the line to stdout"""
156         with contextlib.suppress(BlockingIOError):
157             sys.stdout.write(line)
158             sys.stdout.flush()
159 
160     def on_stop(self, _pid, returncode):
161         """Callback registered on game process termination"""
162         if self.prevent_on_stop:  # stop() already in progress
163             return False
164 
165         logger.debug("The process has terminated with code %s", returncode)
166         self.is_running = False
167         self.return_code = returncode
168 
169         resume_stop = self.stop()
170         if not resume_stop:
171             logger.info("Full shutdown prevented")
172             return False
173 
174         return False
175 
176     def on_stdout_output(self, stdout, condition):
177         """Called by the stdout monitor to dispatch output to log handlers"""
178         if condition == GLib.IO_HUP:
179             self.stdout_monitor = None
180             return False
181         if not self.is_running:
182             return False
183         try:
184             line = stdout.read(262144).decode("utf-8", errors="ignore")
185         except ValueError:
186             # file_desc might be closed
187             return True
188         if "winemenubuilder.exe" in line:
189             return True
190         for log_handler in self.log_handlers:
191             log_handler(line)
192         return True
193 
194     def run_in_terminal(self):
195         """Write command in a script file and run it.
196 
197         Running it from a file is likely the only way to set env vars only
198         for the command (not for the terminal app).
199         It's also the only reliable way to keep the term open when the
200         game is quit.
201         """
202         script_path = os.path.join(settings.CACHE_DIR, "run_in_term.sh")
203         exported_environment = "\n".join(
204             'export %s="%s" ' % (key, value)
205             for key, value in self.env.items()
206         )
207         command = " ".join(['"%s"' % token for token in self.wrapper_command])
208         with open(script_path, "w") as script_file:
209             script_file.write(dedent(
210                 """#!/bin/sh
211                 cd "%s"
212                 %s
213                 exec %s
214                 """ % (self.cwd, exported_environment, command)
215             ))
216             os.chmod(script_path, 0o744)
217         return self.execute_process([self.terminal, "-e", script_path])
218 
219     def execute_process(self, command, env=None):
220         """Execute and return a subprocess"""
221         if self.cwd and not system.path_exists(self.cwd):
222             try:
223                 os.makedirs(self.cwd)
224             except OSError:
225                 logger.error("Failed to create working directory, falling back to %s",
226                              self.fallback_cwd)
227                 self.cwd = "/tmp"
228         try:
229 
230             return subprocess.Popen(
231                 command,
232                 stdout=subprocess.PIPE,
233                 stderr=subprocess.STDOUT,
234                 cwd=self.cwd,
235                 env=env,
236             )
237         except OSError as ex:
238             logger.exception("Failed to execute %s: %s", " ".join(command), ex)
239             self.error = ex.strerror
240 
241     def stop(self):
242         """Stops the current game process and cleans up the instance"""
243         # Prevent stop() being called again by the process exiting
244         self.prevent_on_stop = True
245 
246         try:
247             self.game_process.terminate()
248         except ProcessLookupError:  # process already dead.
249             logger.debug("Management process looks dead already.")
250 
251         if hasattr(self, "stop_func"):
252             resume_stop = self.stop_func()
253             if not resume_stop:
254                 return False
255 
256         if self.stdout_monitor:
257             logger.debug("Detaching logger")
258             GLib.source_remove(self.stdout_monitor)
259             self.stdout_monitor = None
260         else:
261             logger.debug("logger already detached")
262 
263         self.is_running = False
264         self.ready_state = False
265         return True
266 
267 
268 def exec_command(command):
269     """Execute arbitrary command in a MonitoredCommand
270 
271     Used by the --exec command line flag.
272     """
273     command = MonitoredCommand(shlex.split(command), env=runtime.get_env())
274     command.start()
275     return command
276
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/lutris/command.py b/lutris/command.py
--- a/lutris/command.py
+++ b/lutris/command.py
@@ -61,7 +61,7 @@
 
         self._stdout = io.StringIO()
 
-        self._title = title if title else command
+        self._title = title if title else command[0]
 
     @property
     def stdout(self):

{"golden_diff": "diff --git a/lutris/command.py b/lutris/command.py\n--- a/lutris/command.py\n+++ b/lutris/command.py\n@@ -61,7 +61,7 @@\n \n         self._stdout = io.StringIO()\n \n-        self._title = title if title else command\n+        self._title = title if title else command[0]\n \n     @property\n     def stdout(self):\n", "issue": "Can't use any installers\nWhen I try to install Epic Games Store, for example https://lutris.net/games/epic-games-store/\r\nthe installer eventually fails on this:\r\n```\r\n2019-12-29 10:35:48,329: Error while completing task <bound method CommandsMixin.execute of <lutris.installer.interpreter.ScriptInterpreter object at 0x7f3f726e59a0>>: sequence item 1: expected str instance, list found\r\n<class 'TypeError'> sequence item 1: expected str instance, list found\r\n  File \"/usr/lib/python3.8/site-packages/lutris/util/jobs.py\", line 30, in target\r\n    result = self.function(*args, **kwargs)\r\n  File \"/usr/lib/python3.8/site-packages/lutris/installer/commands.py\", line 152, in execute\r\n    command.start()\r\n  File \"/usr/lib/python3.8/site-packages/lutris/command.py\", line 116, in start\r\n    logger.debug(\"Running %s\", \" \".join(self.wrapper_command))\r\n```\r\nIt seems to affect all installers though, not just this one.\r\n\n", "before_files": [{"content": "\"\"\"Threading module, used to launch games while monitoring them.\"\"\"\n\nimport io\nimport os\nimport sys\nimport fcntl\nimport shlex\nimport subprocess\nimport contextlib\nfrom textwrap import dedent\n\nfrom gi.repository import GLib\n\nfrom lutris import settings\nfrom lutris import runtime\nfrom lutris.util.log import logger\nfrom lutris.util import system\n\nWRAPPER_SCRIPT = os.path.join(os.path.dirname(os.path.abspath(sys.argv[0])), \"lutris-wrapper\")\n\n\nclass MonitoredCommand:\n    \"\"\"Exexcutes a commmand while keeping track of its state\"\"\"\n\n    fallback_cwd = \"/tmp\"\n\n    def __init__(\n            self,\n            command,\n            runner=None,\n            env=None,\n            term=None,\n            cwd=None,\n            include_processes=None,\n            exclude_processes=None,\n            log_buffer=None,\n            title=None,\n    ):  # pylint: disable=too-many-arguments\n        self.ready_state = True\n        self.env = self.get_environment(env)\n\n        self.command = command\n        self.runner = runner\n        self.stop_func = lambda: True\n        self.game_process = None\n        self.prevent_on_stop = False\n        self.return_code = None\n        self.terminal = system.find_executable(term)\n        self.is_running = True\n        self.error = None\n        self.log_handlers = [\n            self.log_handler_stdout,\n            self.log_handler_console_output,\n        ]\n        self.set_log_buffer(log_buffer)\n        self.stdout_monitor = None\n        self.include_processes = include_processes or []\n        self.exclude_processes = exclude_processes or []\n\n        self.cwd = self.get_cwd(cwd)\n\n        self._stdout = io.StringIO()\n\n        self._title = title if title else command\n\n    @property\n    def stdout(self):\n        return self._stdout.getvalue()\n\n    @property\n    def wrapper_command(self):\n        \"\"\"Return launch arguments for the wrapper script\"\"\"\n\n        return [\n            WRAPPER_SCRIPT,\n            self._title,\n            str(len(self.include_processes)),\n            str(len(self.exclude_processes)),\n        ] + self.include_processes + self.exclude_processes + self.command\n\n    def set_log_buffer(self, log_buffer):\n        \"\"\"Attach a TextBuffer to this command enables the buffer handler\"\"\"\n        if not log_buffer:\n            return\n        self.log_buffer = log_buffer\n        if self.log_handler_buffer not in self.log_handlers:\n            self.log_handlers.append(self.log_handler_buffer)\n\n    def get_cwd(self, cwd):\n        \"\"\"Return the current working dir of the game\"\"\"\n        if not cwd:\n            cwd = self.runner.working_dir if self.runner else None\n        return os.path.expanduser(cwd or \"~\")\n\n    @staticmethod\n    def get_environment(user_env):\n        \"\"\"Process the user provided environment variables for use as self.env\"\"\"\n        env = user_env or {}\n        # not clear why this needs to be added, the path is already added in\n        # the wrappper script.\n        env['PYTHONPATH'] = ':'.join(sys.path)\n        # Drop bad values of environment keys, those will confuse the Python\n        # interpreter.\n        return {\n            key: value for key, value in env.items() if \"=\" not in key\n        }\n\n    def get_child_environment(self):\n        \"\"\"Returns the calculated environment for the child process.\"\"\"\n        env = os.environ.copy()\n        env.update(self.env)\n        return env\n\n    def start(self):\n        \"\"\"Run the thread.\"\"\"\n        logger.debug(\"Running %s\", \" \".join(self.wrapper_command))\n        for key, value in self.env.items():\n            logger.debug(\"ENV: %s=\\\"%s\\\"\", key, value)\n\n        if self.terminal:\n            self.game_process = self.run_in_terminal()\n        else:\n            env = self.get_child_environment()\n            self.game_process = self.execute_process(self.wrapper_command, env)\n\n        if not self.game_process:\n            logger.warning(\"No game process available\")\n            return\n\n        GLib.child_watch_add(self.game_process.pid, self.on_stop)\n\n        # make stdout nonblocking.\n        fileno = self.game_process.stdout.fileno()\n        fcntl.fcntl(\n            fileno,\n            fcntl.F_SETFL,\n            fcntl.fcntl(fileno, fcntl.F_GETFL) | os.O_NONBLOCK\n        )\n\n        self.stdout_monitor = GLib.io_add_watch(\n            self.game_process.stdout,\n            GLib.IO_IN | GLib.IO_HUP,\n            self.on_stdout_output,\n        )\n\n    def log_handler_stdout(self, line):\n        \"\"\"Add the line to this command's stdout attribute\"\"\"\n        self._stdout.write(line)\n\n    def log_handler_buffer(self, line):\n        \"\"\"Add the line to the associated LogBuffer object\"\"\"\n        self.log_buffer.insert(self.log_buffer.get_end_iter(), line, -1)\n\n    def log_handler_console_output(self, line):  # pylint: disable=no-self-use\n        \"\"\"Print the line to stdout\"\"\"\n        with contextlib.suppress(BlockingIOError):\n            sys.stdout.write(line)\n            sys.stdout.flush()\n\n    def on_stop(self, _pid, returncode):\n        \"\"\"Callback registered on game process termination\"\"\"\n        if self.prevent_on_stop:  # stop() already in progress\n            return False\n\n        logger.debug(\"The process has terminated with code %s\", returncode)\n        self.is_running = False\n        self.return_code = returncode\n\n        resume_stop = self.stop()\n        if not resume_stop:\n            logger.info(\"Full shutdown prevented\")\n            return False\n\n        return False\n\n    def on_stdout_output(self, stdout, condition):\n        \"\"\"Called by the stdout monitor to dispatch output to log handlers\"\"\"\n        if condition == GLib.IO_HUP:\n            self.stdout_monitor = None\n            return False\n        if not self.is_running:\n            return False\n        try:\n            line = stdout.read(262144).decode(\"utf-8\", errors=\"ignore\")\n        except ValueError:\n            # file_desc might be closed\n            return True\n        if \"winemenubuilder.exe\" in line:\n            return True\n        for log_handler in self.log_handlers:\n            log_handler(line)\n        return True\n\n    def run_in_terminal(self):\n        \"\"\"Write command in a script file and run it.\n\n        Running it from a file is likely the only way to set env vars only\n        for the command (not for the terminal app).\n        It's also the only reliable way to keep the term open when the\n        game is quit.\n        \"\"\"\n        script_path = os.path.join(settings.CACHE_DIR, \"run_in_term.sh\")\n        exported_environment = \"\\n\".join(\n            'export %s=\"%s\" ' % (key, value)\n            for key, value in self.env.items()\n        )\n        command = \" \".join(['\"%s\"' % token for token in self.wrapper_command])\n        with open(script_path, \"w\") as script_file:\n            script_file.write(dedent(\n                \"\"\"#!/bin/sh\n                cd \"%s\"\n                %s\n                exec %s\n                \"\"\" % (self.cwd, exported_environment, command)\n            ))\n            os.chmod(script_path, 0o744)\n        return self.execute_process([self.terminal, \"-e\", script_path])\n\n    def execute_process(self, command, env=None):\n        \"\"\"Execute and return a subprocess\"\"\"\n        if self.cwd and not system.path_exists(self.cwd):\n            try:\n                os.makedirs(self.cwd)\n            except OSError:\n                logger.error(\"Failed to create working directory, falling back to %s\",\n                             self.fallback_cwd)\n                self.cwd = \"/tmp\"\n        try:\n\n            return subprocess.Popen(\n                command,\n                stdout=subprocess.PIPE,\n                stderr=subprocess.STDOUT,\n                cwd=self.cwd,\n                env=env,\n            )\n        except OSError as ex:\n            logger.exception(\"Failed to execute %s: %s\", \" \".join(command), ex)\n            self.error = ex.strerror\n\n    def stop(self):\n        \"\"\"Stops the current game process and cleans up the instance\"\"\"\n        # Prevent stop() being called again by the process exiting\n        self.prevent_on_stop = True\n\n        try:\n            self.game_process.terminate()\n        except ProcessLookupError:  # process already dead.\n            logger.debug(\"Management process looks dead already.\")\n\n        if hasattr(self, \"stop_func\"):\n            resume_stop = self.stop_func()\n            if not resume_stop:\n                return False\n\n        if self.stdout_monitor:\n            logger.debug(\"Detaching logger\")\n            GLib.source_remove(self.stdout_monitor)\n            self.stdout_monitor = None\n        else:\n            logger.debug(\"logger already detached\")\n\n        self.is_running = False\n        self.ready_state = False\n        return True\n\n\ndef exec_command(command):\n    \"\"\"Execute arbitrary command in a MonitoredCommand\n\n    Used by the --exec command line flag.\n    \"\"\"\n    command = MonitoredCommand(shlex.split(command), env=runtime.get_env())\n    command.start()\n    return command\n", "path": "lutris/command.py"}], "after_files": [{"content": "\"\"\"Threading module, used to launch games while monitoring them.\"\"\"\n\nimport io\nimport os\nimport sys\nimport fcntl\nimport shlex\nimport subprocess\nimport contextlib\nfrom textwrap import dedent\n\nfrom gi.repository import GLib\n\nfrom lutris import settings\nfrom lutris import runtime\nfrom lutris.util.log import logger\nfrom lutris.util import system\n\nWRAPPER_SCRIPT = os.path.join(os.path.dirname(os.path.abspath(sys.argv[0])), \"lutris-wrapper\")\n\n\nclass MonitoredCommand:\n    \"\"\"Exexcutes a commmand while keeping track of its state\"\"\"\n\n    fallback_cwd = \"/tmp\"\n\n    def __init__(\n            self,\n            command,\n            runner=None,\n            env=None,\n            term=None,\n            cwd=None,\n            include_processes=None,\n            exclude_processes=None,\n            log_buffer=None,\n            title=None,\n    ):  # pylint: disable=too-many-arguments\n        self.ready_state = True\n        self.env = self.get_environment(env)\n\n        self.command = command\n        self.runner = runner\n        self.stop_func = lambda: True\n        self.game_process = None\n        self.prevent_on_stop = False\n        self.return_code = None\n        self.terminal = system.find_executable(term)\n        self.is_running = True\n        self.error = None\n        self.log_handlers = [\n            self.log_handler_stdout,\n            self.log_handler_console_output,\n        ]\n        self.set_log_buffer(log_buffer)\n        self.stdout_monitor = None\n        self.include_processes = include_processes or []\n        self.exclude_processes = exclude_processes or []\n\n        self.cwd = self.get_cwd(cwd)\n\n        self._stdout = io.StringIO()\n\n        self._title = title if title else command[0]\n\n    @property\n    def stdout(self):\n        return self._stdout.getvalue()\n\n    @property\n    def wrapper_command(self):\n        \"\"\"Return launch arguments for the wrapper script\"\"\"\n\n        return [\n            WRAPPER_SCRIPT,\n            self._title,\n            str(len(self.include_processes)),\n            str(len(self.exclude_processes)),\n        ] + self.include_processes + self.exclude_processes + self.command\n\n    def set_log_buffer(self, log_buffer):\n        \"\"\"Attach a TextBuffer to this command enables the buffer handler\"\"\"\n        if not log_buffer:\n            return\n        self.log_buffer = log_buffer\n        if self.log_handler_buffer not in self.log_handlers:\n            self.log_handlers.append(self.log_handler_buffer)\n\n    def get_cwd(self, cwd):\n        \"\"\"Return the current working dir of the game\"\"\"\n        if not cwd:\n            cwd = self.runner.working_dir if self.runner else None\n        return os.path.expanduser(cwd or \"~\")\n\n    @staticmethod\n    def get_environment(user_env):\n        \"\"\"Process the user provided environment variables for use as self.env\"\"\"\n        env = user_env or {}\n        # not clear why this needs to be added, the path is already added in\n        # the wrappper script.\n        env['PYTHONPATH'] = ':'.join(sys.path)\n        # Drop bad values of environment keys, those will confuse the Python\n        # interpreter.\n        return {\n            key: value for key, value in env.items() if \"=\" not in key\n        }\n\n    def get_child_environment(self):\n        \"\"\"Returns the calculated environment for the child process.\"\"\"\n        env = os.environ.copy()\n        env.update(self.env)\n        return env\n\n    def start(self):\n        \"\"\"Run the thread.\"\"\"\n        logger.debug(\"Running %s\", \" \".join(self.wrapper_command))\n        for key, value in self.env.items():\n            logger.debug(\"ENV: %s=\\\"%s\\\"\", key, value)\n\n        if self.terminal:\n            self.game_process = self.run_in_terminal()\n        else:\n            env = self.get_child_environment()\n            self.game_process = self.execute_process(self.wrapper_command, env)\n\n        if not self.game_process:\n            logger.warning(\"No game process available\")\n            return\n\n        GLib.child_watch_add(self.game_process.pid, self.on_stop)\n\n        # make stdout nonblocking.\n        fileno = self.game_process.stdout.fileno()\n        fcntl.fcntl(\n            fileno,\n            fcntl.F_SETFL,\n            fcntl.fcntl(fileno, fcntl.F_GETFL) | os.O_NONBLOCK\n        )\n\n        self.stdout_monitor = GLib.io_add_watch(\n            self.game_process.stdout,\n            GLib.IO_IN | GLib.IO_HUP,\n            self.on_stdout_output,\n        )\n\n    def log_handler_stdout(self, line):\n        \"\"\"Add the line to this command's stdout attribute\"\"\"\n        self._stdout.write(line)\n\n    def log_handler_buffer(self, line):\n        \"\"\"Add the line to the associated LogBuffer object\"\"\"\n        self.log_buffer.insert(self.log_buffer.get_end_iter(), line, -1)\n\n    def log_handler_console_output(self, line):  # pylint: disable=no-self-use\n        \"\"\"Print the line to stdout\"\"\"\n        with contextlib.suppress(BlockingIOError):\n            sys.stdout.write(line)\n            sys.stdout.flush()\n\n    def on_stop(self, _pid, returncode):\n        \"\"\"Callback registered on game process termination\"\"\"\n        if self.prevent_on_stop:  # stop() already in progress\n            return False\n\n        logger.debug(\"The process has terminated with code %s\", returncode)\n        self.is_running = False\n        self.return_code = returncode\n\n        resume_stop = self.stop()\n        if not resume_stop:\n            logger.info(\"Full shutdown prevented\")\n            return False\n\n        return False\n\n    def on_stdout_output(self, stdout, condition):\n        \"\"\"Called by the stdout monitor to dispatch output to log handlers\"\"\"\n        if condition == GLib.IO_HUP:\n            self.stdout_monitor = None\n            return False\n        if not self.is_running:\n            return False\n        try:\n            line = stdout.read(262144).decode(\"utf-8\", errors=\"ignore\")\n        except ValueError:\n            # file_desc might be closed\n            return True\n        if \"winemenubuilder.exe\" in line:\n            return True\n        for log_handler in self.log_handlers:\n            log_handler(line)\n        return True\n\n    def run_in_terminal(self):\n        \"\"\"Write command in a script file and run it.\n\n        Running it from a file is likely the only way to set env vars only\n        for the command (not for the terminal app).\n        It's also the only reliable way to keep the term open when the\n        game is quit.\n        \"\"\"\n        script_path = os.path.join(settings.CACHE_DIR, \"run_in_term.sh\")\n        exported_environment = \"\\n\".join(\n            'export %s=\"%s\" ' % (key, value)\n            for key, value in self.env.items()\n        )\n        command = \" \".join(['\"%s\"' % token for token in self.wrapper_command])\n        with open(script_path, \"w\") as script_file:\n            script_file.write(dedent(\n                \"\"\"#!/bin/sh\n                cd \"%s\"\n                %s\n                exec %s\n                \"\"\" % (self.cwd, exported_environment, command)\n            ))\n            os.chmod(script_path, 0o744)\n        return self.execute_process([self.terminal, \"-e\", script_path])\n\n    def execute_process(self, command, env=None):\n        \"\"\"Execute and return a subprocess\"\"\"\n        if self.cwd and not system.path_exists(self.cwd):\n            try:\n                os.makedirs(self.cwd)\n            except OSError:\n                logger.error(\"Failed to create working directory, falling back to %s\",\n                             self.fallback_cwd)\n                self.cwd = \"/tmp\"\n        try:\n\n            return subprocess.Popen(\n                command,\n                stdout=subprocess.PIPE,\n                stderr=subprocess.STDOUT,\n                cwd=self.cwd,\n                env=env,\n            )\n        except OSError as ex:\n            logger.exception(\"Failed to execute %s: %s\", \" \".join(command), ex)\n            self.error = ex.strerror\n\n    def stop(self):\n        \"\"\"Stops the current game process and cleans up the instance\"\"\"\n        # Prevent stop() being called again by the process exiting\n        self.prevent_on_stop = True\n\n        try:\n            self.game_process.terminate()\n        except ProcessLookupError:  # process already dead.\n            logger.debug(\"Management process looks dead already.\")\n\n        if hasattr(self, \"stop_func\"):\n            resume_stop = self.stop_func()\n            if not resume_stop:\n                return False\n\n        if self.stdout_monitor:\n            logger.debug(\"Detaching logger\")\n            GLib.source_remove(self.stdout_monitor)\n            self.stdout_monitor = None\n        else:\n            logger.debug(\"logger already detached\")\n\n        self.is_running = False\n        self.ready_state = False\n        return True\n\n\ndef exec_command(command):\n    \"\"\"Execute arbitrary command in a MonitoredCommand\n\n    Used by the --exec command line flag.\n    \"\"\"\n    command = MonitoredCommand(shlex.split(command), env=runtime.get_env())\n    command.start()\n    return command\n", "path": "lutris/command.py"}]}