【问题标题】:Determining if a given Python module is a built-in module确定给定的 Python 模块是否为内置模块
【发布时间】:2011-06-22 19:01:00
【问题描述】:

我正在对各种模块进行一些解析和自省,但我不想解析内置模块。现在,内置模块没有像types.BuiltinFunctionType这样的特殊类型,那么我该怎么做呢?

>>> import CornedBeef
>>> CornedBeef
<module 'CornedBeef' from '/meatish/CornedBeef.pyc'>
>>> CornedBeef.__file__
'/meatish/CornedBeef.pyc'
>>> del CornedBeef.__file__
>>> CornedBeef
<module 'CornedBeef' (built-in)>

根据 Python,如果一个模块没有 __file__ 属性,它显然是内置的。这是否意味着hasattr(SomeModule, '__file__') 是检查模块是否内置的方法?当然,del SomeModule.__file__ 并不完全常见,但是有没有更可靠的方法来确定模块是否是内置的?

【问题讨论】:

    标签: python module internals python-internals


    【解决方案1】:

    当您说“内置”时,您是指用 C 语言编写的,还是标准库的一部分?如果您的意思是第一个,那么寻找__file__ 是正确的做法。如您所见,即使是 Python 解释器也使用 __file__ 的存在作为内置指标。

    如果您的意思是“标准库的一部分”,那么很难确定。

    【讨论】:

      【解决方案2】:

      您可以使用imp.is_builtin 来查看模块名称是否与内置模块匹配,但我想不出任何可靠地实际自省模块对象的方法。

      您还可以尝试以下方法:

      >>> import imp
      >>> f, path, desc = imp.find_module("sys")
      >>> desc
      ('', '', 6)
      >>> desc[2] == imp.C_BUILTIN
      True
      

      【讨论】:

        【解决方案3】:

        sys.builtin_module_names

        一个字符串元组给出的名称 编译成的所有模块 这个 Python 解释器。 (这 信息不可用在任何 其他方式 - modules.keys() 只列出 导入的模块。)

        【讨论】:

        • builtin 有多个含义:1) 与 python 解释器一起提供 2) 编译到 cpython 解释器中。更有用的意义是1,但这个答案只适用于2。
        • @bukzor:不要混淆“builtin”和“stdlib”模块。显然sys.builtin_module_names 不是 CPython 特定的。
        【解决方案4】:

        如果您认为它只是被问到builtins,那么接受的答案显然是正确的。

        就我而言,我也在寻找标准库,我的意思是一个给定 Python 发行版附带的所有可导入模块的列表。已多次提出有关此问题的问题,但我找不到包含我正在寻找的所有内容的答案。

        我的用例是在 Python import x 语句中将任意 x 存储为:

        • 包含在 Python 标准库 + 内置程序中
        • 作为第三方模块安装
        • 都没有

        这适用于 virtualenvs 或全局安装。它查询运行脚本的任何 python 二进制文件的分布。最后一个块确实超出了 virtualenv,但我认为这是所需的行为。

        # You may need to use setuptools.distutils depending on Python distribution (from setuptools import distutils)
        import distutils
        import glob
        import os
        import pkgutil
        import sys    
        
        def get_python_library():
        
            # Get list of the loaded source modules on sys.path.
            modules = { 
                module
                for _, module, package in list(pkgutil.iter_modules())
                if package is False
            }
        
            # Glob all the 'top_level.txt' files installed under site-packages.
            site_packages = glob.iglob(os.path.join(os.path.dirname(os.__file__) 
                            + '/site-packages', '*-info', 'top_level.txt'))
        
            # Read the files for the import names and remove them from the modules list.
            modules -= {open(txt).read().strip() for txt in site_packages}
        
            # Get the system packages.
            system_modules = set(sys.builtin_module_names)
        
            # Get the just the top-level packages from the python install.
            python_root = distutils.sysconfig.get_python_lib(standard_lib=True)
            _, top_level_libs, _ = list(os.walk(python_root))[0]
        
            return sorted(top_level_libs + list(modules | system_modules))
        

        退货

        导入的排序列表:[..., 'imaplib', 'imghdr', 'imp', 'importlib', 'imputil', 'inspect', 'io', ...]

        解释

        我把它分成几块,这样每个组需要的原因就很清楚了。

        • modules

          • pkgutil.iter_modules 调用扫描 sys.path 上所有已加载的模块并返回 (module_loader, name, ispkg) 元组的生成器。
          • 我把它变成一个集合并过滤掉包,因为这里我们只关心源模块。
        • site_packages

          • 获取常规站点包目录下所有已安装包的列表,并将它们从modules 列表中删除。这大致对应于第三方部门。
          • 这是最难做到的部分。许多事情几乎起作用了,比如pip.get_installed_distributionssite。但是pip 会返回 PyPi 上的模块名称,而不是导入源文件时的名称。某些病态包裹会从裂缝中溜走,例如:
            • requests-futures 导入为requests_futures
            • colors,实际上是 PyPi 上的 ansicolors,因此会混淆任何合理的启发式算法。
          • 我确信某些低使用率模块在其包中不包含top_level.txt。但这涵盖了我 100% 的用例,似乎适用于正确配置的所有内容。
        • system_modules

          • 如果您不明确要求它们,您将不会获得这些系统模块,例如 sysgcerrno 和其他一些 optional modules
        • top_level_libs

          • distutils.sysconfig.get_python_lib(standard_lib=True) 调用返回平台无关标准库的顶层目录。
          • 这些很容易被忽略,因为它们可能与其他模块不在同一 python 路径下。如果你在 OSX 上运行 virtualenv,这些模块实际上是从系统安装中导入的。这些模块包括emailloggingxml 等等。

        结论

        对于我的 2013 款 MacBookPro,我找到了 403 个模块用于 python2.7 安装。

           >>> print(sys.version)
           2.7.10 (default, Jul 13 2015, 12:05:58)
           [GCC 4.2.1 Compatible Apple LLVM 6.1.0 (clang-602.0.53)]
           >>> print(sys.hexversion)
           34015984
           >>> python_stdlib = get_python_libirary()
           >>> len(python_stdlib)
           403
        

        我提出了the code and output 的要点。如果您认为我缺少课程或包含了虚假模块,我想听听。

        * 替代品

        • 在写这篇文章时,我挖掘了 pipsetuptools API。这些信息有可能通过单个模块获得,但您确实需要了解该 API 的使用方式。

        • 在我开始之前,有人告诉我six 有专门针对这个问题的功能。可能存在但我自己找不到它是有道理的。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2013-09-14
          • 1970-01-01
          • 1970-01-01
          • 2013-04-18
          • 1970-01-01
          • 2010-10-20
          • 1970-01-01
          相关资源
          最近更新 更多