Home | History | Annotate | Download | only in Lib
      1 """Pathname and path-related operations for the Macintosh."""
      2 
      3 import os
      4 from stat import *
      5 import genericpath
      6 from genericpath import *
      7 
      8 __all__ = ["normcase","isabs","join","splitdrive","split","splitext",
      9            "basename","dirname","commonprefix","getsize","getmtime",
     10            "getatime","getctime", "islink","exists","lexists","isdir","isfile",
     11            "expanduser","expandvars","normpath","abspath",
     12            "curdir","pardir","sep","pathsep","defpath","altsep","extsep",
     13            "devnull","realpath","supports_unicode_filenames"]
     14 
     15 # strings representing various path-related bits and pieces
     16 # These are primarily for export; internally, they are hardcoded.
     17 curdir = ':'
     18 pardir = '::'
     19 extsep = '.'
     20 sep = ':'
     21 pathsep = '\n'
     22 defpath = ':'
     23 altsep = None
     24 devnull = 'Dev:Null'
     25 
     26 def _get_colon(path):
     27     if isinstance(path, bytes):
     28         return b':'
     29     else:
     30         return ':'
     31 
     32 # Normalize the case of a pathname.  Dummy in Posix, but <s>.lower() here.
     33 
     34 def normcase(path):
     35     if not isinstance(path, (bytes, str)):
     36         raise TypeError("normcase() argument must be str or bytes, "
     37                         "not '{}'".format(path.__class__.__name__))
     38     return path.lower()
     39 
     40 
     41 def isabs(s):
     42     """Return true if a path is absolute.
     43     On the Mac, relative paths begin with a colon,
     44     but as a special case, paths with no colons at all are also relative.
     45     Anything else is absolute (the string up to the first colon is the
     46     volume name)."""
     47 
     48     colon = _get_colon(s)
     49     return colon in s and s[:1] != colon
     50 
     51 
     52 def join(s, *p):
     53     try:
     54         colon = _get_colon(s)
     55         path = s
     56         if not p:
     57             path[:0] + colon  #23780: Ensure compatible data type even if p is null.
     58         for t in p:
     59             if (not path) or isabs(t):
     60                 path = t
     61                 continue
     62             if t[:1] == colon:
     63                 t = t[1:]
     64             if colon not in path:
     65                 path = colon + path
     66             if path[-1:] != colon:
     67                 path = path + colon
     68             path = path + t
     69         return path
     70     except (TypeError, AttributeError, BytesWarning):
     71         genericpath._check_arg_types('join', s, *p)
     72         raise
     73 
     74 
     75 def split(s):
     76     """Split a pathname into two parts: the directory leading up to the final
     77     bit, and the basename (the filename, without colons, in that directory).
     78     The result (s, t) is such that join(s, t) yields the original argument."""
     79 
     80     colon = _get_colon(s)
     81     if colon not in s: return s[:0], s
     82     col = 0
     83     for i in range(len(s)):
     84         if s[i:i+1] == colon: col = i + 1
     85     path, file = s[:col-1], s[col:]
     86     if path and not colon in path:
     87         path = path + colon
     88     return path, file
     89 
     90 
     91 def splitext(p):
     92     if isinstance(p, bytes):
     93         return genericpath._splitext(p, b':', altsep, b'.')
     94     else:
     95         return genericpath._splitext(p, sep, altsep, extsep)
     96 splitext.__doc__ = genericpath._splitext.__doc__
     97 
     98 def splitdrive(p):
     99     """Split a pathname into a drive specification and the rest of the
    100     path.  Useful on DOS/Windows/NT; on the Mac, the drive is always
    101     empty (don't use the volume name -- it doesn't have the same
    102     syntactic and semantic oddities as DOS drive letters, such as there
    103     being a separate current directory per drive)."""
    104 
    105     return p[:0], p
    106 
    107 
    108 # Short interfaces to split()
    109 
    110 def dirname(s): return split(s)[0]
    111 def basename(s): return split(s)[1]
    112 
    113 def ismount(s):
    114     if not isabs(s):
    115         return False
    116     components = split(s)
    117     return len(components) == 2 and not components[1]
    118 
    119 def islink(s):
    120     """Return true if the pathname refers to a symbolic link."""
    121 
    122     try:
    123         import Carbon.File
    124         return Carbon.File.ResolveAliasFile(s, 0)[2]
    125     except:
    126         return False
    127 
    128 # Is `stat`/`lstat` a meaningful difference on the Mac?  This is safe in any
    129 # case.
    130 
    131 def lexists(path):
    132     """Test whether a path exists.  Returns True for broken symbolic links"""
    133 
    134     try:
    135         st = os.lstat(path)
    136     except OSError:
    137         return False
    138     return True
    139 
    140 def expandvars(path):
    141     """Dummy to retain interface-compatibility with other operating systems."""
    142     return path
    143 
    144 
    145 def expanduser(path):
    146     """Dummy to retain interface-compatibility with other operating systems."""
    147     return path
    148 
    149 class norm_error(Exception):
    150     """Path cannot be normalized"""
    151 
    152 def normpath(s):
    153     """Normalize a pathname.  Will return the same result for
    154     equivalent paths."""
    155 
    156     colon = _get_colon(s)
    157 
    158     if colon not in s:
    159         return colon + s
    160 
    161     comps = s.split(colon)
    162     i = 1
    163     while i < len(comps)-1:
    164         if not comps[i] and comps[i-1]:
    165             if i > 1:
    166                 del comps[i-1:i+1]
    167                 i = i - 1
    168             else:
    169                 # best way to handle this is to raise an exception
    170                 raise norm_error('Cannot use :: immediately after volume name')
    171         else:
    172             i = i + 1
    173 
    174     s = colon.join(comps)
    175 
    176     # remove trailing ":" except for ":" and "Volume:"
    177     if s[-1:] == colon and len(comps) > 2 and s != colon*len(s):
    178         s = s[:-1]
    179     return s
    180 
    181 def abspath(path):
    182     """Return an absolute path."""
    183     if not isabs(path):
    184         if isinstance(path, bytes):
    185             cwd = os.getcwdb()
    186         else:
    187             cwd = os.getcwd()
    188         path = join(cwd, path)
    189     return normpath(path)
    190 
    191 # realpath is a no-op on systems without islink support
    192 def realpath(path):
    193     path = abspath(path)
    194     try:
    195         import Carbon.File
    196     except ImportError:
    197         return path
    198     if not path:
    199         return path
    200     colon = _get_colon(path)
    201     components = path.split(colon)
    202     path = components[0] + colon
    203     for c in components[1:]:
    204         path = join(path, c)
    205         try:
    206             path = Carbon.File.FSResolveAliasFile(path, 1)[0].as_pathname()
    207         except Carbon.File.Error:
    208             pass
    209     return path
    210 
    211 supports_unicode_filenames = True
    212