• Python之路--你不知道的platform


    某次在查看测试机(Ubuntu)发行版本时,发现得到的结果并不准确;本应得到Ubuntu,结果显示的却是Debian,大致代码如下

    ...
    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,第一反应是用交互模式验证一下。

    >>> import platform
    >>> platform.platform()
    'Linux-4.4.0-62-generic-x86_64-with-debian-stretch-sid'
    

    得出的结果确实为debian。既没有报错,也没有异常,那么问题是出在哪里了呢?

    遂又使用系统自带的python验证。

    >>> import platform
    >>> platform.platform()
    'Linux-4.4.0-62-generic-x86_64-with-Ubuntu-16.04-xenial'
    

    然而系统得到的却是正确的结果,难道是移植版本的bug?
    在同事的提醒下,意识到应该看一下platform模块的源码,看看问题是否出在这里。

    首先,查看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()获得相应信息。

    >>> 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源码(具体行号可能存在些许偏差)

    ...
    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模块源码。

    ···
    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_IDs*=)s*(.*)", re.I)
    294 _release_file_re = re.compile("(?:DISTRIB_RELEASEs*=)s*(.*)", re.I)
    295 _codename_file_re = re.compile("(?:DISTRIB_CODENAMEs*=)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发行版本。

  • 相关阅读:
    20145223《Java程序程序设计》课程总结
    20145223《Java程序程序设计》第10周学习总结
    20145223《Java程序程序设计》实验报告5
    20145223《Java程序程序设计》第9周学习总结
    20145223 《Java程序程序设计》实验报告4
    20145223《Java程序程序设计》第8周学习总结
    20145223《Java程序设计》实验报告3
    20145223《Java程序程序设计》第7周学习总结
    20145223《Java程序程序设计》实验报告二
    node_promise
  • 原文地址:https://www.cnblogs.com/agnewee/p/6444836.html
Copyright © 2020-2023  润新知