Почему работает этот производственный код: `base64.b64decode(api_token.encode(“utf-8)).decode(“utf-8”)`?

Сегодня на работе я увидел следующую строку кода:

decoded_token = base64.b64decode(api_token.encode("utf-8")).decode("utf-8")

Он является частью ETL-скрипта AirFlow, а decoded_token используется в качестве токена носителя в запросе API. Этот код выполняется на сервере, который использует Python 2.7, и мой коллега сказал мне, что этот код работает ежедневно и успешно.

Тем не менее, насколько я понимаю, код сначала пытается превратить api_token в байты (.encode), затем превратить байты в строку (base64.b64decode) и, наконец, снова превратить строку в строку (.decode). Я думаю, что это всегда приводит к ошибке.

import base64
api_token = "random-string"
decoded_token = base64.b64decode(api_token.encode("utf-8")).decode("utf-8")

Запуск кода локально дает мне:

Ошибка: UnicodeDecodeError: 'utf8' codec can't decode byte 0xad in position 0: invalid start byte

Какой ввод/тип должен быть api_token, чтобы эта строка нет выдавала ошибку? Это вообще возможно или должно быть что-то еще в игре?

Редактировать: Как упоминал Клаус Д., по-видимому, в Python 2 и encode, и decode потребляли и возвращали строку. Тем не менее, запуск приведенного выше кода в Python 2.7 дает мне ту же ошибку, и мне еще предстоит найти ввод для api_token, который не выдает ошибку.

для этого случая вам нужно .b64encode() перед декодированием, так как это не base64 (пока), а вашей входящей строке это может не понадобиться.. вам может понадобиться .encode() тоже (без "utf-8"), так как строка уже знает свою кодировку (упс, может дополнительно применимо только к Python 3.x)

ti7 31.03.2022 17:43

Да, как я уже говорил, это не мой код, а якобы код, который отлично работает в продакшене, и я не понимаю, как он может...

ptts 31.03.2022 17:45

В Python 2 не было байтов. И encode(), и decode() были strstr.

Klaus D. 31.03.2022 17:45

@Клаус В Python 2 это было str <-> unicode…!?

deceze 31.03.2022 17:50

@Клаус Д. Разве они не добавили тип bytes в Python 2.7, который является просто псевдонимом для str?

Mark Ransom 31.03.2022 17:51
api_token ожидается, что это строка в кодировке base 64.
deceze 31.03.2022 17:53

@deceze: Ах, ты прав! Спасибо - я как-то пропустил это... И base64.b64decode возвращает Unicode, который затем превращается в обычную строку с помощью .decode("utf-8"), верно? (P.S. Мне кажется хорошей идеей прочитать вашу последнюю статью в блоге :-))

ptts 31.03.2022 17:59

@MarkRansom Верно. Теперь я вспоминаю, как взбудоражил колледж переход на Python 2 → 3.

Klaus D. 31.03.2022 18:18
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
8
44
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Проблема, скорее всего, просто в том, что ваша тестовая входная строка не является строкой в ​​кодировке base64, в то время как в производстве, какой бы вход уже ни был!

Python 2.7.18 (default, Jan  4 2022, 17:47:56)
...
>>> import base64
>>> api_token = "random-string"
>>> base64.b64decode(api_token)
'\xad\xa9\xdd\xa2k-\xae)\xe0'
>>> base64.b64decode(api_token).decode("utf-8")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/encodings/utf_8.py", line 16, in decode
    return codecs.utf_8_decode(input, errors, True)
UnicodeDecodeError: 'utf8' codec can't decode byte 0xad in position 0: invalid start byte

кодируя строку как base64, вам также не нужно впоследствии декодировать ее как «utf-8», хотя вы можете, если ожидаете символы Unicode

>>> api_token = base64.b64encode(api_token)
>>> api_token
'cmFuZG9tLXN0cmluZw=='
>>> base64.b64decode(api_token)
'random-string'
>>> base64.b64decode(api_token).decode("utf-8")
u'random-string'

Пример с не-ascii-символами

>>> base64.b64decode(base64.b64encode("random string后缀"))
'random string\xe5\x90\x8e\xe7\xbc\x80'
>>> base64.b64decode(base64.b64encode("random string后缀")).decode("utf-8")
u'random string\u540e\u7f00'
>>> sys.stdout.write(base64.b64decode(base64.b64encode("random string后缀")) + "\n")
random string后缀

Обратите внимание, что в Python 2.7 bytes — это просто псевдоним для str, а специальный unicode был добавлен для поддержки юникода!

>>> bytes is str
True
>>> bytes is unicode
False
>>> str("foo")
'foo'
>>> unicode("foo")
u'foo'

Я думаю, что древняя поговорка «Мусор на входе, мусор на выходе» здесь как нельзя лучше подходит.

Mark Ransom 31.03.2022 19:37

Другие вопросы по теме