So I have a path to a server that looks like
\\foo\bar\baz
I am trying to convert this to a Path object with Python's pathlib library with something like
Path('\\foo').joinpath('bar').joinpath('baz')
Trying to see if this path exists with "path.exists()" however signals that the path doesn't exist.
I have also replace the starting path of the path with the following the same result:
Path(r'\\foo'), Path('\\\\foo'), Path('\foo)
Printing out the path after being parsed by Path appears to show the path being interpreted as, no matter how the foo root is entered
'C:/foo/bar/baz'
However when I simply plop the path in os.path.exists as a raw string, the path is successfully found (using a normal string results in a unicode error)
os.path.exists(r'\\foo\bar\baz')
Is there anyway to use Pathlib in such a way that the server path is successfully parsed / interpreted, or am I forced to just use a raw string with os.path operations?
r'\\foo' is just not a valid windows UNC path, even if foo exists. You need at least a sharename after that.
From Microsoft documentation:
A valid UNC path MUST contain two or more path components.
This creates a valid path object on which exists() will work properly:
>>> pathlib.Path(r'\\foo\bar').joinpath('baz')
WindowsPath('//foo/bar/baz')
Related
I'h have an executable named "MyCamelCase.exe" in the current python script directory and in a subfolder "MyFolder". Additionally, in "MyFolder" there is another executable "DontWannaFindThis.exe". I'd like to find all occurences of "MyCamelCase.exe" in the current directory and all subfolders. Therefore, I'm using Path.rglob(pattern):
from pathlib import Path
if __name__ == '__main__':
[print(f) for f in Path.cwd().rglob('MyCamelCase.exe')]
[print(f) for f in Path.cwd().rglob('.\MyCamelCase.exe')]
[print(f) for f in Path.cwd().rglob('*.exe')]
This code leads to the following output:
D:\PyTesting\mycamelcase.exe
D:\PyTesting\MyFolder\mycamelcase.exe
D:\PyTesting\mycamelcase.exe
D:\PyTesting\MyFolder\mycamelcase.exe
D:\PyTesting\MyCamelCase.exe
D:\PyTesting\MyFolder\DontWannaFindThis.exe
D:\PyTesting\MyFolder\MyCamelCase.exe
Why does rglob returns a string with only lower case if a provide the full file name and on the other hand return a string containing the original notation when using a pattern with '.*'?
Note: The same happens when using Path.glob()
This is because all paths on Windows are case insensitive (in fact, before Windows 10 there was no way to make Windows case sensitive). For some reason, when looking for an exact match, pathlib makes the path lowercase in Windows. When it is doing normal globbing with *, it takes whatever the normal representation is from Windows.
The casing not matching in Windows should not matter though, and it will not if the only consumer of the information is the computer itself when it is processing the files.
As it have already been answered, this is the correct behaviour on Windows. However, if you want to display the full path without discarding the case, the resolve() method may be useful.
E.g.
from pathlib import Path
if __name__ == '__main__':
[print(f) for f in Path.cwd().rglob('MyCamelCase.exe').resolve()]
[print(f) for f in Path.cwd().rglob('.\MyCamelCase.exe').resolve()]
[print(f) for f in Path.cwd().rglob('*.exe').resolve()]
Note that .resolve() is normally used to convert relative and/or symlinked paths to absolute ones.
While creating a program that backs up my files, I found that os.path.basename() was not working consistently. For example:
import os
folder = '\\\\server\\studies\\backup\\backup_files'
os.path.basename(folder)
returns 'backup_files'
folder = '\\\\server\\studies'
os.path.basename(folder)
returns ''
I want that second basename function to return 'studies' but it returns an empty string. I ran os.path.split(folder) to see how it's splitting the string and it turns out it's considering the entire path to be the directory, i.e. ('\\\\server\\studies', ' ').
I can't figure out how to get around it.. The weirdest thing is I ran the same line earlier and it worked, but it won't anymore! Does it have something to do with the very first part being a shared folder on the network drive?
that looks like a Windows UNC specificity
UNC paths can be seen as equivalent of unix path, only with double backslashes at the start.
A workaround would be to use classical rsplit:
>>> r"\\server\studies".rsplit(os.sep,1)[-1]
'studies'
Fun fact: with 3 paths it works properly:
>>> os.path.basename(r"\\a\b\c")
'c'
Now why this? let's check the source code of ntpath on windows:
def basename(p):
"""Returns the final component of a pathname"""
return split(p)[1]
okay now split
def split(p):
seps = _get_bothseps(p)
d, p = splitdrive(p)
now splitdrive
def splitdrive(p):
"""Split a pathname into drive/UNC sharepoint and relative path specifiers.
Returns a 2-tuple (drive_or_unc, path); either part may be empty.
Just reading the documentation makes us understand what's going on.
A Windows sharepoint has to contain 2 path parts:
\\server\shareroot
So \\server\studies is seen as the drive, and the path is empty. Doesn't happen when there are 3 parts in the path.
Note that it's not a bug, since it's not possible to use \\server like a normal directory, create dirs below, etc...
Note that the official documentation for os.path.basename doesn't mention that (because os.path calls ntpath behind the scenes) but it states:
Return the base name of pathname path. This is the second element of the pair returned by passing path to the function split(). Note that the result of this function is different from the Unix basename program
That last emphasised part at least is true! (and the documentation for os.path.split() doesn't mention that issue or even talks about windows)
python has recently added the pathlib module (which i like a lot!).
there is just one thing i'm struggling with: is it possible to normalize a path to a file or directory that does not exist? i can do that perfectly well with os.path.normpath. but wouldn't it be absurd to have to use something other than the library that should take care of path related stuff?
the functionality i would like to have is this:
from os.path import normpath
from pathlib import Path
pth = Path('/tmp/some_directory/../i_do_not_exist.txt')
pth = Path(normpath(str(pth)))
# -> /tmp/i_do_not_exist.txt
but without having to resort to os.path and without having to type-cast to str and back to Path. also pth.resolve() does not work for non-existing files.
is there a simple way to do that with just pathlib?
is it possible to normalize a path to a file or directory that does not exist?
Starting from 3.6, it's the default behavior. See https://docs.python.org/3.6/library/pathlib.html#pathlib.Path.resolve
Path.resolve(strict=False)
...
If strict is False, the path is resolved as far as possible and any remainder is appended without checking whether it exists
As of Python 3.5: No, there's not.
PEP 0428 states:
Path resolution
The resolve() method makes a path absolute, resolving
any symlink on the way (like the POSIX realpath() call). It is the
only operation which will remove " .. " path components. On Windows,
this method will also take care to return the canonical path (with the
right casing).
Since resolve() is the only operation to remove the ".." components, and it fails when the file doesn't exist, there won't be a simple means using just pathlib.
Also, the pathlib documentation gives a hint as to why:
Spurious slashes and single dots are collapsed, but double dots ('..')
are not, since this would change the meaning of a path in the face of
symbolic links:
PurePath('foo//bar') produces PurePosixPath('foo/bar')
PurePath('foo/./bar') produces PurePosixPath('foo/bar')
PurePath('foo/../bar') produces PurePosixPath('foo/../bar')
(a naïve approach would make PurePosixPath('foo/../bar') equivalent to PurePosixPath('bar'), which is wrong if foo is a symbolic link to another directory)
All that said, you could create a 0 byte file at the location of your path, and then it'd be possible to resolve the path (thus eliminating the ..). I'm not sure that's any simpler than your normpath approach, though.
If this fits you usecase(e.g. ifle's directory already exists) you might try to resolve path's parent and then re-append file name, e.g.:
from pathlib import Path
p = Path()/'hello.there'
print(p.parent.resolve()/p.name)
Old question, but here is another solution in particular if you want POSIX paths across the board (like nix paths on Windows too).
I found pathlib resolve() to be broken as of Python 3.10, and this method is not currently exposed by PurePosixPath.
What I found worked was to use posixpath.normpath(). Also found PurePosixPath.joinpath() to be broken. I.E. It will not join ".." with "myfile.txt" as expected. It will return just "myfile.txt". But posixpath.join() works perfectly; will return "../myfile.txt".
Note this is in path strings, but easily back to pathlib.Path(my_posix_path) et al for an OOP container.
And easily transpose to Windows platform paths too by just constructing this way, as the module takes care of the platform independence for you.
Might be the solution for others with Python file path woes..
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 have a script in Python which i get 3 arguments from the user
One of the arguments is a folder path in which there are some files i need to use
Since my program is designed for all OS, i would like to know how to use the path from the argument correctly to get to my required files
I.E.
if i get the following path :
c:\windows
i would like to be able to get 1.exe in this folder,
In windows it will be slash or backslash but in unix systems it will be probably different,
As i understand there is a const or defined var from 'os' module in which i can use as this subdir sign, where can i find it ?
Thank you
Just use os.path.join and Python will take care of the slash for you:
path = os.path.join(sys.argv[1], '1.exe')
The platform-specific path separator is stored as os.sep.
Use os.path.join(). For example:
os.path.join(dirname, "1.exe")