How to use pathlib to process paths that begin with ~? - python

I am writing a cli-tool that needs some path as input.
I am writing this tool in python and would like to use no python interpreter below 3.6. Using the packagepathlibseems to be the modern way to go when dealing with paths in python. So I would like to leave os and os.path behind if possible.
It seems like pathlib interprets the path ~/test/ as a relative path to the current working directory, the code below shows it
import pathlib
test_path = pathlib.Path('~/test')
absolute_path = test_path.absolute()
print(f"{str(test_path):>31}\n{str(absolute_path):>31}")
# output:
# ~/test
# /home/myUser/~/test
How can I use pathlib to recognize every path starting with ~ as an absolute path and automatically expand ~ into the users home directory?

The answer is easy, use .expanduser() on your Path object instead of .absolute()and it will replace ~ with the home directory of the user running the script, the result is also an absolute path only if ~ is at the beginning:
import pathlib
test_path = pathlib.Path('~/test')
absolute_path = test_path.expanduser()
# If ~ is somewhere in the middle of the path, use .resolve() to get an absolute path.
print(f"{str(test_path):>31}\n{str(absolute_path):>31}")
# output:
# ~/test
# /home/myUser/test

Related

Python searching for image in wrong directory

I am trying to load some assets onto my program that I have them in a folder called 'Graphics', which is inside the folder 'Snake', which is inside the folder 'Projects', which is inside the folder 'Python'. However, also inside that folder 'Python' is another folder named 'HelloWorld'.
I am trying to load some assets in a program that I am running in 'Snake' and Python is searching for the assets in the 'HelloWorld' folder (which is where I used to keep my python files).
I get the error:
FileNotFoundError: No file 'Projects/Snake/Graphics/apple.png' found in working directory 'C:\Users\35192\OneDrive - WC\Desktop\Python\HelloWorld'
I believe that for this I have to change the default directory for vs code. I have changed the default directory for the command prompt and it did nothing. Perhaps this is because the python that I am running in the command prompt is different from the one in vs code (?)
How do I fix this?
Thank you in advance.
Edit:
This is how I am currently loading the image:
apple = pygame.image.load('Projects\Snake\Graphics\apple.png').convert_alpha()
Use pathlib to construct the path to your images. You wil have to add import pathlib to your code.
pathlib.Path(__file__) will give you the path to the current file. pathlib.Path(__file__).parent will give you the folder. Now you can construct the path with the / operator.
Try the following code and check the output.
import pathlib
print(pathlib.Path(__file__))
print(pathlib.Path(__file__).parent)
print(pathlib.Path(__file__).parent / 'Grahics' / 'apple.png')
Now you will be able to move the full project to a totally different folder without having to adjust any code.
Your code example looks like this: apple = pygame.image.load('Projects\Snake\Graphics\apple.png').convert_alpha()
If you import pathlib you can replace that with the dynamic approach:
path_to_image= pathlib.Path(__file__).parent / 'Grahics' / 'apple.png'
apple = pygame.image.load(path_to_image).convert_alpha()
I'm quite sure that pygame can work with a path from pathlib. If not then you have to convert the path to a string manually
apple = pygame.image.load(str(path_to_image)).convert_alpha()
You don't need to change the default directory. Just load from the full directory. That should look something like: "C:\Users\...\Python\Snake\Graphics\apple.png".
I think the simplest way is to first see your active directory by simply typing in
pwd, and then you could simply change the directory by cd("C:/path/to/location"), remember you have to use the backslash, or just use the following library:
import os
os.chdir("C:/path/to/location")
As pydragon posted, you could also import it by just giving the import function a path.

How to normalize a relative path using pathlib

