Get name of current script in Python
Asked Answered
B

14

660

I'm trying to get the name of the Python script that is currently running.

I have a script called foo.py and I'd like to do something like this in order to get the script name:

print(Scriptname)
Begonia answered 11/11, 2010 at 9:32 Comment(1)
Path(__file__).name since Python3.4+.Reachmedown
C
952

You can use __file__ to get the name of the current file. When used in the main module, this is the name of the script that was originally invoked.

If you want to omit the directory part (which might be present), you can use os.path.basename(__file__).

Cavil answered 11/11, 2010 at 9:35 Comment(16)
Python 3.2: "Exception NameError: NameError("global name '__file__' is not defined",)"Partan
@sdaau: __file__ is not defined in the interactive interpreter, because it is meaningless there. It is set by the import implementation, so if you use a non-standard import mechanism it might also be unset.Cavil
At least for Python 2.7, I believe an import os is required for this to work. I'd add this into the answer.Pronounce
Actually, import os.path.Alectryomancy
@cdunn2001: import os and import os.path are completely equivalent.Cavil
@sven-marnach: Oh, you're right. I've been Doing It Wrong for years!Alectryomancy
you use os.path if you want to import only listed functions, for example from os.path import join or especially if you want to give them an alias - for example from os.path import join as path_joinRockel
This, in general, will absolutely not work. That's because the code that's executing may be in a separate library, where file will just give you the name of that library file, not the currently executing script, which is what is being asked for. Granted, the question SEEMS to refer to code executing in the main script, but what's clear is that it's the name of the current script that's desired and any solution should work in all cases. See later answers.Theorbo
@JohnDeighan Ok, ok, no need to get upset. There is no solution that works in all circumstances. The value of sys.argv[0] can be arbitrarily set when the process is created, so it won't work in general. Importing __main__ doesn't always work for various reasons. In my opinion, you shouldn't need the path of the current script, and if you absolutely do, you should retrieve it in the main module using __file__. Granted, I didn't discuss any of that in this answer, since most people just want something easy that works in the common case.Cavil
all the answers are great, but fail under certain circumstances. What if the script was called by setup.py test , what if the script is called by doctest, pytest or by uwsgi ? To add a new possibility, aside main.__file__, sys.argv - You can also inspect the stack for the called program filename. And - You should check probably the result - does that file exist ? I put all that solutions together in a short library, see : github.com/bitranox/lib_programnameAdolphadolphe
@Adolphadolphe Thanks! What's the use case you have in mind for this library? I understand it returns the name of the main script, but why would anyone want that information in all these circumstances?Cavil
@sven-marnach: I have many use cases for that - for instance logging, restart processes (since You can not rely on the process list) and so on. And since I do TDD, I want that the main script name is also correct in my tests. Of course, You can do it manually, from case to case different, but for me it is easier to have one small library which delivers the correct name under all circumstances - even when running "setup.py test", "pytest", "doctest in pycharm", "uwsgi" and so on. Also it provides an easy to use, do not think twice method for the community, so why not share it.Adolphadolphe
@Adolphadolphe Hmm, I don't quite understand the use cases. For logging, I generally create loggers local to each module or component, and then let the main script set up the logging config to decide what's happening with the logs. "Restart processes" – this is also unclear to me. How would that work when running tests? And don't get me wrong: I don't mind it's useful for you. I'm just trying to understand whether it would be useful for me as well. :)Cavil
@sven-marnach - well, if You dont know if You need it, then You probably dont need it ;-) I have a huge codebase with a lot of different tests in pytest, doctest and so on, and in some cases I need to get the name (or lets better say the full pathlib.Path) of the main script reliably, even under test conditions. Or imagine You need to find a configuration file that lives in a directory relative to where the application startup script is located. Also it reads cleaner in the code. A lot of people seem to need it, see also : stackoverflow.com/questions/50499Adolphadolphe
@sven_marnach: btw, its changing in python3.9 again : the file attribute of the main module became an absolute path, rather than a relative path. These paths now remain valid after the current directory is changed by os.chdir() So I really only want to change it in ONE place, rather then scanning my code were it should/can be changed ...Adolphadolphe
I've come up with a one-liner: filename = __file__.split('/')[-1] or filetitle = __file__.split('/')[-1].split('.')[0]Auguste
D
240
import sys
print(sys.argv[0])

