module ntpath common operations on winntwin95 pathnames common pathname manipulations windowsnt95 version instead of importing this module directly import os and refer to this module as os path strings representing various pathrelated bits and pieces these are primarily for export internally they are hardcoded should be set before imports for resolving cyclic dependency normalize the case of a pathname and map slashes to backslashes other normalizations such as optimizing away are not done this is done by normpath normalize case of pathname makes all characters lowercase and all slashes into backslashes normalize case of pathname makes all characters lowercase and all slashes into backslashes return whether a path is absolute trivial in posix harder on windows for windows it is absolute if it starts with a slash or backslash current volume or if a pathname after the volumeletterandcolon or uncresource starts with a slash or backslash test whether a path is absolute s os fspaths if isinstances bytes sep b altsep b colonsep b else sep altsep colonsep s s 3 replacealtsep sep absolute unc device and paths with a drive and root legacy bug isabsx should be false since the path has no drive if s startswithsep or s startswithcolonsep 1 return true return false join two or more paths def joinpath paths path os fspathpath if isinstancepath bytes sep b seps b colon b else sep seps colon try if not paths path 0 sep 23780 ensure compatible data type even if p is null resultdrive resultroot resultpath splitrootpath for p in mapos fspath paths pdrive proot ppath splitrootp if proot second path is absolute if pdrive or not resultdrive resultdrive pdrive resultroot proot resultpath ppath continue elif pdrive and pdrive resultdrive if pdrive lower resultdrive lower different drives ignore the first path entirely resultdrive pdrive resultroot proot resultpath ppath continue same drive in different case resultdrive pdrive second path is relative to the first if resultpath and resultpath1 not in seps resultpath resultpath sep resultpath resultpath ppath add separator between unc and nonabsolute path if resultpath and not resultroot and resultdrive and resultdrive1 not in colon seps return resultdrive sep resultpath return resultdrive resultroot resultpath except typeerror attributeerror byteswarning genericpath checkargtypes join path paths raise split a path in a drive specification a drive letter followed by a colon and the path specification it is always true that drivespec pathspec p def splitdrivep drive root tail splitrootp return drive root tail def splitrootp p os fspathp if isinstancep bytes sep b altsep b colon b uncprefix b unc empty b else sep altsep colon uncprefix unc empty normp p replacealtsep sep if normp 1 sep if normp1 2 sep unc drives e g servershare or uncservershare device drives e g device or device start 8 if normp 8 upper uncprefix else 2 index normp findsep start if index 1 return p empty empty index2 normp findsep index 1 if index2 1 return p empty empty return p index2 pindex2 index2 1 pindex2 1 else relative path with root e g windows return empty p 1 p1 elif normp1 2 colon if normp2 3 sep absolute driveletter path e g x windows return p 2 p2 3 p3 else relative path with drive e g x windows return p 2 empty p2 else relative path e g windows return empty empty p split a path in head everything up to the last and tail the rest after the trailing is stripped the invariant joinhead tail p holds the resulting head won t end in unless it is the root def splitp set i to index beyond p s last slash split a path in root and extension the extension is everything starting at the last dot in the last pathname component the root is everything before that it is always true that root ext p return the tail basename part of a path returns the final component of a pathname return splitp1 return the head dirname part of a path def dirnamep is a path a junction test whether a path is a junction try st os lstatpath except oserror valueerror attributeerror return false return boolst streparsetag stat ioreparsetagmountpoint else def isjunctionpath being true for dangling symbolic links is also useful test whether a path exists returns true for broken symbolic links try st os lstatpath except oserror valueerror return false return true is a path a mount point any drive letter root eg c any share unc eg servershare any volume mounted on a filesystem folder no one method detects all three situations historically we ve lexically detected drive letter roots and share uncs the canonical approach to detecting mounted volumes querying the reparse tag fails for the most common case drive letter roots the alternative which uses getvolumepathname fails if the drive letter is the result of a subst try from nt import getvolumepathname except importerror getvolumepathname none def ismountpath expand paths beginning with or user means home user means that user s home directory if the path doesn t begin with or if the user or home is unknown the path is returned unchanged leaving error reporting to whatever function is called with the expanded path as argument see also module glob for expansion of and in pathnames a function should also be defined to do full shstyle environment variable expansion expand and user constructs if user or home is unknown do nothing path os fspathpath if isinstancepath bytes tilde b else tilde if not path startswithtilde return path i n 1 lenpath while i n and pathi not in getbothsepspath i 1 if userprofile in os environ userhome os environ userprofile elif not homepath in os environ return path else try drive os environ homedrive except keyerror drive userhome joindrive os environ homepath if i 1 user targetuser path1 i if isinstancetargetuser bytes targetuser os fsdecodetargetuser currentuser os environ get username if targetuser currentuser try to guess user home directory by default all user profile directories are located in the same place and are named by corresponding usernames if userhome isn t a normal profile directory this guess is likely wrong so we bail out if currentuser basenameuserhome return path userhome joindirnameuserhome targetuser if isinstancepath bytes userhome os fsencodeuserhome return userhome pathi expand paths containing shell variable substitutions the following rules apply no expansion within single quotes is translated into is translated into if are not seen in var1var2 varname is accepted varname is accepted varname is accepted varnames can be made out of letters digits and the characters though is not verified in the varname and varname cases xxx with command com you can use any characters in a variable name xxx except def expandvarspath normalize a path e g ab a b and afoo b all become ab previously this function also truncated pathnames to 83 format but as this module is called ntpath that s obviously wrong normalize path eliminating double slashes etc path os fspathpath if isinstancepath bytes sep b altsep b curdir b pardir b else sep altsep curdir pardir path path replacealtsep sep drive root path splitrootpath prefix drive root comps path splitsep i 0 while i lencomps if not compsi or compsi curdir del compsi elif compsi pardir if i 0 and compsi1 pardir del compsi1 i1 i 1 elif i 0 and root del compsi else i 1 else i 1 if the path is now empty substitute if not prefix and not comps comps appendcurdir return prefix sep joincomps else def normpathpath return the absolute version of a path as a fallback function in case nt getfullpathname is not available or raises oserror see bpo31047 for more return an absolute path return the absolute version of a path try return getfullpathnamenormpathpath except oserror valueerror return abspathfallbackpath try from nt import findfirstfile getfinalpathname readlink as ntreadlink except importerror realpath is a noop on systems without getfinalpathname support realpath abspath else def readlinkdeeppath these error codes indicate that we should stop reading links and return the path we currently have 1 errorinvalidfunction 2 errorfilenotfound 3 errordirectorynotfound 5 erroraccessdenied 21 errornotready implies drive with no media 32 errorsharingviolation probably an ntfs paging file 50 errornotsupported implies no support for reparse points 67 errorbadnetname implies remote server unavailable 87 errorinvalidparameter 4390 errornotareparsepoint 4392 errorinvalidreparsedata 4393 errorreparsetaginvalid allowedwinerror 1 2 3 5 21 32 50 67 87 4390 4392 4393 seen set while normcasepath not in seen seen addnormcasepath try oldpath path path ntreadlinkpath links may be relative so resolve them against their own location if not isabspath if it s something other than a symlink we don t know what it s actually going to be resolved against so just return the old path if not islinkoldpath path oldpath break path normpathjoindirnameoldpath path except oserror as ex if ex winerror in allowedwinerror break raise except valueerror stop on reparse points that are not symlinks break return path def getfinalpathnamenonstrictpath these error codes indicate that we should stop resolving the path and return the value we currently have 1 errorinvalidfunction 2 errorfilenotfound 3 errordirectorynotfound 5 erroraccessdenied 21 errornotready implies drive with no media 32 errorsharingviolation probably an ntfs paging file 50 errornotsupported 53 errorbadnetpath 65 errornetworkaccessdenied 67 errorbadnetname implies remote server unavailable 87 errorinvalidparameter 123 errorinvalidname 161 errorbadpathname 1920 errorcantaccessfile 1921 errorcantresolvefilename implies unfollowable symlink allowedwinerror 1 2 3 5 21 32 50 53 65 67 87 123 161 1920 1921 nonstrict algorithm is to find as much of the target directory as we can and join the rest tail path 0 while path try path getfinalpathnamepath return joinpath tail if tail else path except oserror as ex if ex winerror not in allowedwinerror raise try the os could not resolve this path fully so we attempt to follow the link ourselves if we succeed join the tail and return newpath readlinkdeeppath if newpath path return joinnewpath tail if tail else newpath except oserror if we fail to readlink let s keep traversing pass if we get these errors try to get the real name of the file without accessing it if ex winerror in 1 5 32 50 87 1920 1921 try name findfirstfilepath path splitpath except oserror path name splitpath else path name splitpath if path and not name return path tail tail joinname tail if tail else name return tail def realpathpath strictfalse path normpathpath if isinstancepath bytes prefix b uncprefix b unc newuncprefix b cwd os getcwdb bpo38081 special case for realpathb nul if normcasepath normcaseos fsencodedevnull return b nul else prefix uncprefix unc newuncprefix cwd os getcwd bpo38081 special case for realpath nul if normcasepath normcasedevnull return nul hadprefix path startswithprefix if not hadprefix and not isabspath path joincwd path try path getfinalpathnamepath initialwinerror 0 except valueerror as ex gh106242 raised for embedded null characters in strict mode we convert into an oserror nonstrict mode returns the path asis since we ve already made it absolute if strict raise oserrorstrex from none path normpathpath except oserror as ex if strict raise initialwinerror ex winerror path getfinalpathnamenonstrictpath the path returned by getfinalpathname will always start with strip off that prefix unless it was already provided on the original path if not hadprefix and path startswithprefix for unc paths the prefix will actually be unc handle that case as well if path startswithuncprefix spath newuncprefix pathlenuncprefix else spath pathlenprefix ensure that the nonprefixed path resolves to the same path try if getfinalpathnamespath path path spath except valueerror as ex unexpected as an invalid path should not have gained a prefix at any point but we ignore this error just in case pass except oserror as ex if the path does not exist and originally did not exist then strip the prefix anyway if ex winerror initialwinerror path spath return path all supported version have unicode filename support supportsunicodefilenames true def relpathpath startnone work out how much of the filepath is shared by start and path return the longest common subpath of the sequence of paths given as input the function is caseinsensitive and separatorinsensitive i e if the only difference between two paths is the use of versus as separator they are deemed to be equal however the returned path will have the standard separator even if the given paths had the alternative separator and will have the case of the first path given in the sequence additionally any trailing separator is stripped from the returned path given a sequence of path names returns the longest common subpath if not paths raise valueerror commonpath arg is an empty sequence paths tuplemapos fspath paths if isinstancepaths0 bytes sep b altsep b curdir b else sep altsep curdir try drivesplits splitrootp replacealtsep sep lower for p in paths splitpaths p splitsep for d r p in drivesplits if lenr for d r p in drivesplits 1 raise valueerrorcan t mix absolute and relative paths check that all drive letters or unc paths match the check is made only now otherwise type errors for mixing strings and bytes would not be caught if lend for d r p in drivesplits 1 raise valueerrorpaths don t have the same drive drive root path splitrootpaths0 replacealtsep sep common path splitsep common c for c in common if c and c curdir splitpaths c for c in s if c and c curdir for s in splitpaths s1 minsplitpaths s2 maxsplitpaths for i c in enumerates1 if c s2i common common i break else common common lens1 return drive root sep joincommon except typeerror attributeerror genericpath checkargtypes commonpath paths raise try the isdir isfile islink and exists implementations in genericpath use os stat this is overkill on windows use simpler builtin functions if they are available from nt import pathisdir as isdir from nt import pathisfile as isfile from nt import pathislink as islink from nt import pathexists as exists except importerror use genericpath as imported above pass try from nt import pathisdevdrive except importerror def isdevdrivepath never a dev drive determines whether the specified path is on a windows dev drive try return pathisdevdriveabspathpath except oserror return false module ntpath common operations on winnt win95 pathnames common pathname manipulations windowsnt 95 version instead of importing this module directly import os and refer to this module as os path strings representing various path related bits and pieces these are primarily for export internally they are hardcoded should be set before imports for resolving cyclic dependency normalize the case of a pathname and map slashes to backslashes other normalizations such as optimizing away are not done this is done by normpath normalize case of pathname makes all characters lowercase and all slashes into backslashes normalize case of pathname makes all characters lowercase and all slashes into backslashes return whether a path is absolute trivial in posix harder on windows for windows it is absolute if it starts with a slash or backslash current volume or if a pathname after the volume letter and colon or unc resource starts with a slash or backslash test whether a path is absolute absolute unc device and paths with a drive and root legacy bug isabs x should be false since the path has no drive join two or more paths 23780 ensure compatible data type even if p is null second path is absolute different drives ignore the first path entirely same drive in different case second path is relative to the first add separator between unc and non absolute path split a path in a drive specification a drive letter followed by a colon and the path specification it is always true that drivespec pathspec 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 if you assign result splitdrive p it is always true that result 0 result 1 p if the path contained a drive letter drive_or_unc will contain everything up to and including the colon e g splitdrive c dir returns c dir if the path contained a unc path the drive_or_unc will contain the host name and share up to but not including the fourth directory separator character e g splitdrive host computer dir returns host computer dir paths cannot contain both a drive letter and a unc path split a pathname into drive root and tail the drive is defined exactly as in splitdrive on windows the root may be a single path separator or an empty string the tail contains anything after the root for example splitroot server share server share splitroot c users barney c users barney splitroot c spam ham c spam ham splitroot windows notepad windows notepad unc drives e g server share or unc server share device drives e g device or device relative path with root e g windows absolute drive letter path e g x windows relative path with drive e g x windows relative path e g windows split a path in head everything up to the last and tail the rest after the trailing is stripped the invariant join head tail p holds the resulting head won t end in unless it is the root split a pathname return tuple head tail where tail is everything after the final slash either part may be empty set i to index beyond p s last slash now tail has no slashes split a path in root and extension the extension is everything starting at the last dot in the last pathname component the root is everything before that it is always true that root ext p return the tail basename part of a path returns the final component of a pathname return the head dirname part of a path returns the directory component of a pathname is a path a junction test whether a path is a junction test whether a path is a junction being true for dangling symbolic links is also useful test whether a path exists returns true for broken symbolic links is a path a mount point any drive letter root eg c any share unc eg server share any volume mounted on a filesystem folder no one method detects all three situations historically we ve lexically detected drive letter roots and share uncs the canonical approach to detecting mounted volumes querying the reparse tag fails for the most common case drive letter roots the alternative which uses getvolumepathname fails if the drive letter is the result of a subst test whether a path is a mount point a drive root the root of a share or a mounted volume expand paths beginning with or user means home user means that user s home directory if the path doesn t begin with or if the user or home is unknown the path is returned unchanged leaving error reporting to whatever function is called with the expanded path as argument see also module glob for expansion of and in pathnames a function should also be defined to do full sh style environment variable expansion expand and user constructs if user or home is unknown do nothing user try to guess user home directory by default all user profile directories are located in the same place and are named by corresponding usernames if userhome isn t a normal profile directory this guess is likely wrong so we bail out expand paths containing shell variable substitutions the following rules apply no expansion within single quotes is translated into is translated into if are not seen in var1 var2 varname is accepted varname is accepted varname is accepted varnames can be made out of letters digits and the characters _ though is not verified in the varname and varname cases xxx with command com you can use any characters in a variable name xxx except expand shell variables of the forms var var and var unknown variables are left unchanged no expansion within single quotes variable or variable or normalize a path e g a b a b and a foo b all become a b previously this function also truncated pathnames to 8 3 format but as this module is called ntpath that s obviously wrong normalize path eliminating double slashes etc if the path is now empty substitute normalize path eliminating double slashes etc return the absolute version of a path as a fallback function in case nt _getfullpathname is not available or raises oserror see bpo 31047 for more return an absolute path not running on windows mock up something sensible use native windows method on windows return the absolute version of a path realpath is a no op on systems without _getfinalpathname support these error codes indicate that we should stop reading links and return the path we currently have 1 error_invalid_function 2 error_file_not_found 3 error_directory_not_found 5 error_access_denied 21 error_not_ready implies drive with no media 32 error_sharing_violation probably an ntfs paging file 50 error_not_supported implies no support for reparse points 67 error_bad_net_name implies remote server unavailable 87 error_invalid_parameter 4390 error_not_a_reparse_point 4392 error_invalid_reparse_data 4393 error_reparse_tag_invalid links may be relative so resolve them against their own location if it s something other than a symlink we don t know what it s actually going to be resolved against so just return the old path stop on reparse points that are not symlinks these error codes indicate that we should stop resolving the path and return the value we currently have 1 error_invalid_function 2 error_file_not_found 3 error_directory_not_found 5 error_access_denied 21 error_not_ready implies drive with no media 32 error_sharing_violation probably an ntfs paging file 50 error_not_supported 53 error_bad_netpath 65 error_network_access_denied 67 error_bad_net_name implies remote server unavailable 87 error_invalid_parameter 123 error_invalid_name 161 error_bad_pathname 1920 error_cant_access_file 1921 error_cant_resolve_filename implies unfollowable symlink non strict algorithm is to find as much of the target directory as we can and join the rest the os could not resolve this path fully so we attempt to follow the link ourselves if we succeed join the tail and return if we fail to readlink let s keep traversing if we get these errors try to get the real name of the file without accessing it bpo 38081 special case for realpath b nul bpo 38081 special case for realpath nul gh 106242 raised for embedded null characters in strict mode we convert into an oserror non strict mode returns the path as is since we ve already made it absolute the path returned by _getfinalpathname will always start with strip off that prefix unless it was already provided on the original path for unc paths the prefix will actually be unc handle that case as well ensure that the non prefixed path resolves to the same path unexpected as an invalid path should not have gained a prefix at any point but we ignore this error just in case if the path does not exist and originally did not exist then strip the prefix anyway all supported version have unicode filename support return a relative version of a path work out how much of the filepath is shared by start and path return the longest common sub path of the sequence of paths given as input the function is case insensitive and separator insensitive i e if the only difference between two paths is the use of versus as separator they are deemed to be equal however the returned path will have the standard separator even if the given paths had the alternative separator and will have the case of the first path given in the sequence additionally any trailing separator is stripped from the returned path given a sequence of path names returns the longest common sub path check that all drive letters or unc paths match the check is made only now otherwise type errors for mixing strings and bytes would not be caught the isdir isfile islink and exists implementations in genericpath use os stat this is overkill on windows use simpler builtin functions if they are available use genericpath as imported above determines whether the specified path is on a windows dev drive never a dev drive determines whether the specified path is on a windows dev drive
curdir = '.'
pardir = '..'
extsep = '.'
sep = '\\'
pathsep = ';'
altsep = '/'
defpath = '.;C:\\bin'
devnull = 'nul'

