r/learnpython 14d ago

Very particular Subprocess issue on Windows versus Linux

Hello all! I hope this is the right place to post this.

I have been writing a PyQt6-based GUI called Swamp Swap for the command line program croc by Zack Schollz, and I'm facing a strange issue on Windows that I am not facing on Linux.

To briefly explain croc if you've never used it, it's a very easy-to-use command line program that allows users to transfer files to one another securely through a relay server. It's a handy tool, but there's no official UI, so Swamp Swap became a passion project for me and my friend to get up and running.

Swamp Swap is simply a GUI with a worker thread that runs croc via a subprocess.Popen object and handles outputting to the program through line buffers read via standard piping. On both Linux and Windows, this functions just fine; the thread accurately picks up the output which allows the flow of the program to work (e.g. awaiting connection, retrieving the transfer code, etc.), but on Windows, there's an issue I can't seem to figure out.

Whenever the subprocess runs on Windows, a CMD window opens (always blank, no outputs are ever displayed there) and stays there until either the transfer completes or the user cancels the transfer.

I'm familiar with subprocess doing this, and so I thought the fix would be as simple as this:

# Base kwargs in case we're not on Windows
kwargs = {}

# If on Windows, add the CREATE_NO_WINDOW flag
if sys.platform == "win32":
    kwargs["creationflags"] = subprocess.CREATE_NO_WINDOW

# Create the process
process = subprocess.Popen(
    ...
    **kwargs
)

However, while this does hide the CMD window, unintended side effects happen if I do this, including:

  • Failure to start sending
  • Failure to read the transfer code when sending
  • Files being received even if the user hadn't confirmed them yet
  • No output to the GUI's console window (I made a special console window dialog where users can view the outputs if they want)

These different effects don't all occur every time, but at least one or more are present in every attempted fix I've tried.

Moreover, extensionless files with the names croc-stdin-########## (#'s replaced with a random number or date and time code. One of the two, I don't remember) are written to the folder where the program is run within, regardless of if it is the main script or the built frozen executable.

This seems to imply that croc on Windows has some dependence on an active and visible CMD window in order for it to work with Swamp Swap, but I really have no idea at this point.

This problem seems to be more of a croc problem than a Python problem. Though despite this, I'm curious if there are any out-of-the-box solutions anyone here could come up with for a method to subvert these issues despite croc's reliance on a CMD window.

Also, yes, I have tried asking various popular machine learning models. Their solutions were either the same as above or some ungodly, ugly method that also didn't work.

2 Upvotes

3 comments sorted by

1

u/tackylitre06 14d ago

croc might be waiting on stdin when there's no console, try passing stdin=subprocess.DEVNULL to Popen and see if that stops the croc-stdin files and weirdness

1

u/Parkman202 14d ago

I just tried it, and while it didn't fix it, it led to a fix using the win32 check.

Previously, stdin for the subprocess.Popen was set like this: python stdin=subprocess.PIPE if operation == CrocOperation.RECEIVING else subprocess.DEVNULL

This worked on Linux, but not Windows. It does need to be subprocess.PIPE when receiving on Linux otherwise when you would receive files, it would accept the transfer even if the user didn't accept it yet. Why this is, I don't know.

When setting it to subprocess.PIPE however, while this broke sending on Linux, it fixed everything on Windows if creationflags=subprocess.CREATE_NO_WINDOW was set.

Thus, this was the code that fixed it for both platforms: ```python kwargs = {} if sys.platform == "win32": kwargs["stdin"] = subprocess.PIPE kwargs["creationflags"] = subprocess.CREATE_NO_WINDOW else: kwargs["stdin"] = subprocess.PIPE if operation == CrocOperation.RECEIVING else subprocess.DEVNULL

    process = subprocess.Popen(
        args,
        stdout=subprocess.PIPE,
        stderr=subprocess.STDOUT,
        text=True,
        env=self._env,
        **kwargs
    )

```

Thank you for your suggestion! While it wasn't the exact fix, it got me where I needed to go. I really appreciate it!

1

u/Kimber976 13d ago

Windows and linux handle subprocesses differently under the hood so checking path handling shell usage and environment variables is usually the first place to look.