能夠負責任的說,這篇文檔是windows10安裝pygraphviz中,在中文技術網站中最新的文檔,沒有之一。是本身徹底結合各類問題,包括調試等,總結出來的。html
問題來源:主要是可視化RvNN網絡的樹結構。python
pygraphviz安裝時,我參考了博文http://www.myexception.cn/perl-python/2046792.html。可是,文章的解決方案已經失效。git
站在巨人的肩膀上。github
前述做者在上述博文連接中闡述,windows10下安裝pygraphviz要去http://www.lfd.uci.edu/~gohlke/pythonlibs/的地址下載python packages在windows平臺上的安裝包。windows
可是,如今這個資源已經404.網絡
其提供的過程就是:先安裝graphviz,而後使用以下命令安裝pygraphviz函數
pip install pygraphviz‑1.3.1‑cp27‑none‑win_amd64.whl
很遺憾,雖然網上沒有這些資源了,我在csdn上仍是找到了以下版本:工具
惋惜,python是3.6,使用這個,依舊安裝失敗。visual-studio
由於只在python3.4版本上才能使用。測試
先安裝graphviz-2.38.msi文件。是官網上的。
去官網下載代碼。
https://github.com/pygraphviz/
直接python setup.py install,會報出找不到vc14版本的相關錯誤。
可是信息不夠詳細。因而,spyder帶參數install調試setup.py程序,看到底是哪一步出了問題。
在spyder console鍵入:
而後一直跟蹤到出錯的位置:
調試pygraphviz的setup.py 而且帶參數」install」 調試,之後發現,其出錯函數在這裏: 執行之後會提示:
那麼對於這個函數,裏面有這樣一段說明: Microsoft Visual C++ 14.0: Microsoft Visual C++ Build Tools 2015 (x86, x64, arm) Microsoft Visual Studio 2017 (x86, x64, arm, arm64) Microsoft Visual Studio Build Tools 2017 (x86, x64, arm, arm64) 也就是說:安裝對應的便可。 找到了相應的Microsoft Visual C++ 14.0 builder的生成器。可是因爲個人電腦安裝了vs2015,所以產生衝突。因此,我升級visual studio爲2017版本。下載界面在:
def msvc14_get_vc_env(plat_spec): """ Patched "distutils._msvccompiler._get_vc_env" for support extra compilers.
Set environment without use of "vcvarsall.bat".
Known supported compilers ------------------------- Microsoft Visual C++ 14.0: Microsoft Visual C++ Build Tools 2015 (x86, x64, arm) Microsoft Visual Studio 2017 (x86, x64, arm, arm64) Microsoft Visual Studio Build Tools 2017 (x86, x64, arm, arm64)
Parameters ---------- plat_spec: str Target architecture.
Return ------ environment: dict """ # Try to get environment from vcvarsall.bat (Classical way) try: return get_unpatched(msvc14_get_vc_env)(plat_spec) except distutils.errors.DistutilsPlatformError: # Pass error Vcvarsall.bat is missing pass
# If error, try to set environment directly try: return EnvironmentInfo(plat_spec, vc_min_ver=14.0).return_env() except distutils.errors.DistutilsPlatformError as exc: _augment_exception(exc, 14.0) raise |
所以,下載Microsoft Visual C++ 14.0而且安裝。可是提示Visual studio2015版本和它不兼容。因而,又升級2015版本到2017版本,而後再執行這個程序。成功。這個程序我是從csdn上下載的,後續會放到本文末尾的附件連接當中。
pygraphviz/graphviz_wrap.c(2987): fatal error C1083: Cannot open include file: 'graphviz/cgraph.h': No such file or directory
error: command 'C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\BIN\\x86_amd64\\cl.exe' failed with exit status 2
此時,修改setup.py文件,添加以下代碼。這是由於pygraphviz要對一個graphviz_wrap.c文件進行編譯,所以就要設置頭文件和庫文件的包含路徑。
注意,實際lib是在release下。
雖然添加了頭文件路徑和庫文件路徑,可是會提示沒法打開lib文件。
咱們發現,錯誤是在執行running build_ext時,注意,ext就是extension的意思,也就是在python程序中調用編譯器編譯C語言的相關文件。
能夠看到:C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\BIN\x86_amd64\cl.exe對文件進行編譯的時候,裏面已經添加了"-IC:\Program Files (x86)\Graphviz2.38\include"的頭文件路徑,解決了以前頭文件缺失的問題。
可是,在執行後續C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\BIN\x86_amd64\link.exe
即連接全部庫,生成可執行程序的時候,卻提示沒法打開cdt.lib文件,咱們看到這裏面的尋址路徑中並無以前修改setup.py文件中添加的那句話:
library_dirs=['C:\Program Files (x86)\Graphviz2.38\lib\release\lib']
可是,修改setup.py文件時添加的include_dirs=['C:\Program Files (x86)\Graphviz2.38\include']
確實生效了。
爲何在setup.py中添加library_dirs以後,再調用VC的連接程序,並無向指定庫文件路徑下尋找cdt.lib呢?
這是問題的根本所在。啓動調試進程,調試setup.py文件。在spyder的console端鍵入以下:
debugfile('H:/pygraphviz-master-wrong2/setup.py', args='install', wdir='H:/pygraphviz-master-wrong2')
跟蹤上圖中setup函數中的執行,尤爲是對ext_modules的數據處理。跟蹤這個數據處理,就能找到哪裏引用了include_dirs,哪裏引用了library_dirs。
數據就是通道
咱們須要找到輸出異常的位置,離它越近越好,就像逼近真相。
查看出錯時的輸出信息,有以下最關鍵的地方:
而後跟蹤程序執行,會在python的系統文件dist.py中有run_commands的函數
這裏面就有running %s的輸出。那麼我相信關鍵進程就在cmd_obj.run()中。正是這個執行過程,裏面出錯。
因此,log.info處下斷點,當輸出running buid_ext以後,進入run的函數內部:run內部又會跟進到了build_ext.py文件的核心函數run中:
裏面會對compiler編譯器進行設置,一直到執行self.build_extensions函數。
跟入該函數,
而後,在console端調試:
這裏面的extension的命名「pygraphviz._graphviz」和setup.py文件中指定的命名是一致的。
也就是說,到目前爲止:
已經找到了對setup.py文件中extension進行處理的核心代碼,繼續跟蹤就會知道library_dirs爲何會失效。
跟蹤進入cython_sources函數,就會看到:sources在第一個for循環中正是extension除去名稱以後的第一行。
咱們在cython_sources中下斷點,直到sources是library-dirs的那一行。
惋惜,直接一次循環就報出了本文所出現的link.exe連接的錯誤。
因而,直接跟入build_extension函數,就第一次循環就跟進去:以下,
咱們輸出能夠看到:
咱們跟進compile函數,確實沒有給library_dirs進行賦值的選項。
繼續跟,跟完compile之後,發現compile只是進行了編譯操做。
會輸出:
ipdb> C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\BIN\x86_amd64\cl.exe /c /nologo /Ox /W3 /GL /DNDEBUG /MD "-IC:\Program Files (x86)\Graphviz2.38\include" -IC:\ProgramData\Anaconda3\include -IC:\ProgramData\Anaconda3\include "-IC:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\INCLUDE" "-IC:\Program Files (x86)\Windows Kits\10\include\10.0.10240.0\ucrt" "-IC:\Program Files (x86)\Windows Kits\8.1\include\shared" "-IC:\Program Files (x86)\Windows Kits\8.1\include\um" "-IC:\Program Files (x86)\Windows Kits\8.1\include\winrt" /Tcpygraphviz/graphviz_wrap.c /Fobuild\temp.win-amd64-3.6\Release\pygraphviz/graphviz_wrap.obj
如今,繼續在build_extension中跟入:
這個就是連接過程。也就是在python程序中調用c編譯器編譯c目標程序時,會執行的函數。
咱們跟進去。
ipdb> print (ext.library_dirs)
['C:\\Program Files (x86)\\Graphviz2.38\\lib\release\\lib']
這也說明,確確實實是傳入進去了。咱們進入這個函數:
一直跟到裏面的一個link函數時,咱們發現有問題!
也就是,傳入的library_dirs已經變成了
C:\Program Files (x86)\Graphviz2.38\lib
elease\lib
這是什麼鬼!!!
裏面有一個_fix_lib_args的操做,可是能夠看到:
lib路徑已經錯了。原本應該是lib\\release\lib的,卻變成了libelease\lib了。
而後一直進入到:
self.spawn([self.linker] + ld_args)
咱們能夠輸出以下:
結果這裏面輸出的ld_args居然是:
C:\\Program Files (x86)\\Graphviz2.38\\lib\release\\lib'
千萬不要覺得是正確的地址。正確的是C:\\Program Files (x86)\\Graphviz2.38\\lib\\release\\lib。
咱們來觀察一下:
ld_args = (ldflags + lib_opts + export_opts + objects + ['/OUT:' + output_filename])
而後ld_args是:
猛地一看,仍是錯誤的。爲何在console端用print(ld_args )輸出的是:
C:\\Program Files (x86)\\Graphviz2.38\\lib\release\\lib'
這是由於,你沒有雙擊開。你雙擊開看到的是:
看到了嗎?這是windows下的換行符號\r。在控制檯輸出的時候變成了\r。
在spyder中查看的時候,是換行。
而實際上:
目前爲止,真正傳入的就是這麼個玩意:
/LIBPATH:C:\Program Files (x86)\Graphviz2.38\lib
elease\lib
也就是說,是換行符號\r。
咱們雙擊點開libopts也是同樣的。libopts是構成ld_args的重要組成。因此,後面的就不用跟了。
link程序必然出錯。由於找不到cdt.lib文件。因此,link.exe程序必然失敗,報出1181錯誤。
如今已經知道怎麼改了,就是把setup.py中路徑的反斜槓,所有改成斜槓。可是我不能容忍不知道爲何傳遞的時候出錯。
咱們會發現是在調用link_shared_object的時候出的錯誤:
此時調試輸出的是:
當跟入函數之後:
因此,錯誤就是ext.library_dirs生成的過程出錯。
那麼就要追蹤ext.library_dirs是如何依據setup.py文件中的extension項目生成的。
因此,關鍵是找到調用build_extension函數的地方,而且看是誰把值傳入了ext。
如今開始倒推:
def build_extensions(self): # First, sanity-check the 'extensions' list self.check_extensions_list(self.extensions) for ext in self.extensions: ext.sources = self.cython_sources(ext.sources, ext) self.build_extension(ext)
在進入這個函數的時候,輸出仍然是:
ipdb> print (ext.library_dirs) ['C:\\Program Files (x86)\\Graphviz2.38\\lib\release\\lib']
因此,錯誤就在於生成self.extensions的過程當中就已經註定了。
那麼何時生成self.extensions的呢?
咱們進入check_extensions_list函數,仍舊是輸出的是錯誤的地址。
因此,錯誤的造成不是在build_extensions中。
而是在調用build_extensions以前,而後生成了self.extensions,裏面包含了'扭曲"的地址。
那麼就是build_ext類的問題了,由於它就是那個self。
咱們經過觀察build_ext的類結構,和快速的掃描代碼,找到了。
它的finalize_options函數中對self.extensions進行了設置。
這個時候,剛剛執行完下面代碼:
self.extensions = self.distribution.ext_modules
咱們就在console端鍵入:
能夠看出,已經錯了。因此,錯誤的造成不是在self.extensions的生成中。
而是在self.distribution.ext_modules的生成中。因而,進一步倒推。
在build_ext中,並不能找到self.distribution的相關代碼。咱們發現,build_ext繼承的是Command類。Command類中有self.distribution。
因此,咱們就要關注於build_ext這個類對象的生成。
這個時候,就要重頭開始調試,看哪一個地方生成了build_ext這個類的對象引用。
log.info("running %s", command) cmd_obj = self.get_command_obj(command) cmd_obj.ensure_finalized() cmd_obj.run()
確定是在上面的三行代碼中,完成了對build_ext類的生成,在這裏完成了對self.distribution的操做。
首先跟入get_command_obj函數,感受這個是最有可能對Command子類build_ext類對象的生成過程。由於從名字來看就是get_command_obj,而且註釋是:
"""Return the command object for 'command'. Normally this object is cached on a previous call to 'get_command_obj()'; if no command object for 'command' is in the cache, then we either create and return it (if 'create' is true) or return None. """
執行完第一行程序:
cmd_obj = self.command_obj.get(command)
因爲以前是有:
self.extensions = self.distribution.ext_modules
因此咱們直接在調試console輸入:
結果發現:名稱已經出錯了!!!
那麼問題確定是在self.command_obj.get中了!!!它在生成build_ext類對象的時候,初始化父類Command的distribution成員時,就已經把地址」扭曲「了!!!
最遺憾的是!!!這個self.command_obj.get壓根跟入不進去。頗有可能代碼不是開源的,只是做爲功能提供在連接庫當中。因此,這是一個bug。
什麼bug???
就是在setup.py文件中的extension中寫入library_dirs的時候,若是傳入以下的地址:
library_dirs=['C:\Program Files (x86)\Graphviz2.38\lib\release\lib'],
那麼出於某種緣由,python系統,會將\r看做換行符。所以,在轉變的過程當中:
原本是要將\處理成\\的,可是這個\r被遺漏了。因而就產生了以下「扭曲」的地址
C:\\Program Files (x86)\\Graphviz2.38\\lib\release\\lib
最後,python中編譯(setup.py文件中extension指定)的c程序,就會出現找不到庫的問題。
因而乎,報告這個bug的同時,建議所有用斜槓/。
回覆:
msg3530 (view) Author: berker.peksag Date: 2018-08-20.16:26:32
remove
> In windows, we always give a path using '\' and python 3 can correctly dispose
> it just as we using '/' in Linux. But if you offer
> a path in windows with a '\' followed as 'r'. Everyting will goes wrong.
You need to use raw strings to avoid this. Replace
"C:\Program Files (x86)\Graphviz2.38\lib\release\lib"
with
r"C:\Program Files (x86)\Graphviz2.38\lib\release\lib"
See https://blog.lerner.co.il/avoiding-windows-backslash-problems-with-pythons-raw-strings/ for more details about raw strings.
This tracker is for issues with bugs.python.org. Please use Stack Overflow or python-list to ask usage questions.
老外不認可是bug。說,windows反斜槓的處理,要加r。嗯嗯。就這樣吧。
解決方案見網址:
https://github.com/pygraphviz/pygraphviz/issues/58
這裏面說用64位lib下的庫文件覆蓋到目錄lib下便可。也就是說這些unresolved是缺失了一些庫。能夠看到是生成_graphviz.cp36_win_amd64.lib的時候出錯。這是由於安裝的graphviz是32位的模塊,缺失了不少庫文件。 這個庫,會放在本文附件裏。 接着問題就能排除。
注意是將我附件中的\GraphViz_x64-master\graphviz-2.38_x64\lib放置到: graphviz的msi安裝程序以後的release的lib下面,而不是直接的lib下面C:\Program Files (x86)\Graphviz2.38\lib\release\lib。 可是,僅僅覆蓋lib文件時不夠的。不然在後續的測試程序中,import pygraphviz的時候回報錯提示,dll win32位的有問題。 因此,除了lib路徑須要特殊處理之外,須要把附件中GraphViz_x64-master\graphviz-2.38_x64中的 全部的內容所有覆蓋到C:\Program Files (x86)\Graphviz2.38中去。(因此,你直接換個名字吧。具體見文末的總結)
仍然是下面的這個網址:
https://github.com/pygraphviz/pygraphviz/issues/58
裏面提到:(在網絡海量信息中去僞存真)
最後在以下這個網址:
https://github.com/pygraphviz/pygraphviz/issues/74#issuecomment-238323405
中找到:
點擊進去(https://github.com/Kagami/pygraphviz/commit/fe442dc16accb629c3feaf157af75f67ccabbd6e)
就是一個補丁文件:
按照補丁文件對graphviz.i和pygraphviz/graphviz_wrap.c進行修改(我一行一行對着補丁改的。。。應該有依據補丁的自動化修改工具)。修改後的文件見附件。
import pygraphviz as pgv A=pgv.AGraph() A.add_edge(1,2) A.add_edge(2,3) A.add_edge(1,3) print(A.string()) # print to screen print("Wrote simple.dot") A.write('simple.dot') # write to simple.dot B=pgv.AGraph('simple.dot') # create a new graph from file B.layout() # layout with default (neato) B.draw('simple.png') # draw png print("Wrote simple.png")
上面是測試程序。經過蒐集資料可知,是由於neato的問題。雙擊C:\Program Files (x86)\Graphviz2.38\bin下的neato,會報出異常。
如何解決,見文末總結。
連接:https://pan.baidu.com/s/18VKkVj_CupmvFwihdHwH8Q 密碼:aqr7