import os
import sys
import stat
import genericpath
from genericpath import *

__all__ = ["normcase","isabs","join","splitdrive","splitroot","split","splitext",
           "basename","dirname","commonprefix","getsize","getmtime",
           "getatime","getctime", "islink","exists","lexists","isdir","isfile",
           "ismount", "expanduser","expandvars","normpath","abspath",
           "curdir","pardir","sep","pathsep","defpath","altsep",
           "extsep","devnull","realpath","supports_unicode_filenames","relpath",
           "samefile", "sameopenfile", "samestat", "commonpath", "isjunction"]

def _get_bothseps(path):
    if isinstance(path, bytes):
        return b'\\/'
    else:
        return '\\/'





try:
    from _winapi import (
        LCMapStringEx as _LCMapStringEx,
        LOCALE_NAME_INVARIANT as _LOCALE_NAME_INVARIANT,
        LCMAP_LOWERCASE as _LCMAP_LOWERCASE)

    def normcase(s):
        
        s = os.fspath(s)
        if not s:
            return s
        if isinstance(s, bytes):
            encoding = sys.getfilesystemencoding()
            s = s.decode(encoding, 'surrogateescape').replace('/', '\\')
            s = _LCMapStringEx(_LOCALE_NAME_INVARIANT,
                               _LCMAP_LOWERCASE, s)
            return s.encode(encoding, 'surrogateescape')
        else:
            return _LCMapStringEx(_LOCALE_NAME_INVARIANT,
                                  _LCMAP_LOWERCASE,
                                  s.replace('/', '\\'))
