1

Let's say I have a class which represents a directory (simplified example of course):

import os
class Dir:
    def __init__(self, path):
        self.path = os.path.normcase(path)

To make things easier to implement internally, I am calling os.path.normcase on the path argument before I save it into an attribute. This works great, but it lowercases the path:

>>> import os
>>> os.path.normcase(r'C:\Python34\Lib')
'c:\\python34\\lib'
>>>

I would like a way to turn the path back into its properly capitalized form of C:\Python34\Lib. I plan to do this inside the __repr__ method so that I can get nice outputs such as:

>>> my_dir
Dir(r'C:\Python34\Lib')
>>>

when I am in the interactive interpreter. Is there anything like this in the standard library?


Note: I am not referring to the string that the user supplied as the path argument. If a user does:

my_dir = Dir('c:\PYTHON34\lib')

I still want Dir('C:\Python34\Lib') to be printed in the interpreter because that is the proper capitalization. Basically, I want the outputed paths to be the same as they are in the file explorer.

  • Just curious, why do you care about the capitalization? – OozeMeister Dec 14 '14 at 02:03
  • @OozeMeister - It's just for aesthetic reasons. :) If I ever print my `Dir` objects or use them in strings, I'd like to have readable paths. It also seems more user-friendly to have the paths match what they are in explorer instead of being all lowercase. –  Dec 14 '14 at 02:05
  • @iCodez: why not then store the original path and normalise only when you need to do comparisons? – Martijn Pieters Dec 14 '14 at 02:08
  • perhaps you'd like to use the standard library `pathlib` module, which as i recall handles case-insensitive comparison for you? – Eevee Dec 14 '14 at 02:12
  • @MartijnPieters - I thought about that, but I am only really using the proper path in `__str__` and `__repr__` where as I'm using the normalized path in a lot of places. So, I didn't want to call `normcase` in each method that uses it. Also, I'd like my `Dir` class to accept paths case-instentively but to output them only in a proper, readable form. –  Dec 14 '14 at 02:16
  • [This SO post](http://stackoverflow.com/questions/2113822/python-getting-filename-case-as-stored-in-windows) looks relevant. – jme Dec 14 '14 at 02:52
  • @jme - It is indeed similar, but not exactly the same. It is asking how to get the proper casing for a file *name*. I want it for the whole path. –  Dec 14 '14 at 03:09

2 Answers2

5

Update:

For those using the newer versions of Python, the new pathlib module possesses this functionality in the form of pathlib.Path.resolve:

>>> from pathlib import Path
>>> Path(r'c:\python34\lib').resolve()
WindowsPath('C:/Python34/Lib')
>>> str(Path(r'c:\python34\lib').resolve())
'C:\\Python34\\Lib'
>>>

So, you could store the user-supplied path as a Path object:

from pathlib import Path
class Dir:
    def __init__(self, path):
        self.path = Path(path)

and then implement the __repr__ method like so:

def __repr__(self):
    return "Dir('{}')".format(self.path.resolve())

As an added bonus, we no longer need the os.path.normcase function since Path objects support case-insensitive comparisons directly.

One downside to pathlib though is that it is only available in Python 3.4 (the currently newest version). So, those using earlier versions will need to either get a backport to their version or use the os.path._getfinalpathname function as demonstrated below.


While I was digging through the standard library, I came across an undocumented function in the os.path module named _getfinalpathname:

>>> import os
>>> os.path._getfinalpathname(r'c:\python34\lib')
'\\\\?\\C:\\Python34\\Lib'
>>>

Using str.lstrip, I can get the output I need:

>>> os.path._getfinalpathname(r'c:\python34\lib').lstrip(r'\?')
'C:\\Python34\\Lib'
>>>

The only downside to this approach is that the function is undocumented and somewhat hidden. But it suits my needs for now (of course, I'd love to hear a better approach if you know of one :)

  • i was thinking more that you might want to use `pathlib.Path` _instead of_ writing a totally custom class :) also there's a backlib on pypi called `pathlib` that works on 2.6+ – Eevee Dec 14 '14 at 04:06
0

os.path.realpath

On Windows the above function will works if the matching file exists. if the file does not exist it uses the supplied case for the missing bit, relative paths are made complete and it handles stuff like '.' or '..' .

it makes all the folder separators the same (but uses windows backslash) and seems to capitalise drive letters of local drives, deal with subst etc.

I find it useful for working out what unix file a SMB client is using. Replace the separators and replace the server, share combo with the local volume and we have the file from a server point of view.