If I am using optional argument input for calling subprocess.Popen(command, **kwargs)
I am confronted with an interesting Inspection Warning in PyCharm when I .communicate()
on he return and wants to .decode('utf8')
the output.
def my_call(command, **kwargs):
process = subprocess.Popen(command, **kwargs)
out, err = process.communicate()
return out.decode('utf8'), err.decode('utf8') if err else err # err could be None
Unresolved attribute reference 'decode' for class 'str'
Since the default of the output of .communicate()
is bytestring (as described here) it shouldn't be a runtime problem if the function is not called with encoding
as an optional argument. Nevertheless I am not pleased with this, given that in future this might happen and causes an AttributeError: 'str' object has no attribute 'decode'
The straightforward answer would be to make an if-case or a try-catch-operation around the decoding arguments, like this:
if 'encoding' in kwargs.keys():
return out, err
else:
return out.decode('utf8'), err.decode('utf8') if err else err
Or:
try:
return out.decode('utf8'), err.decode('utf8') if err else err
catch AttributeError:
return out, err
But I would not get rid of the Inspection warning this way.
Ignoring unresolved reference problem is not an option.
I have tried to set the encoding parameter to None as default: subprocess.Popen(command, encoding=None, **kwargs)
, which did not work.
The return types in Python are hard-coded and do not depend on the input arguments given to the function (at least to the best of my knowledge). Changing the input parameter to subprocess.Popen(command, encoding=None, **kwargs)
will therefore not have any impact on the expected return type of the function. To get rid of the warnings, my suggestion would be to use typing combined with your try-catch block:
def my_call(command, **kwargs):
process = subprocess.Popen(command, **kwargs)
err: bytes
out: bytes
out, err = process.communicate()
try:
return out.decode('utf8'), err.decode('utf8') if err else err
catch AttributeError:
# Optionally throw/log a warning here
return out, err
Alternatively you could use a version where you use an if
-condition with isinstance(err,bytes) and isinstance(out, bytes)
which would likely also resolve the warning and won't throw an error, but in Python you ask for forgiveness and not for permission EAFP