except ImportError:
    def normcase(s):
        
        s = os.fspath(s)
        if isinstance(s, bytes):
            return os.fsencode(os.fsdecode(s).replace('/', '\\').lower())
        return s.replace('/', '\\').lower()








def isabs(s):
    
    s = os.fspath(s)
    if isinstance(s, bytes):
        sep = b'\\'
        altsep = b'/'
        colon_sep = b':\\'
    else:
        sep = '\\'
        altsep = '/'
        colon_sep = ':\\'
    s = s[:3].replace(altsep, sep)
    
    
    if s.startswith(sep) or s.startswith(colon_sep, 1):
        return True
    return False



def join(path, *paths):
    path = os.fspath(path)
    if isinstance(path, bytes):
        sep = b'\\'
        seps = b'\\/'
        colon = b':'
    else:
        sep = '\\'
        seps = '\\/'
        colon = ':'
    try:
        if not paths:
            path[:0] + sep  
        result_drive, result_root, result_path = splitroot(path)
        for p in map(os.fspath, paths):
            p_drive, p_root, p_path = splitroot(p)
            if p_root:
                
                if p_drive or not result_drive:
                    result_drive = p_drive
                result_root = p_root
                result_path = p_path
                continue
            elif p_drive and p_drive != result_drive:
                if p_drive.lower() != result_drive.lower():
                    
                    result_drive = p_drive
                    result_root = p_root
                    result_path = p_path
                    continue
                
                result_drive = p_drive
            
            if result_path and result_path[-1] not in seps:
                result_path = result_path + sep
            result_path = result_path + p_path
        
        if (result_path and not result_root and
            result_drive and result_drive[-1:] not in colon + seps):
            return result_drive + sep + result_path
        return result_drive + result_root + result_path
    except (TypeError, AttributeError, BytesWarning):
        genericpath._check_arg_types('join', path, *paths)
        raise





