python html5 bootstrap 视频教程

德云社区

 找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

数字 IDE 网页 ── \"所见即所得\" 编写 HTML4、HTML5 静态 \"网站站群\" 动态网站,生成 Robots、站点地图,死链检测,强大文本编辑器功能。。。集成中英自动 TTS 文本诵读功能。。。
数字 IDE Python ── 支持 \"极速编写-重构\" Python、Django、HTML5、XML、C/C++、Java、Perl、PHP、Ruby、C#、VB .Net 等程序源代码。。。集成了很多常用编程智能辅助工具。。。
业务范围:
01、企业 \"内部 | 外部\" 数据管理、分析、挖据
02、企业效率优化专用 APP 程序定制、IT 外包
03、智能数字化系统平台架设、开发、部署、维护
04、高仿真、低成本,可动画-可 VR 数字样机建模、开发、规划
05、企业 Linux 云计算 \"IaaS | SaaS\" 服务器架设、开发、部署、维护
06、\"标准 | 非标\" 设备全流程研发,PLC、单片机、工控系统开发、集成
07、企业 Linux / Windows 平台 PLM、ERP 系统架设、开发、部署、维护
08、企业 Linux / Windows 平台 \"内网 | 外网\" 服务器架设、开发、部署、维护
09、企业微信公众号智能互动营销、策划,Web 网站代码、SEO 排名优化,大数据网络推广
10、企业跨平台、跨硬件、跨浏览器 HTML5 Web 电子商务平台、\"静态 | 动态\" 网站开发、部署、维护
11、企业文档、资料、公司网页多语种翻译,数据资料标准化、版本化、数字化管理的规划、研发、实施
数字翻译 ── 能 \"批量翻译\" 文档 GUI 用户界面。。。网页浏览 批处理 全文搜索 全文替换 全文删除 全文插入 数据比较 。。。集成中英自动 TTS 文本诵读功能。。。
查看: 425|回复: 0

CPython2.7 subprocess.Popen()不忍直视的Unicode中文路径Bug 解决办法

[复制链接]

150

主题

152

帖子

924

积分

高级技师

Rank: 4

金钱
577
金币
19
威望
0
贡献
0
发表于 2018-1-25 12:52:30 | 显示全部楼层 |阅读模式
|          
CPython2.7 subprocess.Popen()不忍直视的Unicode中文路径Bug 解决办法

On Windows, Python2.7 subprocess.Popen() requires the executable name and working directory to be ascii.  This is because it calls CreateProcess instead of CreateProcessW.


Python2.7 subprocess.Popen() parameters are Unicode strings.  It may support bytes string, but passing bytes to OS functions on Windows is deprecated, especially for filenames.


In Python2, windows implementation of subprocess.Popen(..) and sys.argv use the non unicode ready windows systems call CreateProcess(..),and does not use GetCommandLineW(..) for sys.argv.


In Python3, windows implementation of subprocess.Popen(..) make use of the correct windows systems calls CreateProcessW(..) starting from 3.0 (see code in 3.0), and sys.argv uses GetCommandLineW(..) starting from 3.3 (see code in 3.3).


百度网盘

https://pan.baidu.com/s/1cKxajG


软件仓库

https://github.com/digitser

https://digitser.sourceforge.io/

https://pan.baidu.com/s/1dGxcM7R


快速高效 智能编辑 重构 批处理 "数字化 Python IDE" 集成开发环境

http://dts.digitser.cn/zh-CN/ide/idepy/index.html

http://forum.digitser.cn/thread-2177-1-1.html


不修正 Python2.7 subprocess.Popen() Unicode 路径原因

It's tricky to fix this issue in Python2.7 because you have to choose which function is used: CreateProcessA() (bytes) or CreateProcessW() (Unicode). To use CreateProcessW(), you have to decode bytes parameter.


Python3.x has os.fsencode()/os.fsdecode() functions, similar functions in C. The "mbcs" Python codec is strict by default, but it now supports any Python error handler. This change changed was improved in each Python 3 release.