This will print foo.py for python foo.py, dir/foo.py for python dir/foo.py, etc. It's the first argument to python. (Note that after py2exe it would be foo.exe.)

Dank answered 11/11, 2010 at 9:36 Comment(9)
@DenisMalinovsky: define "won't work". If you call python linkfile.py, where linkfile.py is a symlink to realfile.py, sys.argv[0] will be 'linkfile.py', which may or may not be what you want; it is certainly what I expect. __file__ is the same: it will be linkfile.py. If you want to find 'realfile.py' from 'linkfile.py', try os.path.realpath('linkfile.py').Dank
+1 because it's (a) a little neater and (b) will still work in module (where the file variable would be the module file, not the executed one).Genovera
This answer is nice because it works in IDLE too. As a note, to get just the filename, you can write os.path.basename(sys.argv[0])Comatose
More importantly, this doesn't work except from inside the main file. Do not use this, use __file__ instead.Buttress
WHAT!!! Did you even try this? Exactly the opposite is true. The questioner asked for the name of the python script that is running - not the python file which is currently executing. Imagine that you have a script that, when an error occurs, prints the script name along with allowed arguments. You put that in a function, using one of these 2 techniques. At some point, you decide to move the function to an external library. Would you want to print the name of the main script running, or the name of the library file that's executing?Theorbo
This is the right solution when you want to print out a usage info because this is exactly how the program is called, while file is not.Tybi
This answer is wrong, if that python script is a module located in a package and is being imported, sys.argv[0] will print the name of the "importer" and not the "importee" (which is the Python script containing the sys.argv[0]).Reachmedown
@aderchox: that it’s giving the script (the entry point), not the file, is the whole point, as the comments here also clearly show. The question was ambiguous, this is the correct answer for one of its possible interpretations—the most commonly desired, I suspect.Dank
This one combined with the accepted answer is good to get the python process name on an imported module: os.path.basename(sys.argv[0])Straitlaced
R
159

For completeness' sake, I thought it would be worthwhile summarizing the various possible outcomes and supplying references for the exact behaviour of each.

The answer is composed of four sections:

  1. A list of different approaches that return the full path to the currently executing script.

  2. A caveat regarding handling of relative paths.

  3. A recommendation regarding handling of symbolic links.

  4. An account of a few methods that could be used to extract the actual file name, with or without its suffix, from the full file path.


Extracting the full file path

  • __file__ is the currently executing file, as detailed in the official documentation:

    __file__ is the pathname of the file from which the module was loaded, if it was loaded from a file. The __file__ attribute may be missing for certain types of modules, such as C modules that are statically linked into the interpreter; for extension modules loaded dynamically from a shared library, it is the pathname of the shared library file.

    From Python3.4 onwards, per issue 18416, __file__ is always an absolute path, unless the currently executing file is a script that has been executed directly (not via the interpreter with the -m command line option) using a relative path.

  • __main__.__file__ (requires importing __main__) simply accesses the aforementioned __file__ attribute of the main module, e.g. of the script that was invoked from the command line.

    From Python3.9 onwards, per issue 20443, the __file__ attribute of the __main__ module became an absolute path, rather than a relative path.

  • sys.argv[0] (requires importing sys) is the script name that was invoked from the command line, and might be an absolute path, as detailed in the official documentation:

    argv[0] is the script name (it is operating system dependent whether this is a full pathname or not). If the command was executed using the -c command line option to the interpreter, argv[0] is set to the string '-c'. If no script name was passed to the Python interpreter, argv[0] is the empty string.

    As mentioned in another answer to this question, Python scripts that were converted into stand-alone executable programs via tools such as py2exe or PyInstaller might not display the desired result when using this approach (i.e. sys.argv[0] would hold the name of the executable rather than the name of the main Python file within that executable).

  • If none of the aforementioned options seem to work, probably due to an atypical execution process or an irregular import operation, the inspect module might prove useful, as suggested in another answer to this question:

    import inspect
    source_file_path = inspect.getfile(inspect.currentframe())
    

    However, inspect.currentframe() would raise an exception when running in an implementation without Python stack frame.

    Note that inspect.getfile(...) is preferred over inspect.getsourcefile(...) because the latter raises a TypeError exception when it can determine only a binary file, not the corresponding source file (see also this answer to another question).

  • From Python3.6 onwards, and as detailed in another answer to this question, it's possible to install an external open source library, lib_programname, which is tailored to provide a complete solution to this problem.

    This library iterates through all of the approaches listed above until a valid path is returned. If all of them fail, it raises an exception. It also tries to address various pitfalls, such as invocations via the pytest framework or the pydoc module.

    import lib_programname
    # this returns the fully resolved path to the launched python program
    path_to_program = lib_programname.get_path_executed_script()  # type: pathlib.Path
    