def splitdrive(p):
    
    drive, root, tail = splitroot(p)
    return drive, root + tail


def splitroot(p):
    
    p = os.fspath(p)
    if isinstance(p, bytes):
        sep = b'\\'
        altsep = b'/'
        colon = b':'
        unc_prefix = b'\\\\?\\UNC\\'
        empty = b''
    else:
        sep = '\\'
        altsep = '/'
        colon = ':'
        unc_prefix = '\\\\?\\UNC\\'
        empty = ''
    normp = p.replace(altsep, sep)
    if normp[:1] == sep:
        if normp[1:2] == sep:
            
            
            start = 8 if normp[:8].upper() == unc_prefix else 2
            index = normp.find(sep, start)
            if index == -1:
                return p, empty, empty
            index2 = normp.find(sep, index + 1)
            if index2 == -1:
                return p, empty, empty
            return p[:index2], p[index2:index2 + 1], p[index2 + 1:]
        else:
            
            return empty, p[:1], p[1:]
    elif normp[1:2] == colon:
        if normp[2:3] == sep:
            
            return p[:2], p[2:3], p[3:]
        else:
            
            return p[:2], empty, p[2:]
    else:
        
        return empty, empty, p







def split(p):
    
    p = os.fspath(p)
    seps = _get_bothseps(p)
    d, r, p = splitroot(p)
    
    i = len(p)
    while i and p[i-1] not in seps:
        i -= 1
    head, tail = p[:i], p[i:]  
    return d + r + head.rstrip(seps), tail







