将任意命名的文件作为 Python 模块导入,无需生成字节码文件 [英] Import arbitrary-named file as a Python module, without generating bytecode file

查看:24
本文介绍了将任意命名的文件作为 Python 模块导入,无需生成字节码文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Python 程序如何轻松从任意名称的文件中导入 Python 模块?

How can a Python program easily import a Python module from a file with an arbitrary name?

标准库导入机制似乎没有帮助.一个重要的限制是我不希望随附的字节码文件出现;如果我在名为 foo 的源文件上使用 imp.load_module,则会出现名为 fooc 的文件,这很混乱.

The standard library import mechanism doesn't seem to help. An important constraint is that I don't want an accompanying bytecode file to appear; if I use imp.load_module on a source file named foo, a file named fooc appears, which is messy and confusing.

Python 导入机制期望它最了解文件名:模块文件位于特定的文件系统位置,特别是文件名具有特定的后缀(foo.py 对于 Python 源代码)代码等),没有其他.

The Python import mechanism expects that it knows best what the filename will be: module files are found at specific filesystem locations, and in particular that the filenames have particular suffixes (foo.py for Python source code, etc.) and no others.

这与另一个约定冲突,至少在 Unix 上:将作为命令执行的文件应该是 命名不参考实现语言.例如,执行foo"的命令应该在名为 foo 的程序文件中,没有后缀.

That clashes with another convention, at least on Unix: files which will be executed as a command should be named without reference to the implementation language. The command to do "foo", for example, should be in a program file named foo with no suffix.

不过,对这样的程序文件进行单元测试需要导入该文件.我需要程序文件中的对象作为 Python 模块对象,准备在单元测试用例中进行操作,正如 import 会给我的那样.

Unit-testing such a program file, though, requires importing that file. I need the objects from the program file as a Python module object, ready for manipulation in the unit test cases, exactly as import would give me.

Pythonic 导入模块的方式是什么,尤其是从名称不以 .py 结尾的文件中,没有出现字节码文件 用于导入?

What is the Pythonic way to import a module, especially from a file whose name doesn't end with .py, without a bytecode file appearing for that import?

推荐答案

迄今为止我最好的实现是(仅使用 Python 2.6 或更高版本中的功能):

My best implementation so far is (using features only in Python 2.6 or later):

import os
import sys
import imp
import contextlib

@contextlib.contextmanager
def preserve_value(namespace, name):
    """ A context manager to preserve, then restore, the specified binding.

        :param namespace: The namespace object (e.g. a class or dict)
            containing the name binding.
        :param name: The name of the binding to be preserved.
        :yield: None.

        When the context manager is entered, the current value bound to
        `name` in `namespace` is saved. When the context manager is
        exited, the binding is re-established to the saved value.

        """
    saved_value = getattr(namespace, name)
    yield
    setattr(namespace, name, saved_value)


def make_module_from_file(module_name, module_filepath):
    """ Make a new module object from the source code in specified file.

        :param module_name: The name of the resulting module object.
        :param module_filepath: The filesystem path to open for
            reading the module's Python source.
        :return: The module object.

        The Python import mechanism is not used. No cached bytecode
        file is created, and no entry is placed in `sys.modules`.

        """
    py_source_open_mode = 'U'
    py_source_description = (".py", py_source_open_mode, imp.PY_SOURCE)

    with open(module_filepath, py_source_open_mode) as module_file:
        with preserve_value(sys, 'dont_write_bytecode'):
            sys.dont_write_bytecode = True
            module = imp.load_module(
                    module_name, module_file, module_filepath,
                    py_source_description)

    return module


def import_program_as_module(program_filepath):
    """ Import module from program file `program_filepath`.

        :param program_filepath: The full filesystem path to the program.
            This name will be used for both the source file to read, and
            the resulting module name.
        :return: The module object.

        A program file has an arbitrary name; it is not suitable to
        create a corresponding bytecode file alongside. So the creation
        of bytecode is suppressed during the import.

        The module object will also be added to `sys.modules`.

        """
    module_name = os.path.basename(program_filepath)

    module = make_module_from_file(module_name, program_filename)
    sys.modules[module_name] = module

    return module

这有点太宽泛了:它在模块的整个导入过程中禁用字节码文件生成,这意味着在该过程中导入的其他模块也不会生成字节码文件.

This is a little too broad: it disables bytecode file generation during the entire import process for the module, which means other modules imported during that process will also not have bytecode files generated.

我仍在寻找一种方法来禁用指定模块文件的字节码文件生成.

I'm still looking for a way to disable bytecode file generation for only the specified module file.

这篇关于将任意命名的文件作为 Python 模块导入,无需生成字节码文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