MrZcxfph
MrZcxfph

Reputation: 257

In Python 3, how do I convert a file:// URL to an OS path with code that works in both Linux and Windows?

Here is code that demonstrates the problem when converting a file:// URL to an OS path.

import os
import pathlib
import sys
import urllib.parse

print ("sys.argv[0]      = {0}".format(sys.argv[0]))

varFilename = sys.argv[0]
varFilename = os.path.abspath(varFilename)
print ("abs varFilename  = {0}".format(varFilename))

varMainFolder = os.path.dirname(varFilename)
print ("varMainFolder    = {0}".format(varMainFolder))

varTarget = os.path.join(varMainFolder,"test test.py")
print ("varTarget        = {0}".format(varTarget))

varURL = pathlib.Path(varTarget).as_uri()
print ("varURL           = {0}".format(varURL))

varPathRaw = urllib.parse.urlparse(varURL).path
print ("varPathRaw       = {0}".format(varPathRaw))

varPathDecode = urllib.parse.unquote(varPathRaw)
print ("varPathDecode    = {0}".format(varPathDecode))

varOSPath = os.path.normpath(varPathDecode)
print ("varOSPath        = {0}".format(varOSPath))

In Linux, this code prints:

sys.argv[0]      = test.py
abs varFilename  = /home/ldbader/test.py
varMainFolder    = /home/ldbader
varTarget        = /home/ldbader/test test.py
varURL           = file:///home/ldbader/test%20test.py
varPathRaw       = /home/ldbader/test%20test.py
varPathDecode    = /home/ldbader/test test.py
varOSPath        = /home/ldbader/test test.py

Notice the varOSPath is a perfectly valid absolute path. But in Windows, the code prints:

sys.argv[0]      = test.py
abs varFilename  = C:\mli\Junk\test.py
varMainFolder    = C:\mli\Junk
varTarget        = C:\mli\Junk\test test.py
varURL           = file:///C:/mli/Junk/test%20test.py
varPathRaw       = /C:/mli/Junk/test%20test.py
varPathDecode    = /C:/mli/Junk/test test.py
varOSPath        = \C:\mli\Junk\test test.py

Notice that varOSPath has an absolute path preceded by an invalid backslash. Attempting to open a file with this path will fail. I would have expected os.path.normpath() to discard a slash to the left of a drive specification, but it doesn't.

What should I do so the same logic gives me a valid absolute path on both platforms?

Upvotes: 5

Views: 2808

Answers (1)

MrZcxfph
MrZcxfph

Reputation: 257

Well, this is my answer. It appears to work for my scenarios, but I am not sure what will happen if a Linux user creates a folder named /C: (or any other name that looks like a Windows drive letter).

import os
import pathlib
import sys
import urllib.parse

print ("sys.argv[0]      = {0}".format(sys.argv[0]))

varFilename = sys.argv[0]
varFilename = os.path.abspath(varFilename)
print ("abs varFilename  = {0}".format(varFilename))

varMainFolder = os.path.dirname(varFilename)
print ("varMainFolder    = {0}".format(varMainFolder))

varTarget = os.path.join(varMainFolder,"test test.py")
print ("varTarget        = {0}".format(varTarget))

varURL = pathlib.Path(varTarget).as_uri()
print ("varURL           = {0}".format(varURL))

varPathRaw = urllib.parse.urlparse(varURL).path
print ("varPathRaw       = {0}".format(varPathRaw))

varPathDecode = urllib.parse.unquote(varPathRaw)
print ("varPathDecode    = {0}".format(varPathDecode))

varOSPath = os.path.normpath(varPathDecode)
print ("varOSPath        = {0}".format(varOSPath))

varFixedPath = varOSPath
varDrive = os.path.splitdrive(varFixedPath[1:])[0]
print ("varDrive         = {0}".format(varDrive))
if varDrive:
    varFixedPath = varFixedPath[1:]
print ("varFixedPath     = {0}".format(varFixedPath))

The trick is to use os.path.splitdrive() on the absolute path after removing the first character. For a Windows absolute path, it will return the drive specification. For a Linux absolute path, it will return an empty string. If the drive specification is an empty string, use the absolute path as is. If the drive specification is not an empty string, remove the first character from the absolute path and use the result.

I still believe that the os.path.normpath() method should not return an absolute Windows path with a leading backslash.

Upvotes: 2

Related Questions