某次在查看測試機(Ubuntu)發行版本時,發現獲得的結果並不許確;本應獲得Ubuntu
,結果顯示的倒是Debian
,大體代碼以下node
... distribution_name = ['centos', 'ubuntu', 'redhat', 'debian', 'fedora'] class_name = {'centos': 'CentOS', 'ubuntu': 'Ubuntu', 'redhat': 'RedHat', 'debian': 'Debian', 'fedora': 'Fedora'} ... for name in distribution_name: if name in platform.platform.lower(): _platform = class_name[name] break ...
項目使用的是可移植版的python
,第一反應是用交互模式驗證一下。python
>>> import platform >>> platform.platform() 'Linux-4.4.0-62-generic-x86_64-with-debian-stretch-sid'
得出的結果確實爲debian
。既沒有報錯,也沒有異常,那麼問題是出在哪裏了呢?linux
遂又使用系統自帶的python
驗證。ubuntu
>>> import platform >>> platform.platform() 'Linux-4.4.0-62-generic-x86_64-with-Ubuntu-16.04-xenial'
然而系統獲得的倒是正確的結果,難道是移植版本的bug?
在同事的提醒下,意識到應該看一下platform
模塊的源碼,看看問題是否出在這裏。centos
首先,查看platform
模塊中的platform
方法緩存
def platform(aliased=0, terse=0): result = _platform)_cache.get((aliased, terse), None) if result is not None: return resut system, node, release, version, machine, processor = uname() ... elif system in ('Linux', ): disname, distversion, distid = dist('') ... def uname(): ... try: system, node, release, version, machine = os.uname() ... def dist(distname='', version='', id='', supported_dists=_supported_dists): return linux_distribution(distname, version, id, supported_dists=supported_dists, full_distribution_name=0)
當調用platform
方法時,首先它回去模塊緩存信息中查找,如有則直接返回。由於是第一次調用,緩存中確定不會存有相應信息,這裏能夠跳過。
接着,經過uname
方法獲取system
, node
, release
等信息,而uname
方法主要是調用os.uname()
得到相應信息。ide
>>> import os >>> os.uname() ('Linux', 'uyun-VirtualBox', '4.4.0-62-generic', '#83-Ubuntu SMP Wed Jan 18 14:10:15 UTC 2017', 'x86_64')
嘗試使用os.uname()
後,除了能獲得系統版本外發現並無指望獲得的相應發行版信息,跳過。
而後則是dist()
方法。發現dist()
方法實際上調用的python_implementation()
。最終肯定,獲取系統版本的關鍵就在python_implementation()
方法中。測試
如下爲比較可移植版的python
和Ubuntu
自帶的python
源碼(具體行號可能存在些許誤差)code
... 259 _supported_dists = ( 260 'SuSE', 'debian', 'fedora', 'redhat', 'centos', 261 'mandrake', 'mandriva', 'rocks', 'slackware', 'yellowdog', 'gentoo', 262 'UnitedLinux', 'turbolinux') ... ... 291 def linux_distribution(distname='', version='', id='', 292 supported_dists=_supported_dists, 293 full_distribution_name=1): ... 315 try: 316 etc = os.listdir('/etc') 317 except os.error: 318 # Probably not a Unix system 319 return distname, version, id ... ...
以上爲移植版python
的platform
模塊源碼。orm
··· 261 _supported_dists = ( 262 'SuSE', 'debian', 'fedora', 'redhat', 'centos', 263 'mandrake', 'mandriva', 'rocks', 'slackware', 'yellowdog', 'gentoo', 264 'UnitedLinux', 'turbolinux', 'Ubuntu') ... 293 _distributor_id_file_re = re.compile("(?:DISTRIB_ID\s*=)\s*(.*)", re.I) 294 _release_file_re = re.compile("(?:DISTRIB_RELEASE\s*=)\s*(.*)", re.I) 295 _codename_file_re = re.compile("(?:DISTRIB_CODENAME\s*=)\s*(.*)", re.I) 296 297 def linux_distribution(distname='', version='', id='', 298 supported_dists=_supported_dists, 299 full_distribution_name=1): ... 321 # check for the LSB /etc/lsb-release file first, needed so 322 # that the distribution doesn't get identified as Debian. 323 try: 324 with open("/etc/lsb-release", "rU") as etclsbrel: 325 for line in etclsbrel: 326 m = _distributor_id_file_re.search(line) 327 if m: 328 _u_distname = m.group(1).strip() 329 m = _release_file_re.search(line) 330 if m: 331 _u_version = m.group(1).strip() 332 m = _codename_file_re.search(line) 333 if m: 334 _u_id = m.group(1).strip() 335 if _u_distname and _u_version: 336 return (_u_distname, _u_version, _u_id) 337 except (EnvironmentError, UnboundLocalError): 338 pass 339 340 try: 341 etc = os.listdir('/etc') 342 except os.error: 343 # Probably not a Unix system 344 return distname,version,id ... ...
以上爲Ubuntu
系統自帶python
的platform
模塊源碼。
首先能夠看到,Ubuntu
版本中platform
的_supported_dists
元組中多了一個Ubuntu
元素,而且在linux_destribution
方法中,首先會嘗試讀取/etc/lsb-release
文件;接着經過正則匹配(_distributor_id_file_re
, _release_file_re
, _codename_file_re
),查找相應的值,若是都有結果,則直接返回。
讀取/etc/lsb-release
,發現裏面存了一些Ubuntu
系統版本信息。
DISTRIB_ID=Ubuntu DISTRIB_RELEASE=16.04 DISTRIB_CODENAME=xenial DISTRIB_DESCRIPTION="Ubuntu 16.04 LTS"
那麼顯然,三個正則都將匹配到對應的值,返回(Ubuntu, 16.04, xenial)
。
最終,正確的獲取到Ubuntu
發行版本。