Handling relative paths

When dealing with an approach that happens to return a relative path, it might be tempting to invoke various path manipulation functions, such as os.path.abspath(...) or os.path.realpath(...) in order to extract the full or real path.

However, these methods rely on the current path in order to derive the full path. Thus, if a program first changes the current working directory, for example via os.chdir(...), and only then invokes these methods, they would return an incorrect path.


Handling symbolic links

If the current script is a symbolic link, then all of the above would return the path of the symbolic link rather than the path of the real file and os.path.realpath(...) should be invoked in order to extract the latter.


Further manipulations that extract the actual file name

os.path.basename(...) may be invoked on any of the above in order to extract the actual file name and os.path.splitext(...) may be invoked on the actual file name in order to truncate its suffix, as in os.path.splitext(os.path.basename(...)).

From Python 3.4 onwards, per PEP 428, the PurePath class of the pathlib module may be used as well on any of the above. Specifically, pathlib.PurePath(...).name extracts the actual file name and pathlib.PurePath(...).stem extracts the actual file name without its suffix.

Rodroda answered 19/2, 2016 at 20:7 Comment(0)
E
69

Note that __file__ will give the file where this code resides, which can be imported and different from the main file being interpreted. To get the main file, the special __main__ module can be used:

import __main__ as main
print(main.__file__)

Note that __main__.__file__ works in Python 2.7 but not in 3.2, so use the import-as syntax as above to make it portable.

Elviselvish answered 5/11, 2012 at 21:16 Comment(3)
This works in many cases but not when I am using the rPython package from R language. That must be an exceptional case that is just too hard to handle.Olenolin
Indeed, the rPython package embeds the python interpreter, which means there isn't a 'main' file like there is when python is running on its own (you'll find the same behaviour anytime python is embedded). It does import __main__ internally, for use in passing variables between R and python, so it would be relatively easy to make it set __main__.__file__ before calling anything else, but I'm not even sure what would be an appropriate value in this case.Eec
This does not work in an interactive terminal like iPython. I get the following result import main as main print(main.__file__) Traceback (most recent call last): Input In [150] in <cell line: 2> print(main.__file__) AttributeError: module 'main' has no attribute 'file'Isogamy
D
58

The Above answers are good . But I found this method more efficient using above results.
This results in actual script file name not a path.

import sys    
import os    
file_name =  os.path.basename(sys.argv[0])
Dahabeah answered 3/4, 2015 at 10:9 Comment(3)
I like to split off the extension too, so I use: os.path.splitext(os.path.basename(sys.argv[0]))[0]Wheatworm
for me some years later with Python 3.8 this prints ipykernel_launcher.py and not the filename.Balenciaga
Are you using jupyter notebook?Dahabeah
C
38

For modern Python versions (3.4+), Path(__file__).name should be more idiomatic. Also, Path(__file__).stem gives you the script name without the .py extension.

