Skip to content

Execution fails if standard streams are not available #5212

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
miaopass-future opened this issue Sep 20, 2024 · 11 comments
Closed

Execution fails if standard streams are not available #5212

miaopass-future opened this issue Sep 20, 2024 · 11 comments

Comments

@miaopass-future
Copy link

miaopass-future commented Sep 20, 2024

My Python programs rely on the robot framework, and when I package and run them using a wrapper like pyinstaller, an error is reported here if the packaging parameter console is set to False

robot\running\outputcapture.py 47L

def _release_and_log(self): stdout, stderr = self._release() if stdout: LOGGER.log_output(stdout) if stderr: LOGGER.log_output(stderr) sys.__stderr__.write(console_encode(stderr, stream=sys.__stderr__))

The error message is
sys.__stderr__.write(console_encode(stderr, stream=sys.stderr)) AttributeError: 'NoneType' object has no attribute 'write'

@pekkaklarck
Copy link
Member

Thanks for the report. I've always thought that sys.__stdout__ and sys.__stderr__ would exist, but all output streams being None is documented in pyinstaller docs and also standard library docs mention this possibility. Need to check are there other places in our code base where were write to some of these streams without looking do they exist.

I'll assign this initially to RF 7.2 scope. If fixing this properly is easy, we can cherrypick the fix to RF 7.1 maintenance branch as well to get it included in RF 7.1.1.

@pekkaklarck
Copy link
Member

I looked at the code base and there were several places where we write to output streams without looking are they None. I tested disabling these streams by adding

sys.stdout = sys.stderr = sys.__stdout__ = sys.__stderr__ = None

to an imported listener and that broke Log To Console usages with the same error you got:

AttributeError: 'NoneType' object has no attribute 'write'

Fixing the above error would be easy, but when added the above code disabling streams to Robot's own code, the whole execution broke and fixing that would be a lot more complicated.

Do you have a simple example with pyinstaller that I could use to reproduce the exact problem you got? I doubt that problem is as severe as the second problem I mentioned above.

Do you have a simple example using pyinstaller that I could use to reproduce the problem?

@miaopass-future
Copy link
Author

miaopass-future commented Sep 24, 2024

spec file


# -*- mode: python ; coding: utf-8 -*-


a = Analysis(
    ['demo.py'],
    pathex=[],
    binaries=[],
    datas=[],
    hiddenimports=[],
    hookspath=['./hooks'],
    hooksconfig={},
    runtime_hooks=[],
    noarchive=False,
    optimize=0,
)
pyz = PYZ(a.pure)

exe = EXE(
    pyz,
    a.scripts,
    a.binaries,
    a.datas,
    [],
    name='demo',
    debug=True,
    bootloader_ignore_signals=False,
    strip=False,
    upx=True,
    upx_exclude=[],
    runtime_tmpdir=None,
    console=False,
    disable_windowed_traceback=False,
    argv_emulation=False,
    target_arch=None,
    codesign_identity=None,
    entitlements_file=None,
)
coll = COLLECT(
    exe,
    a.binaries,
    a.zipfiles,
    a.datas,
    strip=False,
    upx=True,
    upx_exclude=[],
    name='demo',
)

from robot import *

if __name__ == '__main__':
    run("demo.robot", outputdir="out")

Pyinstall can have problems finding dependencies and needs to be manually specified when it does not automatically find them

@pekkaklarck
Copy link
Member

Could you update the spec file to contain needed imports and also provides instructions how to use it? Notice that I don't have any experience from using pyinstaller.

@miaopass-future
Copy link
Author

Just add the hook file under the project
hooks/h_robot.py

import robot
from PyInstaller.utils.hooks import collect_all
datas, binaries, hiddenimports = collect_all("robot")

1727243359158

@franzhaas
Copy link
Contributor

@miaopass-future there is a marketsquare repo dealing with the distribution of robotframework as a single file (zipapps). pyinstaller is a different solution to a similar problem.

I would love to have a description how to do robot distribution using pyinstaller on marketsquare, in a tested way. You could make your own repo, create a pull request to this one https://github.com/MarketSquare/compact_testprogram_distribution, or share a minimal example and I can integrate it there...

@pekkaklarck
Copy link
Member

Setting up PyInstaller when I have zero experience with it looked a bit complicated. I decided to instead go through places where we write to sys.__stdout__ or sys.__stderr__ without checking are they actually available. I was also able to get this tested by (mis)using sitecustomize. I'll push changes shortly. If it's easy to backport the fix also to RF 7.1 maintenance branch, I'll do that as well. In this case this would be fixed already in RF 7.1.1.

@pekkaklarck pekkaklarck changed the title Using pyinstaller packaging, an error is reported when the parameter console configures False Execution fails if standard streams are not available Oct 17, 2024
pekkaklarck added a commit that referenced this issue Oct 17, 2024
This can happen if Robot is executed using pythonw.exe or is embedded
using, for example, PyInstaller.

Fixes #5212.
@pekkaklarck
Copy link
Member

Fixed in the master branch in 5efbcde and cherry-picked to the v7.1-maintenance branch in 7a8bbc0.

@pekkaklarck pekkaklarck modified the milestones: v7.2, v7.1.1 Oct 17, 2024
@miaopass-future
Copy link
Author

@miaopass-future there is a marketsquare repo dealing with the distribution of robotframework as a single file (zipapps). pyinstaller is a different solution to a similar problem.

I would love to have a description how to do robot distribution using pyinstaller on marketsquare, in a tested way. You could make your own repo, create a pull request to this one https://github.com/MarketSquare/compact_testprogram_distribution, or share a minimal example and I can integrate it there...

There's a problem with the way I describe it, and it's not about marketsquare, it's about sys. And Sys.. None when possible

@franzhaas
Copy link
Contributor

@miaopass-future there is no problem with the way you describe the problem. This part is good.

I would like to know how to build a robotframework pyinstaller. if you can provide me with an example, that would be highly appreciated.

@pekkaklarck
Copy link
Member

@miaopass-future, the Marketsquare is a place where the Robot Framework community can share projects, examples, etc. @franzhaas has set up a repository there for standalone Robot distributions and it already contains his zippapp based example. Having another example using PyInstaller there would be great.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants