1

I am getting this error when I run this script in python 3.4

Traceback (most recent call last):
File "qcdlcomm.py", line 23, in <module>
if not call('whoami')[0] == 'root':
File "qcdlcomm.py", line 20, in call
return  subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE).stdout.read().strip().split("\n")
TypeError: 'str' does not support the buffer interface

I think the answer is here TypeError: 'str' does not support the buffer interface but I don't know how to implement it.

qcdlcomm.py

shantanoo
  • 3,417
  • 1
  • 22
  • 33
  • possible duplicate of [Convert bytes to a Python string](http://stackoverflow.com/questions/606191/convert-bytes-to-a-python-string) – JDong May 24 '15 at 03:27

3 Answers3

2

bytes.split() method does not accept str (Unicode type in Python 3):

>>> b'abc'.split("\n")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Type str doesn't support the buffer API

The error message is improved in Python 3.5:

>>> b"abc".split("\n")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: a bytes-like object is required, not 'str'

"\n" (str type) is a Unicode string (text) that is not bytes-like (binary data) in Python 3.


To get the output of whoami command as a Unicode string:

#!/usr/bin/env python
from subprocess import check_output

username = check_output(['whoami'], universal_newlines=True).rstrip("\n")

universal_newlines enables text mode. check_output() redirects child's stdout automatically and raises an exception on its nonzero exit status.

Note: shell=True is unnecessary here (you don't need the shell, to run whoami).


Unrelated: to find out whether you are root in Python, you could use geteuid():

import os

if os.geteuid() == 0:
   # I'm root (or equivalent e.g., `setuid`)

If you need to find out what is the current user name in Python:

import getpass

print('User name', getpass.getuser())

Beware: don't use getuser() for access control purposes!

Community
  • 1
  • 1
jfs
  • 346,887
  • 152
  • 868
  • 1,518
0

Strings are treated differently in python3. Strings are not the same as bytes. What you get from calling whoami is a byte array. To fix the call function, simply decode the bytes into a string:

def call(cmd,hide_stderr=True):                                                 
    return subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE).stdout.read().decode("utf-8").strip().split("\n")

This is a similar issue as mentioned in Convert bytes to a Python string.

Note that this is only a quick fix, but will work for your use case. Simply insert .decode('utf-8') into line 20 between .read() and .strip().

JDong
  • 2,046
  • 3
  • 18
  • 39
  • 1
    In general, you shouldn't hardcode `utf-8` encoding. Pass `universal_newlines=True` or use `locale.getpreferredencoding(False)` encoding. – jfs May 24 '15 at 14:48
  • Thanks for the help. Will this work? '`def call(cmd,hide_stderr=True): return subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE).stdout.read().decode("utf-8").strip().split("\n")` `#make sure we are root if not call('whoami', universal_newlines=True)[0] == 'root': print ("run as root") sys.exit()` – lunaticMonk May 24 '15 at 16:37
  • No. The `call` function defined on line 19 does not have a `universal_newlines` argument. Simply replace the function definition on lines 19-20 with the version I gave you as a quick fix. – JDong May 24 '15 at 17:27
0

Generally if you want to use text mode with Popen in Python 3 (and assuming that you use cPython implementation), you can pass either encoding, error or universal_newlines argument to Popen constructor. For example:

return subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, universal_newlines=True).stdout.read().strip().split("\n")

Thats because in cPython implementation of the __init__ in Popen there is a following line:

text_mode = encoding or errors or universal_newlines

This is also what check_output in cPython internally does - it assures that input keyword argument (if present and None) is of appropriate type and passes the keyword arguments to run (which later calls Popen):

def check_output(*popenargs, timeout=None, **kwargs):
    if 'stdout' in kwargs:
        raise ValueError('stdout argument not allowed, it will be overridden.')

    if 'input' in kwargs and kwargs['input'] is None:
        # Explicitly passing input=None was previously equivalent to passing an
        # empty string. That is maintained here for backwards compatibility.
        kwargs['input'] = '' if kwargs.get('universal_newlines', False) else b''

    return run(*popenargs, stdout=PIPE, timeout=timeout, check=True,
**kwargs).stdout

See more in subprocess.py source.

krassowski
  • 6,277
  • 1
  • 30
  • 55