#1
  1. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Feb 2005
    Posts
    78
    Rep Power
    10

    Script location problem


    Hi,

    The background:
    class C1 is in file F1 which is in directory D1.
    class C2, which inherits from C1, is in file F2 which is in directory D2.
    The location of D2 is arbitrary (ie: it can't be hardcoded).
    C1's method m_1() (inherited by C2) must open files relative to D2

    The problem:
    F2.main() must be able to be run both directly (from script or Windows) as well as via "import mypackage.F2 as f; f.main()". Running directly causes no problems with relative opens; importing and calling main(), however, tries to open relative to the current working directory.

    Unfortunately __file__ is only provided when the script is not executed.

    Currently I'm putting this monstrosity at the start of F2's main():
    Code:
        try:
            os.chdir(os.path.dirname(os.path.abspath(__file__)))
        except:
            pass
    I've made no headway with getting inspect.py to work (getabspath() would be handy but it just doesn't appear to like newstyle objects)

    Is there a better way to determine in which file/directory a particular object was created in?

    Thanks, --OH.
  2. #2
  3. No Profile Picture
    Contributing User
    Devshed Intermediate (1500 - 1999 posts)

    Join Date
    Feb 2004
    Location
    London, England
    Posts
    1,585
    Rep Power
    1373
    There are a couple of possibilities, depending on your precise requirements.

    Firstly some basic terminology:

    A module is a python (*.py) file that is on the python path. (It can be other sorts of files such as .pyd, but we will ignore that for now).

    A package is a directory on the python path that has an __init__.py file in it. Modules can live in packages, and packages can be nested.

    I will assume that in your example F1 and F2 are modules and D1 and D2 are packages.

    If you know the package/module structure (e.g. D2.F2) then you can get the directory from the package's __path__ attribute:

    Code:
    # this is module F2 in package D2
    import D2
    here = D2.__path__
    
    ...
    
    f = open( os.path.join(here, 'filename'))
    # do stuff with f
    I use this a lot in my unit test code when I need to read a file of test data.

    If you have only an object to work with then it becomes more tricky, but still possible.

    You can get the class of an object through it's __class__ attribute, and you can get the module that the class was defined in from the classes __module__ attribute. This gives you a string representation of the module, e.g. "package.path", so you need to convert that to an absolute directory path. There are a couple of ways of doing this, but the simplest is to look it up in the sys.modules dictionary, which will return the actual module object, and the module's __file__ attribute give you the path of the module file. Whew!

    So here is a function that will take almost any[1] object and return the directory its class was defined in:
    Code:
    import sys, os
    def get_module_path(obj):
        return os.path.dirname(sys.modules[obj.__class__.__module__].__file__)
    [1] almost any, since it will fail for things like builtin classes which do not have an associated file.

    Dave - The Developers' Coach
  4. #3
  5. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Feb 2005
    Posts
    78
    Rep Power
    10
    Originally Posted by DevCoach
    So here is a function that will take almost any[1] object and return the directory its class was defined in:
    Code:
    import sys, os
    def get_module_path(obj):
        return os.path.dirname(sys.modules[obj.__class__.__module__].__file__)
    Hi,

    Unfortunately that doesn't work in the context I need it to :P -

    file test1.py:
    Code:
    import os,sys
    
    class Foo(object):
        def test(self, obj):
            print self.get_module_path(obj)
    
        def get_module_path(self, obj):
            return os.path.dirname(sys.modules[obj.__class__.__module__].__file__)
    file test2.py:
    Code:
    import test1
    
    class Blah(test1.Foo):
        def __init__(self):
            self.test(self)
    
    try:
        if __name__ == "__main__":
            print "This won't work"
        else:
            print "This will work"
        Blah()
    finally:
        raw_input("[More]")
    "import test2" will work; while running it from the shell or running "C:\python24\pythonw.exe test2.py" from the command line will not (assuming both are saved there).
    [I realise that obj and self are the same object - in the real code they won't necessarily be]

    test1 (library) cannot know about test2 (user code) except through what is passed to it.

    Thanks, --OH
    [Terminalogically I meant "directory", not package, as this will break even if D1 and D2 are identical, or if only one of them is in a package; I should have used "module", though]
  6. #4
  7. Mini me.
    Devshed Novice (500 - 999 posts)

    Join Date
    Nov 2003
    Location
    Cambridge, UK
    Posts
    783
    Rep Power
    14
    DevCoach,
    That filepath function was a good tip, I am surprised there is not a more direct way to get the real path though.

    A small improvement for py2exe users is to check if library.zip is in the returned path. If it is then you need to perform the os.path.dirname() again to get the actual path and not the zip library.

    I stumbled on this while testing your solution against a desktop shortcut problem I had. (which it solves ). I needed to always identify the installation directory for a py2exe compiled application regardless of how the application was launched. The usual check for working directory blew up when using shortcuts

    grim

IMN logo majestic logo threadwatch logo seochat tools logo