How do I get the path of the current executed file in Python? [duplicate]
Asked Answered
A

13

234

Is there a universal approach in Python, to find out the path to the file that is currently executing?

Failing approaches

path = os.path.abspath(os.path.dirname(sys.argv[0]))

This does not work if you are running from another Python script in another directory, for example by using execfile in 2.x.

path = os.path.abspath(os.path.dirname(__file__))

I found that this doesn't work in the following cases:

  • py2exe doesn't have a __file__ attribute, although there is a workaround
  • When the code is run from IDLE using execute(), in which case there is no __file__ attribute
  • On Mac OS X v10.6 (Snow Leopard), I get NameError: global name '__file__' is not defined

Test case

Directory tree

C:.
|   a.py
\---subdir
        b.py

Content of a.py

#! /usr/bin/env python
import os, sys

print "a.py: sys.argv[0]=", sys.argv[0]
print "a.py: __file__=", __file__
print "a.py: os.getcwd()=", os.getcwd()
print

execfile("subdir/b.py")

Content of subdir/b.py

#! /usr/bin/env python
import os, sys

print "b.py: sys.argv[0]=", sys.argv[0]
print "b.py: __file__=", __file__
print "b.py: os.getcwd()=", os.getcwd()
print

Output of python a.py (on Windows)

a.py: __file__= a.py
a.py: os.getcwd()= C:\zzz

b.py: sys.argv[0]= a.py
b.py: __file__= a.py
b.py: os.getcwd()= C:\zzz

Related (but these answers are incomplete)

Athanasius answered 13/4, 2010 at 18:37 Comment(1)
execfile doesn't exist any more, and it's not clear to me why the code in the test case should return anything different, or what problem one might hope to solve by having b.py able to identify its own source code location. For that matter, code can run from non-file sources.Throughout
D
89

You can't directly determine the location of the main script being executed. After all, sometimes the script didn't come from a file at all. For example, it could come from the interactive interpreter or dynamically generated code stored only in memory.

However, you can reliably determine the location of a module, since modules are always loaded from a file. If you create a module with the following code and put it in the same directory as your main script, then the main script can import the module and use that to locate itself.

some_path/module_locator.py:

def we_are_frozen():
    # All of the modules are built-in to the interpreter, e.g., by py2exe
    return hasattr(sys, "frozen")

def module_path():
    encoding = sys.getfilesystemencoding()
    if we_are_frozen():
        return os.path.dirname(unicode(sys.executable, encoding))
    return os.path.dirname(unicode(__file__, encoding))

some_path/main.py:

import module_locator
my_path = module_locator.module_path()

If you have several main scripts in different directories, you may need more than one copy of module_locator.

Of course, if your main script is loaded by some other tool that doesn't let you import modules that are co-located with your script, then you're out of luck. In cases like that, the information you're after simply doesn't exist anywhere in your program. Your best bet would be to file a bug with the authors of the tool.

Deerstalker answered 13/4, 2010 at 18:48 Comment(14)
I mention that on OS 10.6 I get NameError: global name '__file__' is not defined using file and this is not inside the IDLE. Think that __file__ is defined only inside modules.Athanasius
@Sorin Sbarnea: I updated my answer with how I get around that.Deerstalker
Thanks, but in fact the problem with missing __file__ had nothing to do with Unicode. I don't know why __file__ is not defined but I'm looking for a generic solution this will work an all cases.Athanasius
@Sorin Sbarnea, unicode isn't the part that I added. The key, I believe, is to put the file in a module, not in the main file. In other words, you should be calling my_module.module_path().Deerstalker
Sorry this is not possible in all cases. For example I try to do this in waf.googlecode.com from inside a wscript file (python). These files are executed but they are not modules and you cannot made them modules (they can be any any subdirectory from the source tree).Athanasius
"For example, it could come from the interactive interpreter or dynamically generated code stored only in memory." Seems like it would still be able to return something in those cases.Toner
Won't that give you the location of some_path/module_locator.py instead?Krystalkrystalle
@Casebash: Yes, you're right. Although the answer stipulates to "create a module with the following code and put it in the same directory as your main script", at best it can tell you what directory the executing file is in, but not its actual name.Illmannered
Just one remark: Modules do not always come from files. One can define custom import hooks. Some nice examples can be found here: pymotw.com/2/sys/imports.htmlShady
@Athanasius use quotes: "file" instead of file in the brackets, treat it like a stringParapet
This also works on windows when calling a frozen exe/script having setup the path to it. Cheers!Threatt
This does not work on python 3. unicode was changed to str. I tried adding if sys.version_info[0] >= 3: unicode = str ala https://mcmap.net/q/14031/-nameerror-global-name-39-unicode-39-is-not-defined-in-python-3 but it is still giving an error. What should I do to make this work?Discommon
The make this work in python3 one should replace unicode(, encoding) with str().Discommon
@Kvothe, I get decoding str is not supported when using str instead of unicodeCormophyte
B
102

