如何保存作为脚本执行的模块的模块路径

时间:2015-09-01 14:18:59

标签: python class module path introspection

我有一个名为1的函数,它返回完整的模块限定类名get_full_class_name(instance)

示例my_utils.py:

instance

不幸的是,当给定一个在当前运行的脚本中定义的类时,此函数会失败。

示例my_module.py:

def get_full_class_name(instance):
    return '.'.join([instance.__class__.__module__,
                     instance.__class__.__name__])

当我运行上述脚本时,它会打印#! /usr/bin/env python from my_utils import get_full_class_name class MyClass(object): pass def main(): print get_full_class_name(MyClass()) if __name__ == '__main__': main() 而不是打印my_module.MyClass

__main__.MyClass

如果我从另一个脚本运行上面的$ ./my_module.py __main__.MyClass ,我会得到所需的行为。

示例run_my_module.py:

main()

运行上面的脚本得到:

#! /usr/bin/env python

from my_module import main

if __name__ == '__main__':
    main()

我是否可以编写$ ./run_my_module.py my_module.MyClass 函数,以便无论get_full_class_name()是否作为脚本运行,它始终返回my_module.MyClass

2 个答案:

答案 0 :(得分:0)

我建议使用Find Path to File Being Run中讨论的技术处理案例__name__ == '__main__'。这导致了这个新的my_utils:

import sys
import os.path

def get_full_class_name(instance):
    if instance.__class__.__module__ == '__main__':
        return '.'.join([os.path.basename(sys.argv[0]),
            instance.__class__.__name__])
    else:
        return '.'.join([instance.__class__.__module__,
            instance.__class__.__name__])

这不处理交互式会话和其他特殊情况(例如从stdin读取)。为此,您可能需要包含detect python running interactively中讨论的技术。

答案 1 :(得分:0)

根据mkiever的回答,我最终将get_full_class_name()更改为您在下面看到的内容。

如果instance.__class__.__module____main__,则不会将其用作模块路径。相反,它使用从sys.argv[0]sys.path中最近的目录的相对路径。

一个问题是sys.path始终包含sys.argv[0]本身的目录,因此该相对路径最终只是sys.argv[0]的文件名部分。作为一个快速的黑客,下面的代码假定sys.argv[0]目录始终是sys.path的第一个元素,并忽略它。这似乎不安全,但对于我的个人代码来说,更安全的选项对于我们来说太繁琐了。

非常感谢任何更好的解决方案/建议。

import os
import sys
from nose.tools import assert_equal, assert_not_equal

def get_full_class_name(instance):
    '''
    Returns the fully-qualified class name.

    Handles the case where a class is declared in the currently-running script
    (where instance.__class__.__module__ would be set to '__main__').
    '''

    def get_module_name(instance):

        def get_path_relative_to_python_path(path):
            path = os.path.abspath(path)
            python_paths = [os.path.abspath(p) for p in sys.path]
            assert_equal(python_paths[0],
                         os.path.split(os.path.abspath(sys.argv[0]))[0])
            python_paths = python_paths[1:]

            min_relpath_length = len(path)
            result = None
            for python_path in python_paths:
                relpath = os.path.relpath(path, python_path)
                if len(relpath) < min_relpath_length:
                    min_relpath_length = len(relpath)
                    result = os.path.join(os.path.split(python_path)[-1],
                                          relpath)

            if result is None:
                raise ValueError("Path {} doesn't seem to be in the "
                                 "PYTHONPATH.".format(path))
            else:
                return result

        if instance.__class__.__module__ == '__main__':
            script_path = os.path.abspath(sys.argv[0])
            relative_path = get_path_relative_to_python_path(script_path)
            relative_path = relative_path.split(os.sep)

            assert_not_equal(relative_path[0], '')
            assert_equal(os.path.splitext(relative_path[-1])[1], '.py')
            return '.'.join(relative_path[1:-1])
        else:
            return instance.__class__.__module__

    module_name = get_module_name(instance)
    return '.'.join([module_name, instance.__class__.__name__])