Every documentation and answer I could find, says that in order to check if the program is "frozen" (an exe for example), we can use getattr(sys, 'frozen', False)
in the following way:
import sys
if getattr(sys, 'frozen', False):
print('program is frozen exe')
else:
print('program is a .py script')
Where False
is returned by default if the frozen
attribute doesn't exist instead of throwing an AttributeError. An example from the console:
>>> getattr(sys, 'frozen')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: module 'sys' has no attribute 'frozen'
>>> getattr(sys, 'frozen', False)
False
>>> hasattr(sys, 'frozen')
False
This is all fine, but there is a shorter version of this that does the same job, unless I'm missing something:
hasattr(sys, 'frozen')
Which simply returns True
or False
without the need to specify a default. Despite this being shorter and possibly more readable, every documentation and answer online uses getattr
instead. I'm sure there's a clever difference I might be overlooking, which is why I'm asking this question.
Example sources that refer to getattr
:
hasattr
on something else)The PyInstaller documentation itself uses the getattr
style block of code here, so by the copy-paste effect, it will proliferate.
So then the question becomes: why does the PyInstaller documentation do it this way? As others said in comments, sys.frozen
could theoretically be set to False
or some other falsy value, in which case, hasattr(sys, 'frozen')
would still return True
:
>>> class Stuff:
... pass
...
>>> x = Stuff()
>>> x.yes = True
>>> hasattr(x, 'yes')
True
>>> getattr(x, 'yes', False)
True
>>> x.no = False
>>> hasattr(x, 'no')
True
>>> getattr(x, 'no', False)
False
But as used by PyInstaller, is it possible for sys.frozen
to be set, but with a falsy value? Let's check the source code:
$ git clone git://github.com/pyinstaller/pyinstaller
Cloning into 'pyinstaller'...
...
$ cd pyinstaller
$ git grep '\(sys\.frozen\|frozen = \)' PyInstaller
PyInstaller/loader/pyiboot01_bootstrap.py: sys.frozen = True
PyInstaller/utils/win32/winutils.py: # True if "sys.frozen" is currently set.
PyInstaller/utils/win32/winutils.py: is_sys_frozen = hasattr(sys, 'frozen')
PyInstaller/utils/win32/winutils.py: # Current value of "sys.frozen" if any.
PyInstaller/utils/win32/winutils.py: sys_frozen = getattr(sys, 'frozen', None)
PyInstaller/utils/win32/winutils.py: sys.frozen = '|_|GLYH@CK'
PyInstaller/utils/win32/winutils.py: # If "sys.frozen" was previously set, restore its prior value.
PyInstaller/utils/win32/winutils.py: sys.frozen = sys_frozen
PyInstaller/utils/win32/winutils.py: del sys.frozen
So this variable is only set in two scenarios:
True
, while loading/bootstrapping; and'|_|GLYH@CK'
temporarily in a utility method.It never receives a falsy value from PyInstaller.
So in practice, using hasattr(sys, 'frozen')
will work fine. It just feels wrong from a correctness perspective, because it's an incorrectly handled edge case, which is probably why the PyInstaller documentation uses getattr
instead: it's more future-proof.