First, you need to import from inspect and os

from inspect import getsourcefile
from os.path import abspath

Next, wherever you want to find the source file from you just use

abspath(getsourcefile(lambda:0))
Bracing answered 28/8, 2013 at 13:19 Comment(7)
Great answer. Thanks. It also seems to also be the shortest and most portable (running on different os-es) answer and doesn't encounter problems such as NameError: global name '__file__' is not defined (another solution caused this).Grindstone
Another possibility which is equally short: lambda:_. It worked for me - not sure it'll always work. lambda:0 probably runs faster by some immeasurably small amount (or maybe not so small... might be a load immediate or something even faster for 0 vs a load global for _?) Debatable whether it's cleaner or easier to read or even more clever/obscure.Bracing
As with almost every proposal I have tried, this just returns the cwd for me, not the directory file I am running (debugging).Motile
@Motile - This code goes in the file you're running... running getsourcefile(lambda:0) will be meaningless and just return None if you try running it at an interactive prompt (since the lambda won't be in any source file.) If you want to know where another function or object is coming from in an interactive environment, maybe abspath(getsourcefile(thatFunctionOrObject)) will be more helpful for you?Bracing
So it doesn't work when stepping through file code @artofwarfare?Motile
What does lambda:0 do, and why is it being done?Lucan
@Brōtsyorfuzthrāx - It creates a function because getsourcefile wants a function. getsourcefile then returns the file which contains the function it's given, which would be whatever file we typed that line in. The function just returns 0 if it were ever run, which it never will be.Bracing
D
89

You can't directly determine the location of the main script being executed. After all, sometimes the script didn't come from a file at all. For example, it could come from the interactive interpreter or dynamically generated code stored only in memory.

However, you can reliably determine the location of a module, since modules are always loaded from a file. If you create a module with the following code and put it in the same directory as your main script, then the main script can import the module and use that to locate itself.

some_path/module_locator.py:

def we_are_frozen():
    # All of the modules are built-in to the interpreter, e.g., by py2exe
    return hasattr(sys, "frozen")

def module_path():
    encoding = sys.getfilesystemencoding()
    if we_are_frozen():
        return os.path.dirname(unicode(sys.executable, encoding))
    return os.path.dirname(unicode(__file__, encoding))

some_path/main.py:

import module_locator
my_path = module_locator.module_path()

If you have several main scripts in different directories, you may need more than one copy of module_locator.

Of course, if your main script is loaded by some other tool that doesn't let you import modules that are co-located with your script, then you're out of luck. In cases like that, the information you're after simply doesn't exist anywhere in your program. Your best bet would be to file a bug with the authors of the tool.

Deerstalker answered 13/4, 2010 at 18:48 Comment(14)
I mention that on OS 10.6 I get NameError: global name '__file__' is not defined using file and this is not inside the IDLE. Think that __file__ is defined only inside modules.Athanasius
@Sorin Sbarnea: I updated my answer with how I get around that.Deerstalker
Thanks, but in fact the problem with missing __file__ had nothing to do with Unicode. I don't know why __file__ is not defined but I'm looking for a generic solution this will work an all cases.Athanasius
@Sorin Sbarnea, unicode isn't the part that I added. The key, I believe, is to put the file in a module, not in the main file. In other words, you should be calling my_module.module_path().Deerstalker
Sorry this is not possible in all cases. For example I try to do this in waf.googlecode.com from inside a wscript file (python). These files are executed but they are not modules and you cannot made them modules (they can be any any subdirectory from the source tree).Athanasius
"For example, it could come from the interactive interpreter or dynamically generated code stored only in memory." Seems like it would still be able to return something in those cases.Toner
Won't that give you the location of some_path/module_locator.py instead?Krystalkrystalle
@Casebash: Yes, you're right. Although the answer stipulates to "create a module with the following code and put it in the same directory as your main script", at best it can tell you what directory the executing file is in, but not its actual name.Illmannered
Just one remark: Modules do not always come from files. One can define custom import hooks. Some nice examples can be found here: pymotw.com/2/sys/imports.htmlShady
@Athanasius use quotes: "file" instead of file in the brackets, treat it like a stringParapet
This also works on windows when calling a frozen exe/script having setup the path to it. Cheers!Threatt
This does not work on python 3. unicode was changed to str. I tried adding if sys.version_info[0] >= 3: unicode = str ala https://mcmap.net/q/14031/-nameerror-global-name-39-unicode-39-is-not-defined-in-python-3 but it is still giving an error. What should I do to make this work?Discommon
The make this work in python3 one should replace unicode(, encoding) with str().Discommon
@Kvothe, I get decoding str is not supported when using str instead of unicodeCormophyte
D
31