def splitext(p):
    p = os.fspath(p)
    if isinstance(p, bytes):
        return genericpath._splitext(p, b'\\', b'/', b'.')
    else:
        return genericpath._splitext(p, '\\', '/', '.')
splitext.__doc__ = genericpath._splitext.__doc__




def basename(p):
    
    return split(p)[1]




def dirname(p):
    
    return split(p)[0]




if hasattr(os.stat_result, 'st_reparse_tag'):
    def isjunction(path):
        
        try:
            st = os.lstat(path)
        except (OSError, ValueError, AttributeError):
            return False
        return bool(st.st_reparse_tag == stat.IO_REPARSE_TAG_MOUNT_POINT)
else:
    def isjunction(path):
        
        os.fspath(path)
        return False




def lexists(path):
    
    try:
        st = os.lstat(path)
    except (OSError, ValueError):
        return False
    return True











try:
    from nt import _getvolumepathname
except ImportError:
    _getvolumepathname = None
def ismount(path):
    
    path = os.fspath(path)
    seps = _get_bothseps(path)
    path = abspath(path)
    drive, root, rest = splitroot(path)
    if drive and drive[0] in seps:
        return not rest
    if root and not rest:
        return True

    if _getvolumepathname:
        x = path.rstrip(seps)
        y =_getvolumepathname(path).rstrip(seps)
        return x.casefold() == y.casefold()
    else:
        return False











