最近又在写ansible相关的东西,因为得集成python项目,我开始都想之间命令方式调用ansible了。因为之前搞了好几个版本的ansible集成,每次都是大折腾一段时间,而且最早的时候,因为要集成或者解决问题,需要查看ansible源码,发现就是一坨。我好像不止一次跟别的同事说ansible是看过的最垃圾的开源代码。
最近又开始折腾了,虽然封装的接口还是不好用,但是看源码和修改给的example,最起码折腾起来轻松一点点了。源码也写的比之前好太多了。
playbook这玩意更坑,可能也是我写的不太多。
Read more...
Archive for python
matplotlib画图依赖_tkinter的问题解决
最近接入他们大学的课题程序,很多都是用matplotlib画图,展示最终的精确度之类的。
然后我用pyenv+virtualenv安装那些程序的依赖,然后matplotlib需要_tkinter,这个还需要系统安装,虽然能装,但是挺费劲,还有种方式可能需要重新编译python,也是麻烦。网上搜索发现可以不使用的方法,每次装一次找一次,决定记录一下了。
直接import matplotlib.pyplot as plt
会报错ImportError: No module named _tkinter, please install the python-tk package
一般都是用plt.show()方式直接展示。我给改成plt.savefig()报错图片了。
网上找到的方法
在import之前加入两行:import matplotlib
matplotlib.use('agg')
然后就可以了。好像是选了matplotlib里gui别的显示方式,就不依赖_tkinter了
Read more...
使用python压缩扫描pdf文件,压缩率0.1
代码基本网上搜的,没啥贴的,写一下思路,有需要的自己网上搜就行了。主要是自己看的电子书是扫描版的pdf,里边全是图片,超级大,好几百M。之前找了一圈,靠谱软件需要收费,不靠谱个人软件只能windwos下用。于是自己搞吧,发现也不太难。
第一步是把pdf提取图片
用到pymupdf库,这个是封装的c的接口,感觉写个pdf软件也不难啊,都有开源库,不知道为啥他们还收费。我提取图片发现,好多水印图片是直接用pdf编辑软件加上的,导致水印可以直接从提取的图片里边删掉,这个挺好。
第二步,把图片二值化
这里用到opencv,用到cv2.threshold。这个阈值不太好设置,可以使用cv2.THRESH_OTSU,提取一个分析值,但是这个不是最优的,有的书是半彩页的,书边会有浅色的背景之类,或者重点颜色等。这个有分析建议值之后,还需要自己试一下哪个值比较好。
第三部,把图片生成新pdf
这个还是用pymupdf库,基本都差不多。
没想到的是压缩率还挺低,一百多MB的pdf能压缩成十多兆。可能也是之前的pdf质量比较高吧。但是我看了一下压缩后的,清晰度一点不减少。有背景色的地方,选择阈值合适,背景色也都过滤掉了,文字不受影响。
Read more...
Python源码特殊点分析3-运行导入相关
主要看pymain_run_filename,是文件执行pymain_open_filename,不是文件赋值fp = stdin,最后执行pymain_run_file(fp, pymain->filename, cf)。看这里应该交互和文件执行都在这一个里边的。
pymain_open_filename就是打开文件返回句柄。
pymain_run_file文件里,主要就是调用PyRun_AnyFileExFlags。下面应该与main.c没关系了,主要在Python/pythonrun.c里了.
pymain_repl里调用 PyRun_AnyFileFlags,然后也是调用的PyRun_AnyFileExFlags。
PyRun_AnyFileExFlags里, Py_FdIsInteractive判断是否是交互模式,如果是调用PyRun_InteractiveLoopFlags,里边有个do,这个不看了。如果不是调用PyRun_SimpleFileExFlags,
先调用PyImport_AddModule,interp->modules里插入了__main__,对我没啥用。maybe_pyc_file判断是否是pyc文件,比较了文件后缀,MagicNumber.
MAGIC_NUMBER是在Lib/importlib的_bootstrap_external.py文件里定义的,直接写在python文件里了。
如果像pyc文件,关闭文件句柄,重新打开文件,然后调用set_main_loader,再调用run_pyc_file。
如果不像pyc文件,调用PyRun_FileExFlags。
set_main_loader中通过loader_name,从importlib/_bootstrap_external中获取loader,源码为SourceFileLoader,pyc为SourcelessFileLoader,设置到__loader__
run_pyc_file,先比较MagicNumber是否匹配,然后PyMarshal_ReadLastObjectFromFile读取文件,其中的就不看了,不关心。PyEval_EvalCode应该是执行代码了,这个也不看了。
然后就完结了。
import 相关
第一篇里边的pymain_init_sys_path,调用pymain_get_importer,然后调用PyImport_GetImporter,然后调用get_path_importer,然后从path_hooks里找合适的import的方法进行导入
pymain_update_sys_path更新sys.path
config_init_module_search_paths里有个Py_GetPath(,这里比较乱,调用了几次_PyPathConfig_Init,我也没仔细看。_PyPathConfig_Calculate里应该是初始化的path,没仔细看。
第三方库的搜索路径添加在initsite,导入Lib/site.py模块,执行addusersitepackages里添加的
Read more...
Python源码特殊点分析2-参数解析分析
按照上一篇的流程
Modules/main.c里, pymain_main里pymain_init里的pymain_cmdline,是解析参数的。
初始化一个_PyCmdline,暂存参数的
line464
typedef struct {
wchar_t **argv;
int nwarnoption; /* Number of -W options */
wchar_t **warnoptions; /* -W options */
int nenv_warnoption; /* Number of PYTHONWARNINGS options */
wchar_t **env_warnoptions; /* PYTHONWARNINGS options */
int print_help; /* -h, -? options */
int print_version; /* -V option */
int bytes_warning; /* Py_BytesWarningFlag, -b */
int debug; /* Py_DebugFlag, -b, PYTHONDEBUG */
int inspect; /* Py_InspectFlag, -i, PYTHONINSPECT */
int interactive; /* Py_InteractiveFlag, -i */
int isolated; /* Py_IsolatedFlag, -I */
int optimization_level; /* Py_OptimizeFlag, -O, PYTHONOPTIMIZE */
int dont_write_bytecode; /* Py_DontWriteBytecodeFlag, -B, PYTHONDONTWRITEBYTECODE */
int no_user_site_directory; /* Py_NoUserSiteDirectory, -I, -s, PYTHONNOUSERSITE */
int no_site_import; /* Py_NoSiteFlag, -S */
int use_unbuffered_io; /* Py_UnbufferedStdioFlag, -u, PYTHONUNBUFFERED */
int verbosity; /* Py_VerboseFlag, -v, PYTHONVERBOSE */
int quiet_flag; /* Py_QuietFlag, -q */
const char *check_hash_pycs_mode; /* --check-hash-based-pycs */
#ifdef MS_WINDOWS
int legacy_windows_fs_encoding; /* Py_LegacyWindowsFSEncodingFlag,
PYTHONLEGACYWINDOWSFSENCODING */
int legacy_windows_stdio; /* Py_LegacyWindowsStdioFlag,
PYTHONLEGACYWINDOWSSTDIO */
#endif
} _PyCmdline;
然后调用cmdline_get_global_config,设置默认参数,默认参数都在 Python/pylifecycle.c 全为0.后面的cmdline_set_global_config是设置全局值的。两个是一对
然后调用pymain_cmdline_impl,里面调用pymain_read_conf,看了一下,主要在函数pymain_parse_cmdline_impl里面。其他的就不看了。
cmdline_get_env_flags是从环境拿值设置参数的。
pymain_init_core_argv是设置-c-m俩参数
没想到比较简单,没有细看,看的这些基本能达到目的了。有需要再补充
Read more...
Python源码特殊点分析1-入口函数分析
看Makefile.pre.in文件,入口函数在Programs/python.c文件里,发现main函数。
只调用的Modules/main.c的_Py_UnixMain函数,这里面初始化了_PyMain,用来保存参数,然后调用pymain_main
先调用pymain_init,后调用pymain_run_python,最后调用pymain_free.
pymain_init里,初始化_PyCoreConfig,大体看是pyvm的一些参数,先不关心。然后调用pymain_cmdline,大体看是解析参数的,不关心。pymain_init_stdio设置了stdin_is_interactive的值,这个可能有用,先记录。然后调用_Py_InitializeCore,里边调用_Py_InitializeCore_impl,里边各种初始化,不跟进去看了。
里边调用了一个_PyImport_Init,没啥,_PyImportHooks_Init,初始化了几个sys的参数,但是都是空值, sys.meta_path, sys.path_importer_cache,sys.path_hooks,这几个有用。
_Py_InitializeCore_impl最后调用了一个initimport(, sys.modules不知道在哪初始化的,可能有用,先不找了。importlib的导入,然后是_PyImportZip_Init.
_PyImportZip_Init在Python/import.c里,导入zipimport,path_hooks里加入了一个zipimport.zipimporter。搞定
回到 pymain_init, 最后调用了 pymain_init_python_main 和pymain_init_sys_path.
pymain_init_python_main调用_Py_InitializeMainInterpreter好像仅仅是重新设置了一下参数,这里没看懂,从自己复制了参数设置了args,有设置了一遍。没啥用先不管了。
pymain_init_sys_path这个看名字是要找的了, 第一个是zip或者目录导入,这个有用,记录一下,暂时不会用。从参数中获取sys.path的配置,最后调用pymain_update_sys_path插入到sys.path里边
pymain_run_python中,先调用pymain_header,pymain_import_readline,pymain_run_command,pymain_run_module, pymain_run_filename, pymain_repl.
pymain_header,如果是交互模式,打印了版本和操作系统。可以查到版本信息在Include/patchlevel.h
pymain_import_readline 为交互模式导入readline模块。pymain_run_command,直接执行的代码。pymain_run_module执行模块的.
pymain_run_filename 执行文件的。
pymain_repl 解析器模式
这部分就基本看完了,下边要单独看一下,启动参数解析的地方
Read more...
python使用pyVmomi调用VMware api创建cluster,传入vim.cluster.DasConfigInfo,指定的参数不正确
先写解决方案:制定dasconfig的failoverLevel = 1,就解决了。
这是一个必传的参数。但是官网特别说明,所有参数都是可选的。All fields are optional. 然后报错“指定的参数不正确:”也没写哪个参数不正确。
我是google搜索找到了解决方案,而且还是六七年前的帖子,也就是说这个问题持续六七年了吗???
vmware文档啥分类没有,我找都是从manager到数据方法,或者dir看到了,直接搜索。太麻烦了。而且返回的数据是关联返回的,虽然可以制定返回的字段。
Read more...
centos 安装mysqlclient,mysql session错误的问题
折腾了我一个周五下午加一个周末的问题。
写的代码上线两个周了,之前只测试了同步跟列表的功能,展示没问题,上周测试了一下更新问题,开始删除没刷新,我还以为是前端老版本的编译有问题,发现接口确实调用了就没管了。
直到测试一个更新功能,发现接口调用成功了,但是数据没更新。还返回了http 200。我就看了一下代码,返回200不可能没执行到更新保存的代码。
发现只有在提交commit之前有查询操作,就会导致表更新失败。所以应该是个session问题。
试了我本机环境没问题,然后我就想到装的python包的版本问题,踩什么bug了吧。然后我对比了一下版本,我管理都是只写主要包的版本,依赖包的版本没有写,所以依赖包有的版本会高一点。
我选了几个可能的包,更换版本,发现不是这个原因。周末想想,如果是这个包的问题,应该早就发现了,会打补丁。
然后我就想到数据库的问题,我用的mariadb,测试系统是centos,然后我看了一下有新版本的mariadb,更新了一下,这里现在想想数据库的问题的概率也十分小了,而且是这么明显的bug。
mariadb的源巨慢无比,我又在尝试更新mysqlclient包,因为我记得安装的时候mariadb-devel不能用,网上说的用mysql-devel。我又从新试了一次,各种方法,发现不成功。
到下午快下班,安装完mariadb,又试了一遍mariadb-devel也不行。觉得还是用mysql,又安装称mysql。
但是发现数据库数据文件不兼容,升级的命令好像也不太行,我备份的导出数据库数据,导入又有外键关联的限制,懒得搞数据了。下班了。
周末有时间的时候就想了想,我之前虽然抓包测试环境mysql,发现没问题,但是没对比本机的。如果周一mysql不行,试试本机环境连线上数据库,排除一下是包的问题还是数据库问题。感觉周五的时候应该先排除问题再更新数据库才对。
周一来,同步了一下数据,更新了一下发现mysql还是不行。然后重新编译mysqlclient,测试发现通过了。
最后还是mysqlclient编译依赖有问题,使用mysql-devel编译,连接mariadb有问题。好像还不是所有的版本有这个问题。
Read more...
用antlr生成Python语言的mysql语法解析器
做个项目要解析mysql sql语句,使用开源的库发现问题很多,不太适合。推荐的几个项目都是项目里边集成的,语言也不符合,太麻烦。只能自己搞了,选择antlr的原因是官方提供mysql解析的语法,做起来比较简单。
安装:
curl -O http://www.antlr.org/download/antlr-4.5.3-complete.jar
然后写一个shell脚本antlr,用来执行命令
#!/bin/bash
java -cp .:./antlr-4.5.3-complete.jar:$CLASSPATH org.antlr.v4.Tool $*
从https://github.com/antlr/grammars-v4.git clone一些已经写好的sql语法文件。
生成Python版的解析代码。
./antlr -Dlanguage=Python2 MySQLLexer.g4
./antlr -Dlanguage=Python2 -visitor MySQLParser.g4
第二个加参数是为了生成MySQLGrammarVisitor。
然后参照例子写了个函数包起来就能用。这工具做的好,原因就是不用研究太深就能用。
Read more...
virtualenv源码阅读
想研究一下virtualenv是怎样保证python运行环境独立的。库里边带了几个二进制,导致clone源代码的时候发现库好大啊,当时就祈祷源码不要太多。
目录结构
大体看了一下文件目录,主要文件就一个virtualenv.py。(也确实安装完就这一个文件)
bin/rebuild-script.py这个文件是用来构建新的virtualenv.py文件的。
virtualenv_embedded 文件夹里边的文件是 生成虚拟环境后的脚本,但不是最终版的。上边构建文件将这几个文件插入virtualenv.py
virtualenv_support 几个二进制库
创建一个环境
$ virtualenv ENV
从virtualevn main()入手,2000多行。前边解析参数,extend_parser和adjust_options可以自定义修改参数,目的应该是可以使创建环境的命令简单。after_install是环境创建完成后执行,可以做一些基础包的安装等操作。我猜virtualenvwrapper应该会对这几个函数定义。
--python参数可以指定解析器的版本。resolve_interpreter是返回指定解析器的绝对路径,作者使用path变量和提供的执行文件名组合再判断文件是否存在,是否可执行来判断。我感觉可以直接使用which取一下,差不多。
VIRTUALENV_INTERPRETER_RUNNING是判断是否使用的指定解析器,不是指定解析器,就更改变量,用指定解析器重新执行。是的话就继续执行。在加载文件的时候判断是指定解析器,把virtualenv安装环境的path去掉。
后面删掉了PYTHONHOME,make_environment_relocatable好像是对环境目录移动复制等操作后进行重定向,这个没用过。主要就把环境bin目录下的文件的python执行解析器,修改lib库文件路径
create_environment就是创建环境的函数了,第一个参数是环境根目录,后边都是执行参数。
path_locations 根据不同系统解析器环境,返回要创建的环境的目录
install_python,开始先判断了不能用虚拟环境创建自己,clear参数清除环境下的lib库。使用sys.prefix找到python安装路径,还有一个real_prefix和base_prefix这俩路径官网没查到什么意思
创建新环境的lib文件夹,fix_lib64是为了兼容一些别的环境lib文件夹命名不同,咋不开始就定好。使用os库找到标准库的目录stdlib_dirs.然后将标准库文件软连接到新环境的lib下,copy_file分析了是软连接然后找到真实文件,连接到真实文件上.
还有一个REQUIRED_MODULES必要库,先判断这些必要库是否在标准库里边,不在通过imp.find_module找到,同样添加软连接到lib目录.这个应该也是为了兼容吧.其中lib目录有单独的一套,卧槽 我只能说这是python的坑吗,我觉得肯定不是作者特意写好多。(具体不了解不同系统有啥区别也不想看)
创建lib目录下的site-packages,site又是一套我查.include也是软连接
bin目录python二进制复制,那么多代码都是不同py实现的结果,其实就一句copy.不是windows解析器二进制还有带版本号的。然后测试了一把解析器能不能执行。最后返回新环境python的路径
install_distutils 安装
install_wheel 安装安装包的工具.
install_activate 安装环境的启动退出脚本
install_python_config 安装python_config文件
上边两个模式基本相同,源文件在virtualenv_embedded下,通过编译到virtualenv.py文件里,然后安装的时候替换相关变量,然后生成新的文件。
最后再看看activate.sh文件如何启动
执行这个脚本的时候,先进行deactivate操作,相当于切换环境。主要就是判断之前的环境变量保存的几个变量是否存在,存在还原, 并删除。因为可以不存在,当前使用的system的python环境.ps1也还原
下边进行新环境变量的赋值。记录要切换的环境根目录,path加入环境bin目录, 删掉pythonHOME.更改ps1加上环境名,替换。pydoc重新绑定,变成了现在的pydoc。
然后就没了
Read more...