python html5 bootstrap 视频教程
德云社区 门户 IT 编程 编程语言 Python 查看内容

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

2018-1-28 06:57| 发布者: digitser| 查看: 1873| 评论: 0|原作者: Python

摘要: 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 Crea ...
AI人工智能 语音助理 人工翻译 教程
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
复制代码

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

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


路过

雷人

握手

鲜花

鸡蛋

相关阅读

最新评论

相关分类

数字网页 IDE 2021 注册机 注册码生成器 附详细破解方法
数字网页 IDE 2021 注册机 注册码生成器 附详细破解方法 完整 完美破解补丁 注册机 注册码生成器,破解后与 VIP 客户在功能方面没有任何区别。 数字 Python IDE 目前还在[8/3 天前]
数字翻译 2021 注册机 注册码生成器 内存破解器 附详细用法
数字翻译 2021 注册机 注册码生成器 内存破解器 附详细用法 完整 完美破解补丁 注册机 注册码生成器 内存破解器,破解后与 VIP 客户在功能方面没有任何区别。 数字 Pytho[49/2021-03-26]
乐数软件2021版应用 IDE 正式对外发行 2020.5版程序停止维护
乐数软件2021版应用 IDE 正式对外发行 2020.5版程序停止维护 2021 年 3 月 21 日乐数软件正式对外发行 2021 版应用 IDE 共 6 款大应用及与之配套的很多小应用,包括大家所[53/2021-03-26]
MicroPython 1.14 官方中文文档编制 帮助手册 人工翻译 在线手册
MicroPython 1.14 官方中文文档编制 帮助手册 人工翻译 在线手册 目前上传的最新 zh-CN 人工翻译版本为 MicroPython 1.14。 MicroPython 1.14 中文文档编制采用机器辅助 +[59/2021-03-18]
MicroPython 1.14 官方中文文档编制 帮助手册 人工翻译 更新日志
MicroPython 1.14 官方中文文档编制 帮助手册 人工翻译 更新日志 目前上传的最新 zh-CN 人工翻译版本为 MicroPython 1.14。 MicroPython 1.14 中文文档编制采用机器辅助 +[24/2021-03-18]
Qt for MCUs 1.7 官方文档编制 中文手册 全人工翻译 更新日志
Qt for MCUs 1.7 官方文档编制 中文手册 全人工翻译 更新日志 目前上传的最新 zh-CN 人工翻译版本为 Qt for MCUs 1.7。 Qt for MCUs 1.7 中文文档编制采用机器辅助 + 全人[28/2021-03-17]
Qt for MCUs 1.7 官方文档编制 中文教程 全人工翻译 在线手册
Qt for MCUs 1.7 官方文档编制 中文教程 全人工翻译 在线手册 目前上传的最新 zh-CN 人工翻译版本为 Qt for MCUs 1.7。 Qt for MCUs 1.7 中文文档编制采用机器辅助 + 全人[33/2021-03-17]
数字 Python IDE 2021 注册机 注册码生成器 附详细破解方法
数字IDE 注册机 注册码生成器 内存破解器 附详细使用方法 完整 完美破解补丁 注册机 注册码生成器 内存破解器,破解后与 VIP 客户在功能方面没有任何区别。 数字 Python [106/2021-02-21]
Qt 6.0精减WebEngine SerialPort Multimedia等成为半残GUI框架一览 何解
Qt 6.0精减WebEngine SerialPort Multimedia等成为半残GUI框架一览 何解 由于 Qt 集成了大量成熟模块,使之成为 C++ 领域中最好用的开源技术跨平台 GUI 开发框架。 基于 Q[62/2021-02-16]
Qt 6.0.1 官方中文文档编制 中文手册 中文帮助 更新日志
Qt 6.0.1 官方中文文档编制 中文手册 中文帮助 更新日志 以后不再上传 en-US 官方原版文档编制,目前上传的最新 zh-CN 人工翻译版本为 Qt 6.0.1。 Qt 6.0.1 中文文档编制[76/2021-02-15]
Qt 6.0.1 官方中文文档编制 中文手册 中文帮助 全人工翻译
Qt 6.0.1 官方中文文档编制 中文手册 中文帮助 全人工翻译 以后不再上传 en-US 官方原版文档编制,目前上传的最新 zh-CN 人工翻译版本为 Qt 6.0.1。 Qt 6.0.1 中文文档编[132/2021-02-15]
Visual Studio Build Tools 2017 2019 en-US for Windows VCTools C/C++编译工具
Visual Studio Build Tools 2017 2019 en-US for Windows VCTools C/C++编译工具包 主要针对 MicroSoft Windows 7 8 10 平台为进行 C/C++ 代码编译而构建的官方离线编译工[86/2021-02-11]
Windows7 Visual Studio 2017 2019 修正 vs_installer.opc certificate is invalid
Windows7 Visual Studio 2017 2019 修正 vs_installer.opc certificate is invalid Windows7 SP1 for Visual Studio Installer 2017 2019 离线安装包有时会无声自动退出,[45/2021-02-11]
Windows7 SP1如何修正Visual Studio 2017 2019 certificate is invalid 问题
Windows7 SP1如何修正Visual Studio 2017 2019 certificate is invalid 问题 Windows7 SP1 无法安装 Visual Studio 2017 2019 离线安装包的主要问题,是系统需要安装特定补[66/2021-02-11]
Windows7 修正 Visual Studio 2017 2019 安装程序清单签名验证失败
Windows7 修正 Visual Studio 2017 2019 安装程序清单签名验证失败 Windows7 SP1 for Visual Studio Installer 2017 2019 离线安装包弹出 安装程序清单签名验证失败 提示对[61/2021-02-11]

Archiver|Sitemap|小黑屋|德云社区   

GMT+8, 2021-4-16 01:37 , Processed in 0.038849 second(s), 27 queries .

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

技术支持 乐数软件     版权所有 © 2014-2021 德云社区    

返回顶部