What's wrong with the way I'm joining my path here?
Everything but the first item in the list will be properly joined.
I'm grabbing a path from a filedialog in tkinter.
i.e filedialog.askdirectory()
Example Path:
PATH = "C:/MyUserName/Desktop/SomeDir"
What I'm doing:
os.path.join(*(PATH.split("/") + ["somefile.txt"]))
This will print out the following:
C:MyUserName/Desktop/SomeDir/somefile.txt
Why does it lose the first /?
I needed to convert my initial path using os.normpath I was getting a filedialog input from tkinter and then trying to use the above path style / code in the question to access / create files.
Because of the bad join / seperators on windows it was causing errors.
You are using Windows, right?
From the Docs:
On Windows, the drive letter is not reset when an absolute path component (e.g., r'\foo') is encountered. If a component contains a drive letter, all previous components are thrown away and the drive letter is reset. Note that since there is a current directory for each drive, os.path.join("c:", "foo") represents a path relative to the current directory on drive C: (c:foo), not c:\foo.
This means that c:foo is in fact a correct path. Try os.path.abspath('c:foo') and os.path.abspath('c:\\foo') to see the difference. The first path is a relative path on the c drive and the second one is an absolute path.
Windows keeps a current path for all drives. C:MyUserName/Desktop/SomeDir/somefile.txt and C:/MyUserName/Desktop/SomeDir/somefile.txt are both valid and there is no way for ntpath.join to know whether you wanted the drive-relative or drive-absolute path.
Related
I'm experiencing very strange behavior with python's os.path module. The drive letter of the working directory is treated as a relative path to the working directory itself. For example:
Using os.path.abspath
os.path.abspath('.') prints 'C:\\Users\\myuser'
os.path.abspath('C:') also prints 'C:\\Users\\myuser'
Using os.path.join
os.path.join('.','Users','myuser') gives the expected '.\\Users\\myuser'
Notice '\\' is inserted between all three entries. However:
os.path.join('C:','Users','myuser') gives 'C:Users\\myuser'
Notice the lack of '\\' being inserted between C: and Users
Using os.path.abspath with os.path.join
Despite the lack of '\\', python accepts 'C:Users' and treats it as '.\\Users' as seen here:
'os.path.abspath(os.path.join('C:','Users','myuser')) gives 'C:\\Users\\J34688\\Users\\myuser'
which is the same as
'os.path.abspath(os.path.join('.','Users','myuser')) gives 'C:\\Users\\J34688\\Users\\myuser'
Using a different drive letter
This unexpected behavior is not seen when using other drives. For example:
os.path.abspath(os.path.join('D:','Users','myuser')) gives
'D:\\Users\\myuser'
Which to me seems far more reasonable.
Conclusion
So what's going on here? Why is 'C:' treated as '.\\'?
Additional Notes
I found a workaround to force the expected behavior by using 'C:\\', which will be treated as the actual letter drive. Still, in every other situation, the '\\' is optional (e.g. '.' is equivalent to '.\\', and 'D:' is equivalent to 'D:\\').
This 'feature' holds regardless of what your current working directory is. If you cd to another directory within the C: drive, then 'C:' will refer to that new directory just as '.' does. Furthermore, if you change to a different drive (say, D:), then 'C:' will function as expected and the new letter will be take on this behavior (e.g. 'D:' is now equivalent to '.').
os.path.abspath calls GetFullPathName in the Windows API. The documentation for that states that
If you specify "U:" the path returned is the current directory on the "U:" drive
This is just how Windows handles paths, not related to Python.
The documentation for os.path.join also states
Note that since there is a current directory for each drive, os.path.join("c:", "foo") represents a path relative to the current directory on drive C: (c:foo), not c:\foo.
For reference. The absolute path is the full path to some place on your computer. The relative path is the path to some file with respect to your current working directory (PWD). For example:
Absolute path:
C:/users/admin/docs/stuff.txt
If my PWD is C:/users/admin/, then the relative path to stuff.txt would be: docs/stuff.txt
Note, PWD + relative path = absolute path.
Cool, awesome. Now, I wrote some scripts which check if a file exists.
os.chdir("C:/users/admin/docs")
os.path.exists("stuff.txt")
This returns TRUE if stuff.txt exists and it works.
Now, instead if I write,
os.path.exists("C:/users/admin/docs/stuff.txt")
This also returns TRUE.
Is there a definite time when we need to use one over the other? Is there a methodology for how python looks for paths? Does it try one first then the other?
Thanks!
If you don't know where the user will be executing the script from, it is best to compute the absolute path on the user's system using os and __file__.
__file__ is a global variable set on every Python script that returns the relative path to the *.py file that contains it.
import os
my_absolute_dirpath = os.path.abspath(os.path.dirname(__file__))
The biggest consideration is probably portability. If you move your code to a different computer and you need to access some other file, where will that other file be? If it will be in the same location relative to your program, use a relative address. If it will be in the same absolute location, use an absolute address.
I made a script in the past to mass rename any file greater than x characters in a directory. When I made that script I had a source directory which you would need to input manually. Any file that was over x characters in that directory would be stripped of it's extension, renamed, then the extension would be re added and it would use os.path.join to join the source and the newly created filename+ext. I'm now making another script and used os.path.join("Folder in the current dir", "file in that dir"). Because this worked I'm guessing that when os.path.join is called with just a foldername and no full path in it's first parameter it starts it's search from the directory that the script it was run in? Just wondering if this is correct.
os.path.join has nothing to do with any actual filesystem, and does not "start" anywhere. It simply joins two arbitrary paths, whether they exist or not.
What os.path.join does is to just join path elements the system-compatible way, taking into effect the particular directory separator character, etc., into account. It's a simple string manipulation tool.
So the returned result simply starts from whatever you give to it as the first argument.
I'm new to Python and I'm trying to access a file with a full path represented by the following:
'X:/01 File Folder/MorePath/Data/Test/myfile.txt'
Every time I try to build the full string using os.path.join, it ends up slicing out everything between the drive letter and the second path string, like so:
import os
basePath = 'X:/01 File Folder/MorePath'
restofPath = '/Data/Test/myfile.txt'
fullPath = os.path.join(basePath,restofPath)
gives me:
'X:/Data/Test/myfile.txt'
as the fullPath name.
Can anyone tell me what I'm doing wrong? Does it have something to do with the digits near the beginning of the base path name?
The / at the beginning of your restofPath means "start at the root directory." So os.path.join() helpfully does that for you.
If you don't want it to do that, write your restofPath as a relative directory, i.e., Data/Test/myfile.txt, rather than an absolute one.
If you are getting your restofPath from somewhere outside your program (user input, config file, etc.), and you always want to treat it as relative even if the user is so gauche as to start the path with a slash, you can use restofPath.lstrip(r"\/").
I would like to change the cwd to a specific folder.
The folder name is known; however, the path to it will vary.
I am attempting the following but cannot seem to get what I am looking for:
absolute_path = os.path.abspath(folder_name)
directory_path = os.path.dirname(absolute_path)
os.chdir(directory_path)
This does not do what I'm looking for because it is keeping the original cwd to where the .py file is run from. I've tried adding os.chdir(os.path.expanduser("~")) prior to the first code block; however, it just creates the absolute_path to /home/user/folder_name.
Of course if there is a simple import that I could use, I'll be open to anything.
What would be the correct way to get the paths of all folders with with a specific name?
def find_folders(start_path,needle):
for cwd, folders, files,in os.walk(start_path):
if needle in folders:
yield os.path.join(cwd,needle)
for path in find_folders("/","a_folder_named_x"):
print path
all this is doing is walking down your directory structure from a given start path and finding all occurances of a folder named needle
in the example it is starting at the root folder of the system and looking for a folder named "a_folder_named_x" ... be forwarned this could take a while to run if you need to search the whole system ...
You need to understand that abspath accepts a relative pathname (which might just be a filename), and gives you the equivalent absolute (full) pathname. A relative pathname is one that begins in your current directory; no searching is involved, and so it always points to one place (which may or may not exist).
What you actually need is to search down a directory tree, starting at ~ or whatever directory makes sense in your case, until you find a folder with the requested name. That's what #Joran's code does.