7

I am building a fairly complex application using Python and PySide. Finally the day of the release is nearing so I want to build this application as an exe.

However, I have a strange problem on my hands. I've used PyInstaller (using version 2 by the way) in the past and never had this happened to me.

Basically, when I build the application with the --console flag, it works fine - but it opens the console window. When I build the application with the window flag (-w), it doesn't work fine. It starts and everything, but there are all these weird glitches. For example, loading a text file often raises the BadFileDescriptor error (which doesn't happen in console mode), and the application crashes after performing a certain task. What's worse is that the task is a loop, and it performs fine the first time, but when it starts working again, it crashes.

When I looked at the minidump file there were some errors about memory access violation of QtGui4.dll file. Again, this doesn't happen in console mode.

Anyone have any ideas?

Bakuriu
  • 98,325
  • 22
  • 197
  • 231
Bo Milanovich
  • 7,995
  • 9
  • 44
  • 61
  • 2
    Does your program use `print` statements or `sys.stdout.write`? Reading [here](https://groups.google.com/forum/?fromgroups=#!topic/it.comp.lang.python/3770608EYPc)(italian unfortunately for you) the bad file descriptor error could be caused by that, and you can resolve it removing/commenting out the `print`s or redirecting `sys.stdout` or using `logging` instead. It seems that when you launch an application in window mode it's `stdout` is a fixed size buffer, which would cause the memory access violations. – Bakuriu Nov 17 '12 at 11:11
  • It does use print statements. Can you tell me more about how to redirect sys.stdout? I mean, is it possible to easily replace all print statements in the software at once with a single line of code? – Bo Milanovich Nov 17 '12 at 11:13
  • 1
    You can place something like `import sys; import tempfile; sys.stdout = tempfile.TemporaryFile(); sys.stderr = tempfile.TemporaryFile()`(with better formatting hopefully) at the very beginning of the execution of your program, then all the `print`s will be redirected to a temporary file, which will be deleted when your program exits. For a more long-term solution I'd consider using `logging`. – Bakuriu Nov 17 '12 at 11:20
  • Thanks a lot! I will try this. I already have logging enabled, but most of my statements were both `print` and `logging`. Thanks again. – Bo Milanovich Nov 17 '12 at 11:23
  • @Bakuriu, hmm for some reason it is not working for me: I have this for now: `sys.stdout = tempfile.TemporaryFile() sys.stderr = tempfile.TemporaryFile()` But for some reason I can still see prints in my PyCharm console, and the application still does not work. --- EDIT, never mind, I had `reload(sys)` in one part of my code. It works now! – Bo Milanovich Nov 17 '12 at 11:33
  • Since it solved your problem I've re-written all in an answer. – Bakuriu Nov 17 '12 at 12:49

1 Answers1

10

The BadFileDescriptor error and the consequently memory access violation are caused by the fact that the stdout of applications in windowed mode is a fixed size buffer. So, if you have are writing to stdout, either with print or sys.stdout directly, after some time you'd see those errors.

You can fix this by:

  1. Removing/commenting out the writings on stdout
  2. Using logging instead of printing to stdout
  3. Redirecting stdout at the beginning of the execution of your application. This is the solution that requires less code to be changed, even though I think moving the debugging statements to logging would be the better choice.

To redirect stdout you can use this kind of code:

import sys
import tempfile
sys.stdout = tempfile.TemporaryFile()
sys.stderr = tempfile.TemporaryFile()

Just before executing your program. You can use also some custom object to put the output in "log" files or whatever, the important thing is that the output should not fill the fixed size buffer.

For example you could do something like this to be able to take advantage of the logging module without changing too much code:

import sys
import logging

debug_logger = logging.getLogger('debug')
debug_logger.write = debug_logger.debug    #consider all prints as debug information
debug_logger.flush = lambda: None   # this may be called when printing
#debug_logger.setLevel(logging.DEBUG)      #activate debug logger output
sys.stdout = debug_logger

The downside of this approach is that print executes more calls to stdout.write for each line:

>>> print 'test'
DEBUG:debug:test
DEBUG:debug:

If you want you can probably avoid this kind of behaviour writing a real write function that calls the_logger.debug only with "full lines".

Anyway I think these kind of solution should only be temporary, and be used only before porting the prints to calls to logging.debug.

(Obviously the loggers should write to a file and not to stdout to avoid the errors.)

Bakuriu
  • 98,325
  • 22
  • 197
  • 231
  • Used the second method with multiprocessing, and it complained about flush() missing - I had to add a dummy (containing only 'pass') flush routine to the debug_logger as well. – Andris Apr 26 '16 at 07:31
  • @Andris Edited to include it, just to be safe. – Bakuriu Apr 26 '16 at 07:36
  • On modern Windows, sometimes Windows will just "pause" the process, waiting for stdout to be emptied, making your program fail silently. Sigh. – std''OrgnlDave Aug 05 '17 at 15:33