Hendrik Clank
Hendrik Clank

Reputation: 1

Why did python subprocess stdin.write() not work after a stdin.flush()?

gameserver.py

import config
import os
from subprocess import Popen, PIPE, STDOUT


class GameserverHandler:

    def __init__(self):
        print("==== Gameserver ====")
        self.__gameServerInstance__ = self.restart_gameserver()
        print("\tGameserver started")

    def restart_gameserver(self):
        if os.path.exists(config.bot_config[0]['GameServer']['OutPutFile']):
            os.remove(config.bot_config[0]['GameServer']['OutPutFile'])
        f = open(config.bot_config[0]['GameServer']['OutPutFile'], "wb")

        return Popen(config.bot_config[0]['GameServer']['Path'], stdin=PIPE, stdout=f, stderr=STDOUT)

    def send_command(self, command):
        try:
            self.__gameServerInstance__.stdin.write(str.encode(command))
            self.__gameServerInstance__.stdin.flush()
        except BrokenPipeError:
            pass
        except OSError as e:
            exit()

main.py

import config
import gameserver


gameServer = gameserver.GameserverHandler()
a = input()
gameServer.send_command('quit\n')

Hallo everyone, i just wrote my first python script. This script just starts a gameserver on my computer, write the stdout and stderr in a file and gives me an option to send commands to the server.

But there is the Problem when i use send_command() the gameserver dont gets the stdin.write. I read that i have to put a flush() after it. But this is not helping. Funny is when i change the code to this:

def send_command(self, command):
    try:
        self.__gameServerInstance__.stdin.write(str.encode(command))
        self.__gameServerInstance__.stdin.flush()
        self.__gameServerInstance__.stdout.flush()
        self.__gameServerInstance__.stderr.flush()
    except BrokenPipeError:
        pass
    except OSError as e:
        exit() 

I get this Error

Traceback (most recent call last):   File "D:\Projekte\python\PycharmProjects\ServerLauncher\main.py", line 7, in <module>
    gameServer.send_command('quit')   File "D:\Projekte\python\PycharmProjects\ServerLauncher\gameserver.py", line 22, in send_command
    self.__gameServerInstance__.stdout.flush() AttributeError: 'NoneType' object has no attribute 'flush'

I think its because i set the stdout, stderr to a file, but why is it working than.

Sorry for a maybe dumb question, i'm just started python programming

UPDATE AFTER ANSWER from Serge Ballesta

Changed the code: gameserver.py

def restart_gameserver(self):
    if os.path.exists(config.bot_config[0]['GameServer']['OutPutFile']):
        os.remove(config.bot_config[0]['GameServer']['OutPutFile'])
    f = open(config.bot_config[0]['GameServer']['OutPutFile'], "wb")

    return Popen(config.bot_config[0]['GameServer']['Path'], stdin=PIPE, stdout=f, stderr=STDOUT)

main.py

import config
import gameserver
import discord

gameServer = gameserver.GameserverHandler()
a = input()
print("SENDING")
gameServer.send_command('quit\n')
print("FINISH")
a = input()

Changes:

But yeah dont know why but still the process dont get the quit. When i put everything in the main.py and remove the class and so like this.

if os.path.exists(config.bot_config[0]['GameServer']['OutPutFile']):
    os.remove(config.bot_config[0]['GameServer']['OutPutFile'])
f = open(config.bot_config[0]['GameServer']['OutPutFile'], "wb")

p = Popen(config.bot_config[0]['GameServer']['Path'], stdin=PIPE, stdout=f, stderr=STDOUT)
a = input()
p.stdin.write(b'quit')

it works, i dont know why, could it be that the stdin is not flushing?

And thx for the fast answer @Serge Ballesta

Upvotes: 0

Views: 686

Answers (1)

Serge Ballesta
Serge Ballesta

Reputation: 148880

There are a number of inconsistencies in your code:

  1. you use the same file for both stdout and stderr. That is wrong and may lead to incorrect output in the file. You should use the special value subprocess.STDOUT:

     from subprocess import Popen, PIPE, STDOUT
     ...
             return Popen(config.bot_config[0]['GameServer']['Path'], stdin=PIPE,
                          stdout=f, stderr=STDOUT)
    
  2. You define the child process with bytes IO, yet open the output file as text. You should use binary mode:

             f = open(config.bot_config[0]['GameServer']['OutPutFile'], "wb")
    
  3. You send a command quit. Most CLI programs expect a command to be terminated with a newline character. You should add a \n to your command:

                 self.__gameServerInstance__.stdin.write(str.encode(command) + 'b\n')
    

    or

     gameServer.send_command('quit\n')
    

After those fixes, I could successfully start a cmd.exe child process (on Windows), have it to terminate after the exit\n command, and got the expected data in the output file.

Upvotes: 1

Related Questions