Python 2 has PyUnicode_DecodeMBCSStateful() and PyUnicode_EncodeMBCS() which use the default Windows behaviour. using PyUnicode_DecodeMBCSStateful() (or directly MultiByteToWideChar) + CreateProcessW() is exactly the same than calling CreateProcessA().


Should support CreateProcessA() and CreateProcessW(), and use one or the other depending on the type of the pararameters?


Such change requires too much work and it is not enough to have a full Unicode support for filenames. Have to fix much more code. Already did all this work in Python3.x (in 3.1, 3.2 and then 3.3). Suggest to upgrade to port the application to Python3.x, if want a full Unicode support. Using Unicode in Python3 is natural and just works fine.

CreateProcessW

以下是 Python3.4 subprocess.Popen 类源代码 _winapi.CreateProcess 部分,要调用的 _winapi.c 代码片段。


开始采用 CreateProcessW,说明 Unicode 中文路径 Bug 问题已彻底修复。

  1. static PyObject *
  2. winapi_CreateProcess(PyObject* self, PyObject* args)
  3. {
  4.     BOOL result;
  5.     PROCESS_INFORMATION pi;
  6.     STARTUPINFOW si;
  7.     PyObject* environment;
  8.     wchar_t *wenvironment;

  9.     wchar_t* application_name;
  10.     wchar_t* command_line;
  11.     PyObject* process_attributes; /* ignored */
  12.     PyObject* thread_attributes; /* ignored */
  13.     BOOL inherit_handles;
  14.     DWORD creation_flags;
  15.     PyObject* env_mapping;
  16.     wchar_t* current_directory;
  17.     PyObject* startup_info;

  18.     if (! PyArg_ParseTuple(args, "ZZOO" F_BOOL F_DWORD "OZO:CreateProcess",
  19.                            &application_name,
  20.                            &command_line,
  21.                            &process_attributes,
  22.                            &thread_attributes,
  23.                            &inherit_handles,
  24.                            &creation_flags,
  25.                            &env_mapping,
  26.                            ¤t_directory,
  27.                            &startup_info))
  28.         return NULL;

  29.     ZeroMemory(&si, sizeof(si));
  30.     si.cb = sizeof(si);

  31.     /* note: we only support a small subset of all SI attributes */
  32.     si.dwFlags = getulong(startup_info, "dwFlags");
  33.     si.wShowWindow = (WORD)getulong(startup_info, "wShowWindow");
  34.     si.hStdInput = gethandle(startup_info, "hStdInput");
  35.     si.hStdOutput = gethandle(startup_info, "hStdOutput");
  36.     si.hStdError = gethandle(startup_info, "hStdError");
  37.     if (PyErr_Occurred())
  38.         return NULL;

  39.     if (env_mapping != Py_None) {
  40.         environment = getenvironment(env_mapping);
  41.         +
  42. −if (! environment)
  43.             return NULL;
  44.         wenvironment = PyUnicode_AsUnicode(environment);
  45.         if (wenvironment == NULL)
  46.         {
  47.             Py_XDECREF(environment);
  48.             return NULL;
  49.         }
  50.     }
  51.     else {
  52.         environment = NULL;
  53.         wenvironment = NULL;
  54.     }

  55.     Py_BEGIN_ALLOW_THREADS
  56.     result = CreateProcessW(application_name,
  57.                            command_line,
  58.                            NULL,
  59.                            NULL,
  60.                            inherit_handles,
  61.                            creation_flags | CREATE_UNICODE_ENVIRONMENT,
  62.                            wenvironment,
  63.                            current_directory,
  64.                            &si,
  65.                            &pi);
  66.     Py_END_ALLOW_THREADS

  67.     Py_XDECREF(environment);

  68.     if (! result)
  69.         return PyErr_SetFromWindowsErr(GetLastError());

  70.     return Py_BuildValue("NNkk",
  71.                          HANDLE_TO_PYNUM(pi.hProcess),
  72.                          HANDLE_TO_PYNUM(pi.hThread),
  73.                          pi.dwProcessId,
  74.                          pi.dwThreadId);
  75. }