This solution is robust even in executables:

import inspect, os.path

filename = inspect.getframeinfo(inspect.currentframe()).filename
path     = os.path.dirname(os.path.abspath(filename))
Dolora answered 16/6, 2017 at 14:54 Comment(6)
This should be the correct answer. This works even in entry_point: console_script, but none of the other answers.Dumfound
just gets cwd()Fatness
@eric: Point being?Tildatilde
@PeterMortensen file being run is often not in cwd()Fatness
This does not work in an interpreter. I am running this is PyCharm interpreter console and it gives me: filename: <ipython-input-107-fff10ab01b8b> path: C:\Users\PowerUser\Desktop.Drowsy
Would inspect.getsourcefile(inspect.currentframe()) or even inspect.getfile(inspect.currentframe()) not do it as well?Fullfaced
E
16

I was running into a similar problem, and I think this might solve the problem:

def module_path(local_function):
   ''' returns the module path without the use of __file__.  Requires a function defined
   locally in the module.
   from https://mcmap.net/q/14032/-getting-file-path-of-imported-module-duplicate'''
   return os.path.abspath(inspect.getsourcefile(local_function))

It works for regular scripts and in IDLE. All I can say is try it out for others!

My typical usage:

from toolbox import module_path
def main():
   pass # Do stuff

global __modpath__
__modpath__ = module_path(main)

Now I use _modpath_ instead of _file_.

Ecosystem answered 21/4, 2011 at 19:4 Comment(7)
According to the PEP8 coding style guide, one should never create names with double leading and trailing underscores -- so __modpath__ should be renamed. You also probably don't need the global statement. Otherwise +1!Illmannered
Actually you can define the local function right in the call to module_path(). i.e. module_path(lambda _: None) which doesn't depend on the other contents of the script it is in.Illmannered
@martineau: I took your suggestion of lambda _: None and have used it for nearly the past two years, but just now I discovered I could condense it down to just lambda:0. Is there any particular reason you suggested the form you did, with an ignored argument of _, instead of with no argument at all? Is there something superior about None prefixed with a space rather than just 0? They're both equally cryptic, I think, just one is 8 characters long while the other is 14 characters long.Bracing
@ArtOfWarfare: The difference between the two is that lambda _: is a function that takes one argument and lambda: is one that doesn't take any. It doesn't matter since the function is never called. Likewise it doesn't matter what return value is used. I guess I chose None because at the time it seemed to indicate it was a do-nothing, never-to-be-called function better. The space in front of it is optional and again there only for improved readability (always trying to follow PEP8 is habit-forming).Illmannered
@martineau: It's obviously an abuse of lambda though, using it for something it was never meant to do. If you were to follow PEP8, I think the right content for it would be pass, not None, but it's not valid to put a statement in a lambda, so you have to put in something with a value. There's a few valid 2 character things you could put in, but I think the only valid single character things you can put in are 0-9 (or a single character variable name assigned outside the lambda.) I figure 0 best indicates the nothingness of 0-9.Bracing
@ArtOfWarfare: It not an abuse of lambda, whose purpose is simply to define an anonymous function. As you point out, using pass isn't an option with one, so any valid return value is suitable. Your goal seems to be to write the shortest definition possible, which when we're talking about a few characters, doesn't seem very important to me. BTW, your answer from 2.5 years after this one is essentially simply applying my original comment to this one.Illmannered
I found almost the same solution independently and have created a simpler answer below with more information (https://mcmap.net/q/13833/-how-do-i-get-the-path-of-the-current-executed-file-in-python-duplicate).Grindstone
I
12

You have simply called:

path = os.path.abspath(os.path.dirname(sys.argv[0]))

instead of:

path = os.path.dirname(os.path.abspath(sys.argv[0]))

abspath() gives you the absolute path of sys.argv[0] (the filename your code is in) and dirname() returns the directory path without the filename.

Impacted answered 4/4, 2018 at 15:19 Comment(1)
thks! Only your answer can manage to make path a global variable in a separate module. I can now always call path_util.cwd_path from anywhere to get the directory of the caller py.Stallfeed
R
7

The short answer is that there is no guaranteed way to get the information you want, however there are heuristics that work almost always in practice. You might look at How do I find the location of the executable in C?. It discusses the problem from a C point of view, but the proposed solutions are easily transcribed into Python.

Romance answered 16/4, 2010 at 10:28 Comment(2)
Hello, my flag was declined so I've now started a discussion on meta: meta.stackoverflow.com/questions/277272/…Bracing
There are simple working answers already on this page however. My solution works well: https://mcmap.net/q/13833/-how-do-i-get-the-path-of-the-current-executed-file-in-python-duplicate.Grindstone
G
6

See my answer to the question Importing modules from parent folder for related information, including why my answer doesn't use the unreliable __file__ variable. This simple solution should be cross-compatible with different operating systems as the modules os and inspect come as part of Python.

First, you need to import parts of the inspect and os modules.

from inspect import getsourcefile
from os.path import abspath

Next, use the following line anywhere else it's needed in your Python code:

abspath(getsourcefile(lambda:0))

How it works:

From the built-in module os (description below), the abspath tool is imported.

OS routines for Mac, NT, or Posix depending on what system we're on.

Then getsourcefile (description below) is imported from the built-in module inspect.

Get useful information from live Python objects.

  • abspath(path) returns the absolute/full version of a file path
  • getsourcefile(lambda:0) somehow gets the internal source file of the lambda function object, so returns '<pyshell#nn>' in the Python shell or returns the file path of the Python code currently being executed.

Using abspath on the result of getsourcefile(lambda:0) should make sure that the file path generated is the full file path of the Python file.
This explained solution was originally based on code from the answer at How do I get the path of the current executed file in Python?.

Grindstone answered 4/11, 2015 at 20:42 Comment(2)
yeah I just came up with the same solution... way better than the answers that just say it can't reliably be done... unless the question isn't asking what I think it is...Boer
Again this works from Python on a command line, but not in the iPython interpreter. It gives: Current Path: C:\Users\PowerUser\Desktop\<ipython-input-110-db9b4c6a7aa0>)Drowsy
A
5