Cutcliffe answered 3/6, 2018 at 3:8 Comment(6)
NameError: name 'Path' is not definedWheatworm
You should from pathlib import Path first.Cutcliffe
"modern" means Python 3.x ?Aleksandrovsk
@Aleksandrovsk pathlib was introduced in Python 3.4, so it should work starting from Python 3.4.Dance
This code throws an error: Path(file).name Traceback (most recent call last): Input In [155] in <cell line: 1> Path(file).name NameError: name 'file' is not definedIsogamy
Its __file__ not filePosture
I
14

As of Python 3.5 you can simply do:

from pathlib import Path
Path(__file__).stem

See more here: https://docs.python.org/3.5/library/pathlib.html#pathlib.PurePath.stem

For example, I have a file under my user directory named test.py with this inside:

from pathlib import Path

print(Path(__file__).stem)
print(__file__)

running this outputs:

>>> python3.6 test.py
test
test.py
Ivar answered 21/5, 2020 at 20:37 Comment(0)
T
12

If you're doing an unusual import (e.g., it's an options file), try:

import inspect
print (inspect.getfile(inspect.currentframe()))

Note that this will return the absolute path to the file.

Terpsichorean answered 5/11, 2017 at 9:32 Comment(1)
this is what finally worked when the current file is invoked from env.SConscript in platform.io build system.Okapi
F
11

Try this:

print __file__
Fowlkes answered 11/11, 2010 at 9:36 Comment(1)
This needs context. It throws an error as is: print(file) Traceback (most recent call last): Input In [156] in <cell line: 1> print(file) NameError: name 'file' is not definedIsogamy
N
8

we can try this to get current script name without extension.

import os

script_name = os.path.splitext(os.path.basename(__file__))[0]
Nivernais answered 22/1, 2020 at 11:59 Comment(0)
S
8

You can do this without importing os or other libs.

If you want to get the path of current python script, use: __file__

If you want to get only the filename without .py extension, use this:

__file__.rsplit("/", 1)[1].split('.')[0]
Spermine answered 8/10, 2020 at 13:42 Comment(0)
A
4

all that answers are great, but have some problems You might not see at the first glance.

lets define what we want - we want the name of the script that was executed, not the name of the current module - so __file__ will only work if it is used in the executed script, not in an imported module. sys.argv is also questionable - what if your program was called by pytest ? or pydoc runner ? or if it was called by uwsgi ?

and - there is a third method of getting the script name, I havent seen in the answers - You can inspect the stack.

Another problem is, that You (or some other program) can tamper around with sys.argv and __main__.__file__ - it might be present, it might be not. It might be valid, or not. At least You can check if the script (the desired result) exists !

the library lib_programname does exactly that :

  • check if __main__ is present
  • check if __main__.__file__ is present
  • does give __main__.__file__ a valid result (does that script exist ?)
  • if not: check sys.argv:
  • is there pytest, docrunner, etc in the sys.argv ? --> if yes, ignore that
  • can we get a valid result here ?
  • if not: inspect the stack and get the result from there possibly
  • if also the stack does not give a valid result, then throw an Exception.

by that way, my solution is working so far with setup.py test, uwsgi, pytest, pycharm pytest , pycharm docrunner (doctest), dreampie, eclipse

there is also a nice blog article about that problem from Dough Hellman, "Determining the Name of a Process from Python"

BTW, it will change again in python 3.9 : the file attribute of the main module became an absolute path, rather than a relative path. These paths now remain valid after the current directory is changed by os.chdir()

So I rather want to take care of one small module, instead of skimming my codebase if it should be changed somewere ...


Disclaimer: I'm the author of the lib_programname library.

Adolphadolphe answered 31/5, 2020 at 17:9 Comment(2)
With your permission, I've upvoted your answer and referred to it and to the library in my answer, which will hopefully increase their exposure.Rodroda
@Yoel: thank You for that. In case of any issue with that library - please let me know, then i will update quicklyAdolphadolphe
Y
1

Since the OP asked for the name of the current script file I would prefer

import os
os.path.split(sys.argv[0])[1]
Yarmouth answered 5/9, 2019 at 19:42 Comment(0)
D
0

if you get script path in base class, use this code, subclass will get script path correctly.

sys.modules[self.__module__].__file__
Dismantle answered 13/1, 2023 at 0:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.