I'm trying to use relative paths in Python, and I want to put my csv files in a separate folder from my python code.
My python program is in the following folder:
G:\projects\code
I want to read this file which is one level up:
G:\projects\data\sales.csv
How do I specify a path using pathlib that is one level up from my current working folder? I don't want to change the current working folder.
I tried this:
from pathlib import Path
file = Path.cwd() /'..'/'data'/'sales.csv'
But now the 'file' variable equals this:
'G:/projects/code/../data/sales.csv'
I read through the docs and either it isn't explained there or I'm just missing it.
Although it's not a problem that your path includes '..' (you can still use this path to open files, etc. in Python), you can normalize the path using resolve():
from pathlib import Path
path = Path.cwd() / '..' / 'data' / 'sales.csv'
print(path) # WindowsPath('G:/projects/code/../data/sales.csv')
print(path.resolve()) # WindowsPath('G:/projects/data/sales.csv')
NB: I personally would name a variable that contains a path path, not file. So you could later on do file = open(path).
print(
Path(__file__).parent, # the folder
Path(__file__).parent.parent, # the folder's parent
sep='\n'
)
print(
Path(
Path(__file__).parent.parent, 'hello.py'
)
)
results in
C:\Users\isik\Desktop\Python\MessAround\project\module
C:\Users\isik\Desktop\Python\MessAround\project
C:\Users\isik\Desktop\Python\MessAround\project\hello.py
with this file structure
-project
-module
-__init__.py
-hello.py
-__init__.py
while the code is located inside project.module.__init__.py
Do you mean "read my csv files"?
The import keyword has a different meaning in Python (you import only other Python modules).
In any case, in order to read a file located one folder above your Python file, you can use this:
import os
filePath = os.path.dirname(__file__)+'/../'+fileName
fileDesc = open(filePath)
fileData = fileDesc.read()
fileDesc.close()
...
here is an example I used:
import json
from pathlib import Path
def read_files(folder_name, file_name):
base_path = Path.cwd().joinpath('configs','resources')
path = base_path.joinpath(folder_name,file_name)
open_file = open(path,'r')
return json.load(open_file.read())
This is pretty old, but I happened on it looking for something else.
Oddly you never got a direct, obvious, answer -- you want the parent property:
from pathlib import Path
file = Path.cwd().parent / 'data' / 'sales.csv'
Note that some of the answers that say you want __file__ rather than the current working directory may be correct (depending on your use case), in which case it's:
from pathlib import Path
file = Path(__file__).parent.parent / 'data' / 'sales.csv'
(parent of the python file is the code dir, parent of that is the projects dir.
However, It's not great practice to refer to your data by its relative path to your code -- I think using the cwd is a better option -- though what you should do is pass the path to the data in to the script via sys.argv.

os.path moving through directories

i have this structure in my code.
app
src
script.py
data
data.ptk
i need to open the file "data.ptk" from "scrip.py", using os.path i'm able to extract the script path.
my_path = os.path.abspath(os.path.dirname(__file__))
But flowing the structure i need to go back 2 directories and then enter the "data" directory to open the file.
The easy way would be to decompose the string my_path with a split("/"), remove the last 2 words and add "data" ...
But I do not think it's the right way
The script.py need to be independent of the OS, that is the reason i'm not able to "hard code" the directory where de pkl file is placed
Any suggestions? thanks.
To elaborate on my comment more, you can see the documentation for pathlib here: https://docs.python.org/3/library/pathlib.html?highlight=pathlib#module-pathlib. It is a stock part of python3 (not sure about python2). I think the following would work:
from pathlib import Path
scriptPath = Path(__file__).absolute() # the absolute() is not strictly necessary
srcPath = scriptPath.parent
appPath = srcPath.parent
commonDirectory = appPath.parent # this could have been shortened with scriptPath.parent.parent.parent
dataPath = commonDirectory / 'data'
dtkFile = dataPath / 'data.ptk'

How to get the proper absolute path of a file using Python 3?

I am trying to get the absolute path of a file, but it's not working. My Code is only showing the path to the directory of my running code. This are the two way I have tried:
1)
import os
print(os.path.abspath("More_Types.py"))
2)
import os
print(os.path.realpath("More_Types.py"))
But I continue to get the full path to my running program. How can I get the correct path to file, that is located some where else in my Computer?
PS: I am sorry I can't provide the output because it will reveal all the folders to my running program.
More_Types.py, subdir/More_Types.py and ../More_Types.py are relative paths.
Outcome
If you provide a relative path, realpath and abspath will return an absolute path relative to the current working directory. So essentially, they behave like os.path.join with the current working directory as first argument:
>>> import os.path
>>> os.path.join(os.getcwd(), 'More_Types.py') == os.path.abspath('More_Types.py')
True
This explains, why you get the result you explained.
Explanation
The purpose of abspath is to convert a relative path into an absolute path:
>>> os.path.abspath('More_Types.py')
'/home/meisterluk/More_Types.py'
Unlike abspath, relpath also follows symbolic links. Technically, this is dangerous and can get you caught up in infinite loops (if a link points to any of its parent directories):
>>> os.path.abspath('More_Types.py')
'/net/disk-user/m/meisterluk/More_Types.py'
Proposed solution
However, if you want to retrieve the absolute path relative to some other directory, you can use os.path.join directly:
>>> directory = '/home/foo'
>>> os.path.join(directory, 'More_Types.py')
'/home/foo/More_Types.py'
>>>
I think the best answer to your question is: You need to specify the directory the file is in. Then you can use os.path.join to create an absolute filepath.
You could use this if you want:
import os
a='MoreTypes.py'
for root , dirs , files in os.walk('.') :
for file in files:
if file==a:
print(os.path.join(root,file))