def expanduser(path):
    
    path = os.fspath(path)
    if isinstance(path, bytes):
        tilde = b'~'
    else:
        tilde = '~'
    if not path.startswith(tilde):
        return path
    i, n = 1, len(path)
    while i < n and path[i] not in _get_bothseps(path):
        i += 1

    if 'USERPROFILE' in os.environ:
        userhome = os.environ['USERPROFILE']
    elif not 'HOMEPATH' in os.environ:
        return path
    else:
        try:
            drive = os.environ['HOMEDRIVE']
        except KeyError:
            drive = ''
        userhome = join(drive, os.environ['HOMEPATH'])

    if i != 1: 
        target_user = path[1:i]
        if isinstance(target_user, bytes):
            target_user = os.fsdecode(target_user)
        current_user = os.environ.get('USERNAME')

        if target_user != current_user:
            
            
            
            
            
            if current_user != basename(userhome):
                return path
            userhome = join(dirname(userhome), target_user)

    if isinstance(path, bytes):
        userhome = os.fsencode(userhome)

    return userhome + path[i:]















def expandvars(path):
    
    path = os.fspath(path)
    if isinstance(path, bytes):
        if b'$' not in path and b'%' not in path:
            return path
        import string
        varchars = bytes(string.ascii_letters + string.digits + '_-', 'ascii')
        quote = b'\''
        percent = b'%'
        brace = b'{'
        rbrace = b'}'
        dollar = b'$'
        environ = getattr(os, 'environb', None)
    else:
        if '$' not in path and '%' not in path:
            return path
        import string
        varchars = string.ascii_letters + string.digits + '_-'
        quote = '\''
        percent = '%'
        brace = '{'
        rbrace = '}'
        dollar = '$'
        environ = os.environ
    res = path[:0]
    index = 0
    pathlen = len(path)
    while index < pathlen:
        c = path[index:index+1]
        if c == quote:   
            path = path[index + 1:]
            pathlen = len(path)
            try:
                index = path.index(c)
                res += c + path[:index + 1]
            except ValueError:
                res += c + path
                index = pathlen - 1
        elif c == percent:  
            if path[index + 1:index + 2] == percent:
                res += c
                index += 1
            else:
                path = path[index+1:]
                pathlen = len(path)
                try:
                    index = path.index(percent)
                except ValueError:
                    res += percent + path
                    index = pathlen - 1
                else:
                    var = path[:index]
                    try:
                        if environ is None:
                            value = os.fsencode(os.environ[os.fsdecode(var)])
                        else:
                            value = environ[var]
                    except KeyError:
                        value = percent + var + percent
                    res += value
        elif c == dollar:  
            if path[index + 1:index + 2] == dollar:
                res += c
                index += 1
            elif path[index + 1:index + 2] == brace:
                path = path[index+2:]
                pathlen = len(path)
                try:
                    index = path.index(rbrace)
                except ValueError:
                    res += dollar + brace + path
                    index = pathlen - 1
                else:
                    var = path[:index]
                    try:
                        if environ is None:
                            value = os.fsencode(os.environ[os.fsdecode(var)])
                        else:
                            value = environ[var]
                    except KeyError:
                        value = dollar + brace + var + rbrace
                    res += value
            else:
                var = path[:0]
                index += 1
                c = path[index:index + 1]
                while c and c in varchars:
                    var += c
                    index += 1
                    c = path[index:index + 1]
                try:
                    if environ is None:
                        value = os.fsencode(os.environ[os.fsdecode(var)])
                    else:
                        value = environ[var]
                except KeyError:
                    value = dollar + var
                res += value
                if c:
                    index -= 1
        else:
            res += c
        index += 1
    return res





