首页 > 解决方案 > 可靠获取调用函数的文件路径

问题描述

说我有一个功能foo()。调用时,我希望该函数能够可靠地确定调用它的模块(或调用函数)的文件路径。问题是inspect模块中的各种函数只返回相对路径(我假设是相对于导入时的工作目录)。但是,我的代码多次切换工作目录,我不知道有问题的工作目录。

import inspect
import os

def foo():
    frame = inspect.stack()[0]
    print(frame.filename)
    print(os.path.abspath(frame.filename))

foo()

os.chdir("/")
print("New CWD:", os.getcwd())

foo()

输出:

$ cd ~
$ python3 foo.py
foo.py
/home/user/foo.py
New CWD: /
foo.py
/foo.py

通过我的 IDE 中的自动完成,我发现有一个未记录的函数inspect.getabsfile()。不幸的是,这也不能可靠地工作:

import inspect

def foo():
    print("Hello")

print(inspect.getabsfile(foo))

os.chdir("/")
print("New CWD:", os.getcwd())

print(inspect.getabsfile(foo))

输出:

$ cd ~
$ python3 foo.py
/home/user/foo.py
New CWD: /
/foo.py

如果我正确理解了这个错误报告__file__并且code.co_filenamedocs)(是?)将在某个时候(还没有发生)成为绝对路径。在此期间我能做些什么来解决这个问题?

更新:以上两个示例脚本的主要问题是它们访问源自主模块的对象/堆栈帧。只要从不同的模块导入对象或考虑存在于另一个模块中的函数的堆栈框架,它就可以工作。以类似的方式,在其他模块中__main____file__确实给出了绝对路径。(另请参阅Python 错误跟踪器中的这个问题。)不过,我不知道为什么对主模块进行不同的处理。

标签: pythonpython-3.x

解决方案


通过使用closure技术,您可以将脚本路径存储在import time中,因此只需检查函数是否来自__main__就足以获取以下所有 3 种情况的路径:

  • from ~ import ~
  • import ~
  • 主脚本中定义的对象

以下示例演示了所有 3 个:

from sys import modules
from os.path import abspath
from os import getcwd, chdir

from Module import a
import timeit


def closure():
    original_working_dir = abspath(__file__)  # store absolute path for linux

    def _get_path(obj):
        nonlocal original_working_dir

        try:
            return abspath(obj.__file__)

        except AttributeError:  # it's probably not a module
            if obj.__module__ == '__main__':  # check if module is same as local:
                return abspath(original_working_dir)

            return abspath(modules[obj.__module__].__file__)
    return _get_path


get_path = closure()


def test_func():
    pass


def test_output():
    print(f"\nworking directory: {getcwd()}")
    print(f"import from: {get_path(a)}")
    print(f"simple import: {get_path(timeit)}")
    print(f"local function: {get_path(test_func)}")


if __name__ == '__main__':
    test_output()
    chdir('/')
    test_output()

窗口的输出:

working directory: Z:\github\StackOverFlow\63865883
import from: Z:\github\StackOverFlow\63865883\Module.py
simple import: C:\Users\--\AppData\Local\Programs\Python\Python38\lib\timeit.py
local function: Z:\github\StackOverFlow\63865883\63865883.py

working directory: Z:\
import from: Z:\github\StackOverFlow\63865883\Module.py
simple import: C:\Users\--\AppData\Local\Programs\Python\Python38\lib\timeit.py
local function: Z:\github\StackOverFlow\63865883\63865883.py

Ubuntu:

working directory: /mnt/z/github/StackOverFlow/63865883
import from: /mnt/z/github/StackOverFlow/63865883/Module.py
simple import: /usr/lib/python3.8/timeit.py
/mnt/z/github/StackOverFlow/63865883/63865883.py
local function: /mnt/z/github/StackOverFlow/63865883/63865883.py

working directory: /
import from: /mnt/z/github/StackOverFlow/63865883/Module.py
simple import: /usr/lib/python3.8/timeit.py
/mnt/z/github/StackOverFlow/63865883/63865883.py
local function: /mnt/z/github/StackOverFlow/63865883/63865883.py

您的代码不时正常工作的主要原因是因为__file__主脚本的属性取决于您是使用相对路径还是绝对路径运行 python 脚本。

似乎__file__主脚本存储从终端提供给 python 解释器的文件参数。

例如,运行此文件时:

print(__file__)

当通过相对调用时python3 scratch.py

scratch.py

你得到它的绝对路径。

使用绝对路径python3 /mnt/c/~~/scratches/scratch.py

/mnt/c/Users/--/AppData/Roaming/JetBrains/PyCharm2020.2/scratches/scratch.py

你得到完整的路径__file__


推荐阅读