Записки программиста, обо всем и ни о чем. Но, наверное, больше профессионального.

2014-07-08

subprocess.Popen

Дано: 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

Комментариев нет:

Отправить комментарий

Архив блога

Ярлыки

linux (241) python (191) citation (186) web-develop (170) gov.ru (159) video (124) бытовуха (115) sysadm (100) GIS (97) Zope(Plone) (88) бурчалки (84) Book (83) programming (82) грабли (77) Fun (76) development (73) windsurfing (72) Microsoft (64) hiload (62) internet provider (57) opensource (57) security (57) опыт (55) movie (52) Wisdom (51) ML (47) driving (45) hardware (45) language (45) money (42) JS (41) curse (40) bigdata (39) DBMS (38) ArcGIS (34) history (31) PDA (30) howto (30) holyday (29) Google (27) Oracle (27) tourism (27) virtbox (27) health (26) vacation (24) AI (23) Autodesk (23) SQL (23) humor (23) Java (22) knowledge (22) translate (20) CSS (19) cheatsheet (19) hack (19) Apache (16) Klaipeda (15) Manager (15) web-browser (15) Никонов (15) functional programming (14) happiness (14) music (14) todo (14) PHP (13) course (13) scala (13) weapon (13) HTTP. Apache (12) SSH (12) frameworks (12) hero (12) im (12) settings (12) HTML (11) SciTE (11) USA (11) crypto (11) game (11) map (11) HTTPD (9) ODF (9) Photo (9) купи/продай (9) benchmark (8) documentation (8) 3D (7) CS (7) DNS (7) NoSQL (7) cloud (7) django (7) gun (7) matroska (7) telephony (7) Microsoft Office (6) VCS (6) bluetooth (6) pidgin (6) proxy (6) Donald Knuth (5) ETL (5) NVIDIA (5) Palanga (5) REST (5) bash (5) flash (5) keyboard (5) price (5) samba (5) CGI (4) LISP (4) RoR (4) cache (4) car (4) display (4) holywar (4) nginx (4) pistol (4) spark (4) xml (4) Лебедев (4) IDE (3) IE8 (3) J2EE (3) NTFS (3) RDP (3) holiday (3) mount (3) Гоблин (3) кухня (3) урюк (3) AMQP (2) ERP (2) IE7 (2) NAS (2) Naudoc (2) PDF (2) address (2) air (2) british (2) coffee (2) fitness (2) font (2) ftp (2) fuckup (2) messaging (2) notify (2) sharepoint (2) ssl/tls (2) stardict (2) tests (2) tunnel (2) udev (2) APT (1) Baltic (1) CRUD (1) Canyonlands (1) Cyprus (1) DVDShrink (1) Jabber (1) K9Copy (1) Matlab (1) Portugal (1) VBA (1) WD My Book (1) autoit (1) bike (1) cannabis (1) chat (1) concurrent (1) dbf (1) ext4 (1) idioten (1) join (1) krusader (1) license (1) life (1) migration (1) mindmap (1) navitel (1) pneumatic weapon (1) quiz (1) regexp (1) robot (1) science (1) seaside (1) serialization (1) shore (1) spatial (1) tie (1) vim (1) Науру (1) крысы (1) налоги (1) пианино (1)