try:
    from nt import _path_normpath

except ImportError:
    def normpath(path):
        
        path = os.fspath(path)
        if isinstance(path, bytes):
            sep = b'\\'
            altsep = b'/'
            curdir = b'.'
            pardir = b'..'
        else:
            sep = '\\'
            altsep = '/'
            curdir = '.'
            pardir = '..'
        path = path.replace(altsep, sep)
        drive, root, path = splitroot(path)
        prefix = drive + root
        comps = path.split(sep)
        i = 0
        while i < len(comps):
            if not comps[i] or comps[i] == curdir:
                del comps[i]
            elif comps[i] == pardir:
                if i > 0 and comps[i-1] != pardir:
                    del comps[i-1:i+1]
                    i -= 1
                elif i == 0 and root:
                    del comps[i]
                else:
                    i += 1
            else:
                i += 1
        
        if not prefix and not comps:
            comps.append(curdir)
        return prefix + sep.join(comps)

else:
    def normpath(path):
        
        path = os.fspath(path)
        if isinstance(path, bytes):
            return os.fsencode(_path_normpath(os.fsdecode(path))) or b"."
        return _path_normpath(path) or "."


def _abspath_fallback(path):
    

    path = os.fspath(path)
    if not isabs(path):
        if isinstance(path, bytes):
            cwd = os.getcwdb()
        else:
            cwd = os.getcwd()
        path = join(cwd, path)
    return normpath(path)


try:
    from nt import _getfullpathname

except ImportError: 
    abspath = _abspath_fallback

else:  
    def abspath(path):
        
        try:
            return _getfullpathname(normpath(path))
        except (OSError, ValueError):
            return _abspath_fallback(path)

try:
    from nt import _findfirstfile, _getfinalpathname, readlink as _nt_readlink
except ImportError:
    
    realpath = abspath
else:
    def _readlink_deep(path):
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        allowed_winerror = 1, 2, 3, 5, 21, 32, 50, 67, 87, 4390, 4392, 4393

        seen = set()
        while normcase(path) not in seen:
            seen.add(normcase(path))
            try:
                old_path = path
                path = _nt_readlink(path)
                
                
                if not isabs(path):
                    
                    
                    
                    if not islink(old_path):
                        path = old_path
                        break
                    path = normpath(join(dirname(old_path), path))
            except OSError as ex:
                if ex.winerror in allowed_winerror:
                    break
                raise
            except ValueError:
                
                break
        return path

    def _getfinalpathname_nonstrict(path):
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        allowed_winerror = 1, 2, 3, 5, 21, 32, 50, 53, 65, 67, 87, 123, 161, 1920, 1921

        
        
        tail = path[:0]
        while path:
            try:
                path = _getfinalpathname(path)
                return join(path, tail) if tail else path
            except OSError as ex:
                if ex.winerror not in allowed_winerror:
                    raise
                try:
                    
                    
                    
                    new_path = _readlink_deep(path)
                    if new_path != path:
                        return join(new_path, tail) if tail else new_path
                except OSError:
                    
                    pass
                
                if ex.winerror in (1, 5, 32, 50, 87, 1920, 1921):
                    try:
                        name = _findfirstfile(path)
                        path, _ = split(path)
                    except OSError:
                        path, name = split(path)
                else:
                    path, name = split(path)
                if path and not name:
                    return path + tail
                tail = join(name, tail) if tail else name
        return tail

    def realpath(path, *, strict=False):
        path = normpath(path)
        if isinstance(path, bytes):
            prefix = b'\\\\?\\'
            unc_prefix = b'\\\\?\\UNC\\'
            new_unc_prefix = b'\\\\'
            cwd = os.getcwdb()
            
            if normcase(path) == normcase(os.fsencode(devnull)):
                return b'\\\\.\\NUL'
        else:
            prefix = '\\\\?\\'
            unc_prefix = '\\\\?\\UNC\\'
            new_unc_prefix = '\\\\'
            cwd = os.getcwd()
            
            if normcase(path) == normcase(devnull):
                return '\\\\.\\NUL'
        had_prefix = path.startswith(prefix)
        if not had_prefix and not isabs(path):
            path = join(cwd, path)
        try:
            path = _getfinalpathname(path)
            initial_winerror = 0
        except ValueError as ex:
            
            
            
            
            if strict:
                raise OSError(str(ex)) from None
            path = normpath(path)
        except OSError as ex:
            if strict:
                raise
            initial_winerror = ex.winerror
            path = _getfinalpathname_nonstrict(path)
        
        
        
        if not had_prefix and path.startswith(prefix):
            
            
            if path.startswith(unc_prefix):
                spath = new_unc_prefix + path[len(unc_prefix):]
            else:
                spath = path[len(prefix):]
            
            try:
                if _getfinalpathname(spath) == path:
                    path = spath
            except ValueError as ex:
                
                
                pass
            except OSError as ex:
                
                
                if ex.winerror == initial_winerror:
                    path = spath
        return path



