Python之路--你不知道的platform

某次在查看測試機(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()方法中。測試

如下爲比較可移植版的pythonUbuntu自帶的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
        ...
...

以上爲移植版pythonplatform模塊源碼。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系統自帶pythonplatform模塊源碼。

首先能夠看到,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發行版本。

相關文章
相關標籤/搜索