Search code examples
pythoninheritancepathfilepath

Accessing script file location from an external file python


I have the following bizarre set up. Consider 3 scripts in different directories:

  • root1/folderA/scriptA.py
  • root2/folderB/scriptB.py
  • root2/folderC/scriptC.py

The first file and it's location are fully modifiable. The second and third are completely fixed.

scriptA.py contains a parent class:

class A:
    def get_path(self):
        # ...
        # code to determine "my_path"
        # ...
        return my_path

scriptB.py contains a child class:

from root1.folderA.scriptA import A

class B(A):
    pass

scriptC.py contains the code to execute:

from root2.folderB.scriptB import B

if __name__ == "__main__":
    b = B()
    print(b.get_path()) # want print root/folderB/scriptB.py

In scriptC.py what I want is the behaviour to get the path of the child class's declaration file (without any hard-coding). Is there any way to program the A.get_path() to have this behavoir?

I've tried looking into the inspect, os.path and pathlib modules but I haven't had any luck.


Solution

  • It looks like the trick is to use __init_subclass__ which runs whenever a class is sub-classed, in conjunction with a class's __module__ attribute to retrieve its containing module, and __file__ to retrieve the absolute path to the python script or module.

    For example, in script_a.py:

    import sys
    from pathlib import Path
    
    
    class PathInfo:
        __slots__ = ()
    
        path: Path
    
        def __init_subclass__(cls, **kwargs):
            mod = sys.modules[cls.__module__]
            # `__file__` is a string, so we want to convert to a `Path` so it's easier to work with
            cls.path = Path(mod.__file__)
    
    
    class A(PathInfo):
        __slots__ = ()
    

    script_b.py:

    from script_a import A
    
    
    class B(A):
        pass
    
    
    # prints: /Users/<name>/PycharmProjects/my-project/script_a.py
    print(A().path)
    # prints: /Users/<name>/PycharmProjects/my-project/script_b.py
    print(B.path)