cpython/Lib/ctypes/util.py ctypes源码解析

import subprocess
import sys

# find_library(name) returns the pathname of a library, or None.
if os.name == "nt":

    def _get_build_version():
        """Return the version of MSVC that was used to build Python.

        For Python 2.3 and up, the version number is included in
        sys.version.  For earlier versions, assume the compiler is MSVC 6.
        """
        # This function was copied from Lib/distutils/msvccompiler.py
        prefix = "MSC v."
        i = sys.version.find(prefix)
        if i == -1:
            return 6
        i = i + len(prefix)
        s, rest = sys.version[i:].split(" ", 1)
        majorVersion = int(s[:-2]) - 6
        if majorVersion >= 13:
            majorVersion += 1
        minorVersion = int(s[2:3]) / 10.0
        # I don't think paths are affected by minor version in version 6
        if majorVersion == 6:
            minorVersion = 0
        if majorVersion >= 6:
            return majorVersion + minorVersion
        # else we don't know what version of the compiler this is
        return None

    def find_msvcrt():
        """Return the name of the VC runtime dll"""
        version = _get_build_version()
        if version is None:
            # better be safe than sorry
            return None
        if version <= 6:
            clibname = 'msvcrt'
        elif version <= 13:
            clibname = 'msvcr%d' % (version * 10)
        else:
            # CRT is no longer directly loadable. See issue23606 for the
            # discussion about alternative approaches.
            return None

        # If python was built with in debug mode
        import importlib.machinery
        if '_d.pyd' in importlib.machinery.EXTENSION_SUFFIXES:
            clibname += 'd'
        return clibname+'.dll'

    def find_library(name):
        if name in ('c', 'm'):
            return find_msvcrt()
        # See MSDN for the REAL search order.
        for directory in os.environ['PATH'].split(os.pathsep):
            fname = os.path.join(directory, name)
            if os.path.isfile(fname):
                return fname
            if fname.lower().endswith(".dll"):
                continue
            fname = fname + ".dll"
            if os.path.isfile(fname):
                return fname
        return None
  1. cpython
  2. /Lib
  3. /ctypes

/util.py

下面是对你提供的 ctypes 相关源码的逐行解释,主要针对 Windows 平台(os.name == "nt")的动态库查找逻辑:


前置导入

 

python

复制编辑

import subprocess import sys

  • 导入 subprocess sys 模块,尽管 subprocess 在这段代码中没有用到,可能是复制时带上了。


判断操作系统是否为 Windows

 

python

复制编辑

if os.name == "nt":

  • 判断是否是 Windows 系统(os.name == "nt"),只有在 Windows 上才会执行下面定义的函数。


_get_build_version 函数:获取构建 Python 的 MSVC(微软编译器)版本

 

python

复制编辑

def _get_build_version():

  • 返回当前 Python 所使用的 MSVC 编译器的大致版本号。

 

python

复制编辑

prefix = "MSC v." i = sys.version.find(prefix)

  • sys.version 字符串中查找 "MSC v.",这是 MSVC 编译器的标志。

  • 例如,sys.version 中可能包含:MSC v.1929 64 bit (AMD64)

 

python

复制编辑

if i == -1: return 6

  • 如果找不到,说明是非常早期的版本,默认认为是 MSVC 6。

 

python

复制编辑

i = i + len(prefix) s, rest = sys.version[i:].split(" ", 1)

  • 提取编译器版本号部分,例如从 "1929 64 bit (AMD64)" 中取出 "1929"。

 

python

复制编辑

majorVersion = int(s[:-2]) - 6

  • s[:-2] 获取主版本号(例如 "19"),并减去 6 得到一个映射值。

 

python

复制编辑

if majorVersion >= 13: majorVersion += 1

  • 特殊处理:如果主版本 >= 13,加 1。目的是匹配不同 MSVC 版本号的行为。

 

python

复制编辑

minorVersion = int(s[2:3]) / 10.0

  • 提取次版本号并除以 10,例如从 "1929" 中提取 "2",变为 0.2。

 

python

复制编辑

if majorVersion == 6: minorVersion = 0

  • 如果主版本是 6,忽略次版本,直接设为 0。

 

python

复制编辑

if majorVersion >= 6: return majorVersion + minorVersion

  • 返回完整的版本号,例如 19.2

 

python

复制编辑

return None

  • 如果判断失败,返回 None。


find_msvcrt 函数:获取 MSVC 运行库对应的 DLL 名

 

python

复制编辑

def find_msvcrt():

  • 返回 VC 运行库(C runtime)的 DLL 名字,比如 msvcr100.dll

 

python

复制编辑

version = _get_build_version() if version is None: return None

  • 如果无法判断版本,返回 None。

 

python

复制编辑

if version <= 6: clibname = 'msvcrt' elif version <= 13: clibname = 'msvcr%d' % (version * 10)

  • 根据 MSVC 版本号确定运行库名字:

    • MSVC6:msvcrt.dll

    • MSVC7~13:msvcr70.dll ~ msvcr130.dll

 

python

复制编辑

else: return None

  • 对于更新的 MSVC(比如 MSVC2015+),运行库不再以这种方式存在,直接返回 None。

 

python

复制编辑

import importlib.machinery if '_d.pyd' in importlib.machinery.EXTENSION_SUFFIXES: clibname += 'd'

  • 如果是 Python debug 版本(扩展名带 _d.pyd),附加 d,即 msvcr100d.dll

 

python

复制编辑

return clibname+'.dll'

  • 最终拼接 .dll 后缀,返回如 "msvcr100.dll"


find_library 函数:核心函数,用于查找库路径

 

python

复制编辑

def find_library(name):

  • 根据名字查找动态库的路径。

 

python

复制编辑

if name in ('c', 'm'): return find_msvcrt()

  • libclibm(数学库)在 Windows 上都映射到 msvcrt 系列。

 

python

复制编辑

for directory in os.environ['PATH'].split(os.pathsep):

  • 遍历系统的环境变量 PATH 中所有目录。

 

python

复制编辑

fname = os.path.join(directory, name) if os.path.isfile(fname): return fname

  • 如果找到了同名文件(如 "sqlite3"),直接返回。

 

python

复制编辑

if fname.lower().endswith(".dll"): continue fname = fname + ".dll" if os.path.isfile(fname): return fname

  • 如果未带 .dll,尝试加上 .dll 后缀再找一次。

 

python

复制编辑

return None

  • 如果找不到,就返回 None


总结作用

这段代码的作用是:

  1. 判断 Python 当前使用的 MSVC 版本(Windows 特有)。

  2. 根据该版本确定运行时 DLL(如 msvcrt.dll)。

  3. 通过 PATH 环境变量遍历查找指定库的实际路径,主要用于 ctypes 载入库时定位动态链接库。


如果你还想了解这段代码是如何集成进 ctypes.util.find_library() 的全局逻辑或其在 POSIX 系统上的 counterpart,也可以继续展开。需要我补充吗?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

强化学习曾小健

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值