某次在查看测试机(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_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发行版本。

Python之路--你不知道的platform的更多相关文章

  1. Python之路,Day17 - 分分钟做个BBS论坛

    Python之路,Day17 - 分分钟做个BBS论坛   本节内容: 项目:开发一个简单的BBS论坛 需求: 整体参考"抽屉新热榜" + "虎嗅网" 实现不同 ...

  2. Python之路,Day8 - Socket编程进阶

    Python之路,Day8 - Socket编程进阶   本节内容: Socket语法及相关 SocketServer实现多并发 Socket语法及相关 socket概念 socket本质上就是在2台 ...

  3. Python之路【第一篇】python基础

    一.python开发 1.开发: 1)高级语言:python .Java .PHP. C#  Go ruby  c++  ===>字节码 2)低级语言:c .汇编 2.语言之间的对比: 1)py ...

  4. Python之路

    Python学习之路 第一天   Python之路,Day1 - Python基础1介绍.基本语法.流程控制              第一天作业第二天   Python之路,Day2 - Pytho ...

  5. python之路 目录

    目录 python python_基础总结1 python由来 字符编码 注释 pyc文件 python变量 导入模块 获取用户输入 流程控制if while python 基础2 编码转换 pych ...

  6. Python之路【第十九篇】:爬虫

    Python之路[第十九篇]:爬虫   网络爬虫(又被称为网页蜘蛛,网络机器人,在FOAF社区中间,更经常的称为网页追逐者),是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本.另外一些不常使用 ...

  7. Python之路【第十八篇】:Web框架们

    Python之路[第十八篇]:Web框架们   Python的WEB框架 Bottle Bottle是一个快速.简洁.轻量级的基于WSIG的微型Web框架,此框架只由一个 .py 文件,除了Pytho ...

  8. Python之路【第十七篇】:Django【进阶篇 】

    Python之路[第十七篇]:Django[进阶篇 ]   Model 到目前为止,当我们的程序涉及到数据库相关操作时,我们一般都会这么搞: 创建数据库,设计表结构和字段 使用 MySQLdb 来连接 ...

  9. Python之路【第十六篇】:Django【基础篇】

    Python之路[第十六篇]:Django[基础篇]   Python的WEB框架有Django.Tornado.Flask 等多种,Django相较与其他WEB框架其优势为:大而全,框架本身集成了O ...

随机推荐

  1. PHP利用数组构造JSON

    问题起因 以往都是直接用构造数组的形式构造json 例子: $arr = array("A"=>"1","B"=>"2 ...

  2. 可用于Windows Server 2008 R2的Xbox One手柄、接收器驱动

    让客厅里的Gen8可以玩FC和PS1游戏,折腾了半天,终于将Xbox One手柄驱动弄好: http://www.drvsky.com/Microsoft/Xbox_One.htm http://ww ...

  3. 【java基础】内部类,局部内部类,匿名内部类、静态内部类、接口中的内部类

    内部类: 1.定义在一个类中的内部类,内部类的实例化伴随着外围类所定义的方法来构造,内部类对象有外围类的隐式引用,所以内部类可以直接访问外围类的外围类的域,包括私有的,这是内部类的访问特权,所以比常规 ...

  4. Struts2标签遍历List<Map<String,String>>

    <s:if test="resultList != null && resultList.size() > 0"> <s:iterator  ...

  5. apue chapter 4 文件和目录

    1.文件信息结构体 struct stat{ mode_t st_mode; //file type and permissions ino_t st_ino; //i-node number (se ...

  6. Bmob 之 简单使用

    1. pod pod 'BmobSDK' 与 pod "BmobSDK" 好像没什么区别 2. 导入 在AppDelegate中: #import <BmobSDK/Bmob ...

  7. Java语言与C语言之间的应用比较

    http://book.51cto.com/art/200906/131809.htm C语言能干的Java也能干的如下: 网络应用层协议服务程序开发:如WebServer.FTPServer.Mai ...

  8. 与文件上传到的三个类:FileItem类、ServletFileUpload 类、DiskFileItemFactory类

    文件上传: ServletFileUpload负责处理上传的文件数据,并将表单中每个输入项封装成一个FileItem对象中, 在使用ServletFileUpload对象解析请求时需要根据DiskFi ...

  9. Windows7启用超级管理员administrator账户的多种方法

    开启win7家庭普通版的超级管理员账户Administrotor WIN7家庭普通版没有"本地用户和组"项,不能通过"用计算机右键-管理"的方法开启超级管理员帐 ...

  10. thinkPHP 模板中的语法

    一.导入CSS和JS文件   1.css link       js  scr        <link rel='stylesheet' type='text/css' href='__PUB ...