Working with relative paths

When I run the following script:
c:\Program Files\foo\bar\scripy.py
How can I refer to directory 'foo'?
Is there a convenient way of using relative paths?
I've done it before with the string module, but there must be a better way (I couldn't find it in os.path).
The os.path module includes various functions for working with paths like this. The convention in most operating system is to use .. to go "up one level", so to get the outside directory you could do this:
import os
import os.path
current_dir = os.getcwd() # find the current directory
print current_dir # c:\Program Files\foo\bar\scripy.py
parent = os.path.join(current_dir, "..") # construct a path to its parent
print parent # c:\Program Files\foo\bar\..
normal_parent = os.path.normpath(parent) # "normalize" the path
print normal_parent # c:\Program Files\foo
# or on one line:
print os.path.normpath(os.path.join(os.getcwd(), ".."))
os.path.dirname(path)
Will return the second half of a SPLIT that is performed on the path parameter. (head - the directory and tail, the file) Put simply it returns the directory the path is in. You'll need to do it twice but this is probably the best way.
Python Docs on path functions:
http://docs.python.org/library/os.path#os.path.expanduser
I have recently started using the unipath library instead of os.path. Its object-oriented representations of paths are much simpler:
from unipath import Path
original = Path(__file__) # .absolute() # r'c:\Program Files\foo\bar\scripy.py'
target = original.parent.parent
print target # Path(u'c:\\Program Files\\foo')
Path is a subclass of str so you can use it with standard filesystem functions, but it also provides alternatives for many of them:
print target.isdir() # True
numbers_dir = target.child('numbers')
print numbers_dir.exists() # False
numbers_dir.mkdir()
print numbers_dir.exists() # True
for n in range(10):
file_path = numbers_dir.child('%s.txt' % (n,))
file_path.write_file("Hello world %s!\n" % (n,), 'wt')
This is a bit tricky. For instance, the following code:
import sys
import os
z = sys.argv[0]
p = os.path.dirname(z)
f = os.path.abspath(p)
print "argv[0]={0} , dirname={1} , abspath={2}\n".format(z,p,f)
gives this output on Windows
argv[0]=../zzz.py , dirname=.. , abspath=C:\Users\michael\Downloads
First of all, notice that argv has the slash which I typed in the command python ../zzz.py and the absolute path has the normal Windows backslashes. If you need to be cross platform you should probably refrain from putting regular slashes on Python command lines, and use os.sep to refer to the character that separated pathname components.
So far I have only partly answered your question. There are a couple of ways to use the value of f to get what you want. Brute force is to use something like:
targetpath = f + os.sep + ".." + os.sep + ".."
which would result in something like C:\Users\michael\Downloads\..\.. on Windows and /home/michael/../.. on Unix. Each .. goes back one step and is the equivalent of removing the pathname component.
But you could do better by breaking up the path:
target = f.split(os.sep)
targetpath = os.sep.join(target[:-2]
and rejoining all but the last two bits to get C:\Users on Windows and / on Unix. If you do that it might be a good idea to check that there are enough pathname components to remove.
Note that I ran the program above by typing python ../xxx.py. In other words I was not in the same working directory as the script, therefore getcwd() would not be useful.

Categories

Resources