a newby Python dev here.
I have a json file in the root project folder /json
I´m using os.path.abspath to obtain the absolute path.
abspath = os.path.abspath("json/quotes.json")
But it seems that depending from which class I execute that code is getting the path since that part.
So running my server, the absolute path return
******/app/resources/json/quotes.json'
And if I try from my unit test
*******/tests/json/quotes.json'
What I'm missing here?
Regards
The path to the file is relative to the location of script being executed.
In your case:
abspath = os.path.abspath("json/quotes.json")
is equivalent to:
abspath = os.path.join(os.getcwd(), "json/quotes.json")
The file may not be present at that location and attempting to open it may result in an error.
You could use an environment variable to set a base path and then use this in your script.
# Set a default in an environment variable
if not os.getenv('BASE_PATH'):
os.environ['BASE_PATH'] = '/path/to/base'
# Use the environment variable
abspath = os.path.join(os.getenv('BASE_PATH'), "json/quotes.json")
Related
So, when I import a certain module in my python script, a new path gets added to os.environ['PATH']. Also I launch my script in conda enviroment, which also adds a bunch of new entries to PATH. Is there an any way I can get original value of PATH (original in a sense that what I would get if I call echo $PATH or echo %PATH% in system's terminal)?
The reason I need this is because I need to call a specific binary in my script with subprocess.run. The binary I'm executing and the library I'm importing in my script are related in a sense that they are based on the same project, which causes my binary to fail when PATH contains entry to module's dll files. If I don't import this module a call to subprocess.run works fine, but if I do it fails without any error.
Of course If I know what path I need to remove from the PATH it is easy to modify os.environ['PATH']:
path_to_remove = r"C:\ProgramData\Anaconda3\envs\ppp\lib\site-packages\mip\libraries\win64"
saved_path = os.environ['PATH']
path_list = os.environ['PATH'].split(os.pathsep)
path_list.remove(path_to_remove)
os.environ['PATH'] = os.pathsep.join(path_list)
# a call to subprocess.run
os.environ['PATH'] = saved_path
# Or you can copy os.environ, modify it and pass it as env argument to subprocess.run
But I'm looking for more stable, preferably cross-platform way of doing it.
You can save the original value of the environment variable in a separate variable before you import the said module, so that you can restore the value of the environment variable from that variable before calling subprocess.run. Use unittest.mock.patch.dict as a context manager around the call to make the modification to PATH temporary:
from unittest.mock import patch
import os
original_path = os.environ['PATH'] # save the original path before importing mip
...
import mip
...
with patch.dict('os.environ', {'PATH': original_path}):
subprocess.run(...)
I have a file structure that looks like this:
-Project
-trainingData
- Folder1
- Folder2
-python
- get_files.py
-train_model.py
If I want to iterate over each folder in my folder trainingData I use the following function:
data = os.listdir(path_data)
for folder in data:
# Do sth
path_data is "../training_data" in this case. Which makes sense because I need to go one level up and then into the trainingData folder. In cmd I would also type "cd .." and then "cd trainingData"
But why is the path "./training_data" when I call the function in get_files.py from train_model.py. This does not make sense to me. Isn't the relativ path still the same if I would call the function in get_files.py from the file itself?
Because the path is relative to the python file being executed. in this case. train_model.py
The relative path is from the __main__ script, in this case train_model.py. If this wasn't the case then every library or script you imported could all have different relative paths and things would get screwy quickly.
why is the path "./training_data" when I call the function in get_files.py from train_model.py
Because you are calling the file from train_model.py hence the relative path is changed (relative to the current running file).
In order to check the Absolute Path you can use os.path.abspath or os.path.realpath.
Also the newest Path.resolve:
Make the path absolute, resolving any symlinks. A new path object is returned:
I use the very usefull OS library for IT automation.
Bellow the code to create a folder / move into the folder / create a file
import os
# create a directory
os.mkdir("directory")
# get the path of the directory
path = os.path.abspath("directory")
print(f"path after creating the directory: {path}")
# change current directory
os.chdir("directory")
path = os.path.abspath("directory")
print(f"path after changing current directory: {path}")
# create a file
with open("hello.py", "w"):
pass
oupput:
path after creating the directory: P:\Code\Python\directory
path after changing current directory: P:\Code\Python\directory\directory
I don't understand something:
Why the path of the directory file is changing?
I don't have any directory into \directory
Thanks for your answers
If you read the documentation of the [abspath][1] function, you would understand why the extra directory is coming.
Return a normalized absolutized version of the pathname path. On most platforms, this is equivalent to calling the function normpath() as follows: normpath(join(os.getcwd(), path)).
Basically, os.path.abspath('directory') is giving you "the absolute path of something named 'directory' inside the current directory (which also happens to be called 'directory') would be"
The absolute path you're seeing is for something inside the directory you just created, something that doesn't yet exist. The absolute path of the directory you created is stil unchanged, you can check that with:
os.path.abspath('.') # . -> current directory, is the one you created
os.path.abspath translates a the name of a file specified relative to your current working directory, however, the file does not necessarily exist.
So the first call of abpath:
# get the path of the directory
path = os.path.abspath("directory")
print(f"path after creating the directory: {path}")
does nothing more than putting your current working directory in front of the string "directory", you can easily do this by yourself:
os.getcwd() + '/' + "directory"
If you change your working directory with os.chdir("directory") the os.getcwd() returns P:\Code\Python\directory, and appends the second "\directory" to the path. Here you see, that the file does not have to exist.
Having this directory structure. fileOpener.py opens the testfile.txt. And fileCallingFileOpener.py, fileCallingFileOpener2.py and fileCallingFileOpener3.py call a method from fileOpener.py that opens testfile.txt. What would be the correct path written in fileOpener.py so that it always work? Even in other computers.
\parentDirectory
\subfldr1
-fileOpener.py
-testfile.txt
\subfldr2
-fileCallingFileOpener.py
\subfldr2
-fileCallingFileOpener2.py
-fileCallingFileOpener3.py
I am asking something like:
os.path.join("..", "subfldr1", "testfile.txt")
But make it generic so it doesn't depends from where you call it.
You could try to get the location of the fileOpener module using __file__ and then make the path relative to that:
# parentDirectory/subfldr1/fileOpener.py
from pathlib import Path
def read_file():
file_path = Path(__file__).parent / "testfile.txt"
with file_path.open("r", encoding="utf-8") as file:
...
An alternative is to just use an absolute path to a file created in a temp directory, if necessary, or configurable by the end user with, for example, an environment variable.
The following code using Path() is losing relative information:
src_file=inspect.getfile(CompileTypeData)
logger.debug(f'SRC_FILE: {src_file}')
src_path = Path(src_file).resolve()
logger.debug(f'SRC_PATH: {src_path}')
logger.debug(f'SRC_DIRNAME: {src_path.parent}')
Produces this:
DEBUG:from_project_types_file:SRC_FILE: ../../build_compile_mod/compile_type.py
DEBUG:from_project_types_file:SRC_PATH: /build_compile_mod/compile_type.py
DEBUG:from_project_types_file:SRC_DIRNAME: /build_compile_mod
What happened to my relative paths? It's my understanding that resolve() should make this an absolute path rather than losing data.
my_path.resolve() accepts an additional parameter, strict that defaults to False. If set to True, it raises a FileNotFoundError if the path cannot be resolved to an absolute path. That could be used to assess whether pathlib has enough data to create an absolute path from the string you obtained with inspect.getfile().
inspect.getfile() does not always return an absolute path, but you can always convert that path using os.path.realpath(inspect.getfile(obj)), or using Path.relative_to(...) method in combination with os.path.realpath(__file__).
Example of Relative Paths and sys.path.insert
If you put relative paths in to sys.path using
sys.path.insert and change directories while
running the script, then you'll get wrong answers
trying to use __file__ or anything derived from
it.
Say we have this:
/tmp/test1
/tmp/test2
/tmp/test2/mymod.py
/tmp/rundir/rundir2
So we CD to `/tmp/test2/ and launch the script.
% cd /tmp/test2
Now we create this script in Test2 that defines
show_path() which finds the path to mymod.py
and prints it.
Notice that sys.path.insert uses a relative path
this is the problem.
import sys
sys.path.insert(0,'../test1')
from mymod import *
import inspect
from pathlib import Path
def show_path():
myClassFile=inspect.getfile(MyClass)
print(Path(myClassFile).resolve())
So now we run our script. It starts in /tmp/test2/
it then changes directory and runs show_path().
Notice that show_path() points to nowhere because
of the relative path.
import os
os.chdir('/tmp/rundir/rundir2')
show_path()
This gives the following incorrect output.
/private/tmp/rundir/test1/mymod.py