This should do the trick in a cross-platform way (so long as you're not using the interpreter or something):

import os, sys
non_symbolic=os.path.realpath(sys.argv[0])
program_filepath=os.path.join(sys.path[0], os.path.basename(non_symbolic))

sys.path[0] is the directory that your calling script is in (the first place it looks for modules to be used by that script). We can take the name of the file itself off the end of sys.argv[0] (which is what I did with os.path.basename). os.path.join just sticks them together in a cross-platform way. os.path.realpath just makes sure if we get any symbolic links with different names than the script itself that we still get the real name of the script.

I don't have a Mac; so, I haven't tested this on one. Please let me know if it works, as it seems it should. I tested this in Linux (Xubuntu) with Python 3.4. Note that many solutions for this problem don't work on Macs (since I've heard that __file__ is not present on Macs).

Note that if your script is a symbolic link, it will give you the path of the file it links to (and not the path of the symbolic link).

Adder answered 10/9, 2014 at 21:55 Comment(0)
T
4

You can use Path from the pathlib module:

from pathlib import Path

# ...

Path(__file__)

You can use call to parent to go further in the path:

Path(__file__).parent
Trapezius answered 10/10, 2016 at 9:49 Comment(2)
This answer uses the __file__ variable which can be unreliable (isn't always the full file path, doesn't work on every operating system etc.) as StackOverflow users have often mentioned. Changing the answer to not include it will cause less problems and be more cross-compatible. For more information, see https://mcmap.net/q/14035/-importing-modules-from-parent-folder.Grindstone
@mrroot5 Ok, so delete your comment please.Trapezius
B
2

Simply add the following:

from sys import *
path_to_current_file = sys.argv[0]
print(path_to_current_file)

Or:

from sys import *
print(sys.argv[0])
Brelje answered 15/11, 2017 at 1:35 Comment(0)
C
1

If the code is coming from a file, you can get its full name

sys._getframe().f_code.co_filename

You can also retrieve the function name as f_code.co_name

Cryptograph answered 25/10, 2018 at 8:25 Comment(0)
C
1

The main idea is, somebody will run your python code, but you need to get the folder nearest the python file.

My solution is:

import os
print(os.path.dirname(os.path.abspath(__file__)))

With os.path.dirname(os.path.abspath(__file__)) You can use it with to save photos, output files, ...etc

Cardiganshire answered 31/3, 2019 at 15:14 Comment(1)
An explanation would be in order. E.g., what is the idea/gist? Please respond by editing (changing) your answer, not here in comments (without "Edit:", "Update:", or similar - the answer should appear as if it was written today).Tildatilde
C
-2
import os
current_file_path=os.path.dirname(os.path.realpath('__file__'))
Cohligan answered 4/4, 2019 at 18:25 Comment(2)
This does not make sense. First, '__file__' should not be a string, second, if you did __file__, this would only work for the file that this line of code is in, and not the file that is executed?Fine
An explanation would be in order. E.g., what is the idea/gist? Please respond by editing (changing) your answer, not here in comments (without "Edit:", "Update:", or similar - the answer should appear as if it was written today).Tildatilde

© 2022 - 2024 — McMap. All rights reserved.