0

While trying to run a telemetry python application on Python 3.9, it fails with the error "TypeError: a bytes-like object is required, not 'str'"

Attempting to fix the code by changing 'r' to 'rb' (and 'w' to 'wb') as suggested elsewhere results in a different error.

Unfortunately, I can't figure this one out. Can anyone help me identify the problem here? I'm very new to this. Thanks in advance.

  def __init__(self):
    try:
      with open(STATUS_FILE, 'r') as sfd:
        self.st_data = pickle.loads(sfd.read())
    except IOError:
      self.st_data = dict(seq=0, timestamp=int(time.time()), rx_packets=0, tx_packets=0)

    self.st_data['seq'] = (self.st_data['seq'] % 999) + 1

  def __repr__(self):
    return "%s %s" % (self.__class__, self.st_data)

  def save(self):
    self.st_data['timestamp'] = int(time.time())
    try:
      with open(STATUS_FILE, 'w') as sfd:
        sfd.write(pickle.dumps(self.st_data))
    except IOError as err:
      print(err)
  • 2
    Which line prompts the error? – James McPherson Feb 05 '22 at 22:45
  • 1
    Changing `r` to `rb` and `w` to `wb` is the right thing to do. Show *that* code and *that* error if it isn't working. This isn't a [mcve]. My attempt at making your code work...worked (made it into a class, added rb/wb, instantiated it and called save), so without seeing the error with those changes we can't help. – Mark Tolonen Feb 05 '22 at 23:07
  • @JamesMcPherson it is this line: ```self.st_data = pickle.loads(sfd.read())``` – samwathegreat Feb 05 '22 at 23:15
  • @MarkTolonen Thanks for trying to help. When I change r to rb and w to wb, the error becomes: "EOFError: Ran out of input". When I say I'm new to this, I mean it. I apologize I didn't give enough data. You can find the entire project here: https://github.com/0x9900/igate_telem Can you tell me what you mean when you say you "made it into a class"? – samwathegreat Feb 05 '22 at 23:27
  • I made an answer to demonstrate. Read the [mcve] link. Posting a link to the entire project isn't necessary. Just reduce the code to the minimum needed that still runs to produce the error. – Mark Tolonen Feb 05 '22 at 23:36
  • @MarkTolonen THANK YOU. I tried to upvote, but I don't have enough reputation to yet. Sorry. Strangely, even though the "status file" was supposed to be in the tmp directory, I was unable to find it to delete it. Instead, I rebooted the system, and now it works as expected after doing nothing more than changing r to rb and w to wb as I originally attempted before I ever started this thread. So the answer ended up being to delete the STATUS_FILE, which was achieved by a reboot. I going to guess that python uses a different tmp directory than the standard /tmp – samwathegreat Feb 05 '22 at 23:42

2 Answers2

0

Using binary file modes is the correct thing to do. Making your code into a reproducible example with those changes works. Make sure to delete any STATUS_FILE created by the original non-working code:

STATUS_FILE = 'test.pkl'   # defined missing variable

class Test:                # added shown methods to a class
    def __init__(self):
        try:
         with open(STATUS_FILE, 'rb') as sfd:  # use binary mode
            self.st_data = pickle.loads(sfd.read())
        except IOError:
            self.st_data = dict(seq=0, timestamp=int(time.time()), rx_packets=0, tx_packets=0)

        self.st_data['seq'] = (self.st_data['seq'] % 999) + 1

    def __repr__(self):
        return "%s %s" % (self.__class__, self.st_data)

    def save(self):
        self.st_data['timestamp'] = int(time.time())
        try:
            with open(STATUS_FILE, 'wb') as sfd:   # use binary mode
                sfd.write(pickle.dumps(self.st_data))
        except IOError as err:
            print(err)

t = Test()   # instantiate class, read from save file if present
print(t)     # view the result
t.save()     # create the save file

Output from multiple runs. Sequence increments and timestamp changes.

<class '__main__.Test'> {'seq': 1, 'timestamp': 1644104029, 'rx_packets': 0, 'tx_packets': 0}
<class '__main__.Test'> {'seq': 2, 'timestamp': 1644104029, 'rx_packets': 0, 'tx_packets': 0}
<class '__main__.Test'> {'seq': 3, 'timestamp': 1644104030, 'rx_packets': 0, 'tx_packets': 0}
<class '__main__.Test'> {'seq': 4, 'timestamp': 1644104030, 'rx_packets': 0, 'tx_packets': 0}
<class '__main__.Test'> {'seq': 5, 'timestamp': 1644104031, 'rx_packets': 0, 'tx_packets': 0}
Mark Tolonen
  • 166,664
  • 26
  • 169
  • 251
-1

pickle.loads() takes a bytes-like object as the first argument. You're sending a string. See: https://docs.python.org/3/library/pickle.html#pickle.loads

Converting a string to bytes is covered in more depth here: Best way to convert string to bytes in Python 3?

The easiest way for you is probably:

my_data = bytes(sfd.read(), 'utf-8')
jsfehler
  • 154
  • 3