Дано: Python 2.7;
на диске файлы с именами, грубо говоря,
в Unicode; обработчик таких файлов, вызываемый
через subprocess.Popen; желание сделать решение
работающее как в MS Windows, так и в POSIX.
Версия первая.
# posix works OK with unicode res = subprocess.Popen([u'python', unicode(fb2desc), u'-l', fname], stdout=subprocess.PIPE).communicate()[0]
тут fname — имя файла, строка в Unicode.
Сниппет
прекрасно работает в Linux, только надо
иметь в виду, что возвращаемый результат
будет в кодировке UTF-8 (ну или какая там
у вас локаль).
Пробуем
запустить это в MS Windows. Не работает:
Traceback (most recent call last): File "C:\Python27\Lib\runpy.py", line 162, in _run_module_as_main ... File "fb2tools\zipname.py", line 104, in callFb2desc File "C:\Python27\Lib\subprocess.py", line 710, in __init__ errread, errwrite) File "C:\Python27\Lib\subprocess.py", line 958, in _execute_child startupinfo) UnicodeEncodeError: 'ascii' codec can't encode characters in position 29-34: ordinal not in range(128)
модуль subprocess не хочет Unicode, он хочет
байтовую строку.
Подробнее про
это можно почитать тут:
Суть в том, что
в Python 2 это работать не будет, потому,
что уже работает в Python 3 и никто не хочет
переделывать старый код. Слишком много
работы. Проще обойти, задействовав
напрямую ctypes.windll.kernel32.CreateProcessW
ОК, версия
вторая, через CreateProcessW.
if sys.platform == "win32": res = [] def pout(s): res.append(unicode(s)) def perr(s): print "Error: '%s'" %s cmd = u' '.join([u'cmd.exe', u'/C', u'dir "%s" /B' % fname]) with win32proc.Win32ShellCommandController(cmd, False) as scc: scc.run(pout, None, perr) return u''.join(res)
Ура, работает. Но есть проблема с выводом,
где-то по дороге всё, что не попадает в
ascii превращается в вопросительные знаки,
типа так:
Input filename: 'c:\t\p\fb2\юникод\бабенко авангардист.fb2' fb2desc output: '??????? ???????????.fb2'
Но это еще не беда. Беда в том, что несмотря
на отработку теста с «cmd.exe», вариант с
вызовом Python снова не проходит:
Input filename: 'c:\t\p\fb2\юникод\бабенко авангардист.fb2' Error: 'Traceback (most recent call last): File "c:\t\p\fb2tools\fb2desc.py", line 704, in <module> main() File "c:\t\p\fb2tools\fb2desc.py", line 688, in main data = open(raw_filename).read() IOError: [Errno 22] invalid mode ('r') or filename: 'c:\\t\\p\\fb2\\??????\\???????'
Очевидно, интерпретатор не переваривает
на входе Unicode.
Что мы имеем
в итоге?
Вывод раз:
можно использовать Unicode при запуске из
Python 2 внешних процессов. Только надо
заменить вызовы subprocess на что-нибудь
более другое, использующее CreateProcessW. Я
использовал
if sys.platform == "win32": import _process_win32_controller as win32proc
Если разобраться, что там происходит с
выводом, то можно применять спокойно.
Вывод два: нет
смысла подавать Unicode данные на вход
процессу интерпретатора Python 2. Он это
все равно не переварит. Посему, тут надо
применять подход озвученный в
а именно —
использовать байтовые строки в UTF-8.
Ребятам оказалось удобнее использовать
utf8 везде и только иногда переводить
строки в Unicode. Я, видимо, предпочту
использовать Unicode везде и переводить
строки в utf8 только для коммуникации с
внешним процессом. Правда, придется
вносить правки в вызываемый модуль,
чего делать не хотелось. А придется.
Вывод три:
полноценная поддержка Unicode в Python 3 сильно
облегчает жизнь разработчика. По крайней
мере в некоторых случаях.
Вывод четыре:
не взирая на технологическое превосходство
ядра MS Windows
(https://speakerdeck.com/trent/pyparallel-how-we-removed-the-gil-and-exploited-all-cores),
тот компот, который устроили разработчики
смешав в кучу коней и людей в ейном API,
сильно вредит и мешает писать простые
и надежные программы. Впрочем, это
общеизвестный факт.
Некоторые
могут спросить — а нахрена ты вызываешь
внешний процесс Python из Python? Ведь проще
импортнуть модуль и вызвать напрямую
его функции? И я отвечу — а по приколу.
Это не промышленная программа а, скорее,
тренировка. Которая, очевидно, пошла на
пользу, вскрыв описанные выше нюансы.
original post http://vasnake.blogspot.com/2014/07/subprocesspopen.html
Комментариев нет:
Отправить комментарий