复制代码

解决办法

测试了 windll.kernel32.CreateProcessW() 方法,直接 windll.kernel32.CreateProcessW(u"C:\\WINDOWS\\system32\\calc.exe", None, None, None, None, creation_flags, None, None, byref(startupinfo), byref(process_info)) 没问题。


由于水平问题,若要  windll.kernel32.CreateProcessW(u"E:\\Python27\\python.exe **process_file.py", None, None, None, None, creation_flags, None, None, byref(startupinfo), byref(process_info)) 则还是不行。估计得修改 CPython2.7 源代码,个人时间有限,暂时不弄啦。


有人参照 CPython3.x 为 CPython2.7 写了 2 个补丁,分别是 subprocess_fix 和 commandline_fix,但其 Git 地址已失效,且新进程还得考虑使用以下代码 "传递参数"。

http://vaab.blog.kal.fr/2017/03/16/fixing-windows-python-2-7-unicode-issue-with-subprocesss-popen/


找了好久,那 2 个两个补丁也没找到,还是用 Python3.x 吧。

  1. def win32_utf8_argv():                                                                                               
  2.     """Uses shell32.GetCommandLineArgvW to get sys.argv as a list of UTF-8                                          
  3.     strings.                                                                                                         
  4.                                                                                                                      
  5.     Versions 2.5 and older of Python don't support Unicode in sys.argv on                                            
  6.     Windows, with the underlying Windows API instead replacing multi-byte                                            
  7.     characters with '?'.                                                                                             
  8.                                                                                                                      
  9.     Returns None on failure.                                                                                         
  10.                                                                                                                      
  11.     Example usage:                                                                                                   
  12.                                                                                                                      
  13.     >>> def main(argv=None):                                                                                         
  14.     ...    if argv is None:                                                                                          
  15.     ...        argv = win32_utf8_argv() or sys.argv                                                                  
  16.     ...                                                                                                              
  17.     """                                                                                                              
  18.                                                                                                                      
  19.     try:                                                                                                            
  20.         from ctypes import POINTER, byref, cdll, c_int, windll                                                      
  21.         from ctypes.wintypes import LPCWSTR, LPWSTR                                                                  
  22.                                                                                                                      
  23.         GetCommandLineW = cdll.kernel32.GetCommandLineW                                                              
  24.         GetCommandLineW.argtypes = []                                                                                
  25.         GetCommandLineW.restype = LPCWSTR                                                                           
  26.                                                                                                                      
  27.         CommandLineToArgvW = windll.shell32.CommandLineToArgvW                                                      
  28.         CommandLineToArgvW.argtypes = [LPCWSTR, POINTER(c_int)]                                                      
  29.         CommandLineToArgvW.restype = POINTER(LPWSTR)                                                                 
  30.                                                                                                                      
  31.         cmd = GetCommandLineW()                                                                                      
  32.         argc = c_int(0)                                                                                             
  33.         argv = CommandLineToArgvW(cmd, byref(argc))                                                                  
  34.         if argc.value > 0:                                                                                          
  35.             # Remove Python executable if present                                                                    
  36.             if argc.value - len(sys.argv) == 1:                                                                     
  37.                 start = 1                                                                                            
  38.             else:                                                                                                   
  39.                 start = 0                                                                                            
  40.             return [argv[i].encode('utf-8') for i in                                                                 
  41.                     xrange(start, argc.value)]                                                                       
  42.     except Exception:                                                                                                
  43.         pass
复制代码

"长按二维码" 或 "扫一扫" 关注 "德云社区" 微信公众号

版权声明:
本文为独家原创稿件,版权归 德云社区,未经许可不得转载;否则,将追究其法律责任。


AI人工智能 语音助理 人工翻译 教程
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|Sitemap|手机版|小黑屋|  |网站地图  

GMT+8, 2018-8-19 08:10 , Processed in 0.082854 second(s), 15 queries , Apc On.

版权所有 © 2014-2018 德云社区

工业和信息化部:粤ICP备14079481号-2

快速回复 返回顶部 返回列表