supports_unicode_filenames = True

def relpath(path, start=None):
    
    path = os.fspath(path)
    if isinstance(path, bytes):
        sep = b'\\'
        curdir = b'.'
        pardir = b'..'
    else:
        sep = '\\'
        curdir = '.'
        pardir = '..'

    if start is None:
        start = curdir

    if not path:
        raise ValueError("no path specified")

    start = os.fspath(start)
    try:
        start_abs = abspath(normpath(start))
        path_abs = abspath(normpath(path))
        start_drive, _, start_rest = splitroot(start_abs)
        path_drive, _, path_rest = splitroot(path_abs)
        if normcase(start_drive) != normcase(path_drive):
            raise ValueError("path is on mount %r, start on mount %r" % (
                path_drive, start_drive))

        start_list = [x for x in start_rest.split(sep) if x]
        path_list = [x for x in path_rest.split(sep) if x]
        
        i = 0
        for e1, e2 in zip(start_list, path_list):
            if normcase(e1) != normcase(e2):
                break
            i += 1

        rel_list = [pardir] * (len(start_list)-i) + path_list[i:]
        if not rel_list:
            return curdir
        return join(*rel_list)
    except (TypeError, ValueError, AttributeError, BytesWarning, DeprecationWarning):
        genericpath._check_arg_types('relpath', path, start)
        raise












def commonpath(paths):
    

    if not paths:
        raise ValueError('commonpath() arg is an empty sequence')

    paths = tuple(map(os.fspath, paths))
    if isinstance(paths[0], bytes):
        sep = b'\\'
        altsep = b'/'
        curdir = b'.'
    else:
        sep = '\\'
        altsep = '/'
        curdir = '.'

    try:
        drivesplits = [splitroot(p.replace(altsep, sep).lower()) for p in paths]
        split_paths = [p.split(sep) for d, r, p in drivesplits]

        if len({r for d, r, p in drivesplits}) != 1:
            raise ValueError("Can't mix absolute and relative paths")

        
        
        
        if len({d for d, r, p in drivesplits}) != 1:
            raise ValueError("Paths don't have the same drive")

        drive, root, path = splitroot(paths[0].replace(altsep, sep))
        common = path.split(sep)
        common = [c for c in common if c and c != curdir]

        split_paths = [[c for c in s if c and c != curdir] for s in split_paths]
        s1 = min(split_paths)
        s2 = max(split_paths)
        for i, c in enumerate(s1):
            if c != s2[i]:
                common = common[:i]
                break
        else:
            common = common[:len(s1)]

        return drive + root + sep.join(common)
    except (TypeError, AttributeError):
        genericpath._check_arg_types('commonpath', *paths)
        raise


try:
    
    
    
    from nt import _path_isdir as isdir
    from nt import _path_isfile as isfile
    from nt import _path_islink as islink
    from nt import _path_exists as exists
except ImportError:
    
    pass


try:
    from nt import _path_isdevdrive
except ImportError:
    def isdevdrive(path):
        
        
        return False
else:
    def isdevdrive(path):
        
        try:
            return _path_isdevdrive(abspath(path))
        except OSError:
            return False