ÿØÿà JFIF    ÿÛ „  ( %"1!%)+...383,7(-.+  -+++--++++---+-+-----+---------------+---+-++7-----ÿÀ  ß â" ÿÄ     ÿÄ H    !1AQaq"‘¡2B±ÁÑð#R“Ò Tbr‚²á3csƒ’ÂñDS¢³$CÿÄ   ÿÄ %  !1AQa"23‘ÿÚ   ? ôÿ ¨pŸªáÿ —åYõõ\?àÒü©ŠÄï¨pŸªáÿ —åYõõ\?àÓü©ŠÄá 0Ÿªáÿ Ÿå[úƒ ú®ði~TÁbqÐ8OÕpÿ ƒOò¤Oè`–RÂáœá™êi€ßÉ< FtŸI“öÌ8úDf´°å}“¾œ6  öFá°y¥jñÇh†ˆ¢ã/ÃÐ:ªcÈ "Y¡ðÑl>ÿ ”ÏËte:qž\oäŠe÷󲍷˜HT4&ÿ ÓÐü6ö®¿øþßèô Ÿ•7Ñi’•j|“ñì>b…þS?*Óôÿ ÓÐü*h¥£ír¶ü UãS炟[AÐaè[ûª•õ&õj?†Éö+EzP—WeÒírJFt ‘BŒ†Ï‡%#tE Øz ¥OÛ«!1›üä±Í™%ºÍãö]°î(–:@<‹ŒÊö×òÆt¦ãº+‡¦%ÌÁ²h´OƒJŒtMÜ>ÀÜÊw3Y´•牋4ǍýʏTì>œú=Íwhyë,¾Ôò×õ¿ßÊa»«þˆѪQ|%6ž™A õ%:øj<>É—ÿ Å_ˆCbõ¥š±ý¯Ýƒï…¶|RëócÍf溪“t.СøTÿ *Ä¿-{†çàczůŽ_–^XþŒ±miB[X±d 1,é”zEù»& î9gœf™9Ð'.;—™i}!ôšåîqêÛ٤ёý£½ÆA–àôe"A$˝Úsäÿ ÷Û #°xŸëí(l »ý3—¥5m! rt`†0~'j2(]S¦¦kv,ÚÇ l¦øJA£Šƒ J3E8ÙiŽ:cÉžúeZ°€¯\®kÖ(79«Ž:¯X”¾³Š&¡* ….‰Ž(ÜíŸ2¥ª‡×Hi²TF¤ò[¨íÈRëÉ䢍mgÑ.Ÿ<öäS0í„ǹÁU´f#Vß;Õ–…P@3ío<ä-±»Ž.L|kªÀê›fÂ6@»eu‚|ÓaÞÆŸ…¨ááå>åŠ?cKü6ùTÍÆ”†sĤÚ;H2RÚ†õ\Ö·Ÿn'¾ ñ#ºI¤Å´%çÁ­‚â7›‹qT3Iï¨ÖÚ5I7Ë!ÅOóŸ¶øÝñØôת¦$Tcö‘[«Ö³šÒ';Aþ ¸èíg A2Z"i¸vdÄ÷.iõ®§)¿]¤À†–‡É&ä{V¶iŽ”.Ó×Õÿ û?h¬Mt–íª[ÿ Ñÿ ÌV(í}=ibÔ¡›¥¢±b Lô¥‡piη_Z<‡z§èŒ)iÖwiÇ 2hÙ3·=’d÷8éŽ1¦¸c¤µ€7›7Ø ð\á)} ¹fËí›pAÃL%âc2 í§æQz¿;T8sæ°qø)QFMð‰XŒÂ±N¢aF¨…8¯!U  Z©RÊ ÖPVÄÀÍin™Ì-GˆªÅËŠ›•zË}º±ŽÍFò¹}Uw×#ä5B¤{î}Ð<ÙD é©¤&‡ïDbàÁôMÁ." ¤‡ú*õ'VŽ|¼´Úgllº¼klz[Æüï÷Aób‡Eÿ dÑ»Xx9ÃÜ£ÁT/`¼¸vI±Ýµ·Ë‚“G³þ*Ÿû´r|*}<¨îºœ @¦mÄ’M¹”.œ«Y–|6ÏU¤jç¥ÕÞqO ˜kDÆÁ¨5ÿ š;ÐЦ¦€GÙk \ –Þ=â¼=SͧµªS°ÚÍpÜãQűÀõ¬?ÃÁ1Ñ•õZà?hóœ€ L¦l{Y*K˜Ù›zc˜–ˆâ ø+¾ ­-Ök¥%ùEÜA'}ˆ><ÊIè“bpÍ/qÞâvoX€w,\úªò6Z[XdÒæ­@Ö—€$òJí#é>'°Ú ôª˜<)4ryÙ£|óAÅn5žêŸyÒäMÝ2{"}‰–¤l÷ûWX\l¾Á¸góÉOÔ /óñB¤f¸çñ[.P˜ZsÊË*ßT܈§QN¢’¡¨§V¼(Üù*eÕ“”5T¨‹Âê¥FŒã½Dü[8'Ò¥a…Ú¶k7a *•›¼'Ò·\8¨ª\@\õ¢¦íq+DÙrmÎ…_ªæ»ŠÓœ¡¯’Ré9MÅ×D™lælffc+ŒÑ,ý™ÿ ¯þǤ=Å’Á7µ÷ÚÛ/“Ü€ñýã¼àí¾ÕÑ+ƒ,uµMâÀÄbm:ÒÎPæ{˜Gz[ƒ¯«® KHà`ߨŠéí¯P8Aq.C‰ à€kòpj´kN¶qô€…Õ,ÜNŠª-­{Zö’æû44‰sŽè‰îVíRœÕm" 6?³D9¡ÇTíÅꋇ`4«¸ÝÁô ï’ýorqКÇZ«x4Žâéþuïf¹µö[P ,Q£éaX±`PÉÍZ ¸äYúg üAx ’6Lê‚xÝÓ*äQ  Ï’¨hÍ =²,6ï#rÃ<¯–£»ƒ‹,–ê•€ aÛsñ'%Æ"®ÛüìBᝠHÚ3ß°©$“XnœÖ’î2ËTeûìxîß ¦å¿çÉ ðK§þ{‘t‚Ϋ¬jéîZ[ ”š7L¥4VÚCE×]m¤Øy”ä4-dz£œ§¸x.*ãÊÊ b÷•h:©‡¦s`BTÁRû¾g⻩‹jø sF¢àJøFl‘È•Xᓁà~*j¯ +(ÚÕ6-£¯÷GŠØy‚<Ç’.F‹Hœw(+)ÜÜâÈzÄäT§FߘãÏ;DmVœ3Àu@mÚüXÝü•3B¨òÌÁÛ<·ÃÜ z,Ì@õÅ·d2]ü8s÷IôÞ¯^Ç9¢u„~ëAŸï4«M? K]­ÅàPl@s_ p:°¬ZR”´›JC[CS.h‹ƒïËœ«Æ]–÷ó‚wR×k7X‰k›‘´ù¦=¡«‰¨¨Â')—71ó’c‡Ðúµ `é.{§p¹ój\Ž{1h{o±Ý=áUÊïGÖŒõ–-BÄm+AZX¶¡ ïHðæ¥JmÙ;…䡟ˆ¦ ° äšiÉg«$üMk5¤L“’çÊvïâï ,=f“"íἊ5ô¬x6{ɏžID0e¸vçmi'︧ºð9$ò¹÷*£’9ÿ ²TÔ…×>JV¥}Œ}$p[bÔ®*[jzS*8 ”·T›Í–ñUîƒwo$áè=LT™ç—~ô·¤ÈÚ$榍q‰„+´kFm)ž‹©i–ËqÞŠ‰à¶ü( ‚•§ •°ò·‡#5ª•µÊ﯅¡X¨šÁ*F#TXJÊ ušJVÍ&=iÄs1‚3•'fý§5Ñ<=[íÞ­ PÚ;ѱÌ_~Ä££8rÞ ²w;’hDT°>ÈG¬8Á²ÚzŽ®ò®qZcqJêäÞ-ö[ܘbň±çb“ж31²n×iƒðÕ;1¶þÉ ªX‰,ßqÏ$>•î íZ¥Z 1{ç൵+ƒÕµ¥°T$§K]á»Ûï*·¤tMI’ÂZbŽÕiÒ˜}bÓ0£ª5›¨ [5Ž^ÝœWøÂÝh° ¢OWun£¤5 a2Z.G2³YL]jåtì”ä ÁÓ‘%"©<Ôúʰsº UZvä‡ÄiÆÒM .÷V·™ø#kèýiíÌ–ª)µT[)BˆõÑ xB¾B€ÖT¨.¥~ð@VĶr#¸ü*åZNDŽH;âi ],©£öØpù(šºãö¼T.uCê•4@ÿ GÕÛ)Cx›®0ø#:ÏðFÒbR\(€€Ä®fã4Þ‰Fä¯HXƒÅ,†öEÑÔÜ]Öv²?tLÃvBY£ú6Êu5ÅAQ³1‘’¬x–HŒÐ‡ ^ ¸KwJôÖŽ5×CÚ¨vÜ«/B0$×k°=ðbÇ(Ï)w±A†Á† 11Í=èQšµ626ŒÜ/`G«µ<}—-Ö7KEHÈÉðóȤmݱû±·ø«Snmá=“䫚mݱŸ¡¶~ó·“äUóJæúòB|E LêŽy´jDÔ$G¢þÐñ7óR8ýÒ…Ç› WVe#·Ÿ p·Fx~•ݤF÷0Èÿ K¯æS<6’¡WШ; ´ÿ ¥Êø\Òuî†åÝ–VNœkÒ7oòX¨Á­Ø÷FÎÑä±g÷ÿ M~Çî=p,X´ ÝÌÚÅ‹’ÃjÖ.ØöÏñ qïQ¤ÓZE†° =6·]܈ s¸>v•Ž^Ý\wq9r‰Î\¸¡kURÒ$­*‹Nq?Þª*!sŠÆ:TU_u±T+øX¡ ®¹¡,ÄâÃBTsÜ$Ø›4m椴zÜK]’’›Pƒ @€#â˜`é¹=I‡fiV•Ôî“nRm+µFPOhÍ0B£ €+¬5c v•:P'ÒyÎ ‰V~‚Ó†ÖuókDoh$å\*ö%Ю=£«…aȼ½÷Û.-½VŒŠ¼'lyî±1¬3ó#ÞE¿ÔS¤gV£m›=§\û"—WU¤ÚǼÿ ÂnÁGŒÃ ‚õN D³õNÚíŒÕ;HôyÄÈ©P¹Ä{:?R‘Ô¨âF÷ø£bÅó® JS|‚R÷ivýáâ€Æé¡è³´IئÑT!§˜•ت‚¬â@q€wnïCWÄ@JU€ê¯m6]Ï:£âx'+ÒðXvÓ¦Úm=–´7œ $ì“B£~p%ÕŸUþ« N@¼üï~w˜ñø5®—'Ôe»¤5ã//€ž~‰Tþ›Å7•#¤× Íö pÄ$ùeåì*«ÓŠEØWEÈsßg ¦ûvžSsLpºÊW–âµEWöˬH; ™!CYõZ ÃÄf æ#1W. \uWâ\,\Çf j’<qTbên›Î[vxx£ë 'ö¨1›˜ÀM¼Pÿ H)ƒêêŒA7s,|F“ 꺸k³9Ìö*ç®;Ö!Ö$Eiž•¹ÒÚ†ýóéÝû¾ÕS®ó$’NÝäŸz¤5r¦ãÄÃD÷Üø!°ø‡Ô&@m™Ì^Ãä­d q5Lnÿ N;.6½·N|#ä"1Nƒx“ã<3('&ñßt  ~ªu”1Tb㫨9ê–›–bìd$ߣ=#ÕãÒmU¯eí$EFù5ýYô櫨æì™Ç—±ssM]·á¿0ÕåJRÓªîiƒ+O58ÖñªŠÒx" \µâá¨i’¤i —Ö ” M+M¤ë9‚‰A¦°Qõ¾ßøK~¼Ã‘g…Ö´~÷Ï[3GUœÒ½#…kàÔ®Ò”‰³·dWV‰IP‰Ú8u¹”E ÖqLj¾êÕCBš{A^Âß;–¨`¯¬ìö ˼ ×tìø.tƐm*n¨y4o&Àx¥n¦×î‡aupáÛj8¿m›è¶ã!o½;ß0y^ý×^EÑ¿ÒjzŒ­)vÚÑnÄL …^ªô× ‡—‚3k Îý­hï]içå–îÏ*÷ñþ»Ô CÒjøjÍznˆ´ ¹#b'Fô‹ ‰v¥'’à'T´ƒHýÍ%M‰ ƒ&ÆÇŒï1 ‘ –Þ ‰i¬s žR-Ÿ kЬá¬7:þ 0ŒÅÒÕ/aÙ¬ÃÝ#Úøœ ©aiVc‰. ¹¦ãµ” ›Yg¦›ÆÎýº°f³7ƒhá·¸­}&D9¡ÂsÉÙÞèŠõØàC™¨ñbFC|´Ü(ŸƒÚÒ-%»'a Ì¿)ËÇn¿úÿ ÞŽX…4ÊÅH^ôΑí@ù¹Eh¶“L8Çjù ¼ÎåVªóR©Ï5uà V4lZß®=€xÖŸ–ÑÈ ÷”¨°¾__yM1tÉ?uÆþIkÄgæ@þ[¢†°XÃJ£j·:nkÅ¢u ‘}âGzö­/IµèЬ¼48q¦F°ŽR¼=ûì{´¯RýicS ÕÛ íNtÍÙï£,w4rêì®»~x(©Uñ§#Ñ&œÕ¤>ÎåÍÓ9’Ö{9eV­[Öjâ²ãu]˜å2›qÑšÕJç0€sÄ|Êëè0튔bÁ>“{×_F`Ø©ºê:µä,v¤ðfc1±"«ÔÍän1#=· Âøv~H½ÐßA¾¿Ü€Óš]Õ; I¾÷ç‚Qi†î¹9ywÔKG˜áñ zQY—§ÃÕZ07§X‚ Áh;ÁM)iÌCH-¯T‘ë|A0{Ò½LÚ–TâÖkÜ’dÀ“rmm»”جPF³ÖcbE§T€ÒxKºû’Ó®7±²(\4ŽÃ¸Uu@j™yĵ;³µ!Á¢b.W¤=mõ´êµK k ¸K^ÜÛ#p*Ü14qkZç5ïë †°5Ï%ÍÛ<Õ¤×Ô¥ê†C Õ´¼ú$ƒÖ“”]Ù¬qÞÚ[4©ý!ûÏ—Áb쳐XµA¬â~`›Çr¸8ìùÝ䫦<>ä÷«?xs´ÇÑ /á;¹øüÊÈÙà{"@Žïzâ¬[âß‚ U_<ÇŸ½4èN˜ú61®qŠu ¦þF£»äJ_ˆÙÎ~ ÞAã–݄ϗrŠD;xTž‘ô`É«…suãO`?³à™ô Lý#Íc5öoæØ‚y´´÷«ZR§<&JÇ+éâô´€i!Àˆ0æAoàðLèÖ-2ŸõW.’t^–(KÁmHµV@xÜÇy®Ñø­â^:Ú3w· 7½¹°ñ¸â¹®:',«Mœ—n­Á+Ãbš LÈ‘ÄnRÓÅœ%¦²‰¨ùQ:¤f‚ "PÕtô¸…cæl…&˜Ú˜Ôkv‹ž+vŠ,=¢v­6—Xy*¥t£«<™:“aîϲ=¦6rO]XI¿Œ÷¤zÚ­›¶ 6÷”w\d ü~v®ˆÌk«^m<ÿ ¢‰Õ\)ùºŽ;… lîÙÅEŠ®cѾ@vnMÏ,¼“ñ•ŽBxðÃzãÇç%3ˆ"}Ù•Åî> BÉú;Ò]V+P˜F_´ßé> Øše|ï‡ÄOmFæÇ ãqÞ$/xÐx­z`ï9"œÜij‚!7.\Td…9M‡•iŽ‹¾‘50ÞŽn¥ß4ÉôO ¹*í^QêËÜÇÌ8=ާs‰'ÂëÙ«á%Pú[O †ÅP¯Vsް.‰,kc¶ ¬A9n˜XÎ-ÞšN["¹QÕ‰ƒMýÁߺXJæÍaLj¾×Ãmã¾ãÚ uñÒþåQô¦¥ /ÄUx:‚ÍÜ’ Đ©ØÝ3V¨‰ÕnÐ6ó*óúK­«…c ¯U òhsý­jóÔj#,ímŒRµ«lbïUTŒÑ8†Ä0œÏr`ð¡¬É Ї ë"À² ™ 6¥ f¶ ¢ÚoܱԷ-<Àî)†a¶ž'Ú»¨TXqØæ¶÷YÄHy˜9ÈIW­YÀuMFë ºÏ’AqÌ4·/Ú †ô'i$øä­=Ä Ý|öK×40è|È6p‘0§)o¥ctî§H+CA-“ xØ|ÐXАç l8íºð3Ø:³¤¬KX¯UÿÙ # 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. 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","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"] def _get_bothseps(path): if isinstance(path, bytes): return b'\\/' else: return '\\/' # 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). def normcase(s): """Normalize case of pathname. Makes all characters lowercase and all slashes into backslashes.""" s = os.fspath(s) if isinstance(s, bytes): return s.replace(b'/', b'\\').lower() else: return s.replace('/', '\\').lower() # 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. def isabs(s): """Test whether a path is absolute""" s = os.fspath(s) # Paths beginning with \\?\ are always absolute, but do not # necessarily contain a drive. if isinstance(s, bytes): if s.replace(b'/', b'\\').startswith(b'\\\\?\\'): return True else: if s.replace('/', '\\').startswith('\\\\?\\'): return True s = splitdrive(s)[1] return len(s) > 0 and s[0] in _get_bothseps(s) # Join two (or more) paths. 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 #23780: Ensure compatible data type even if p is null. result_drive, result_path = splitdrive(path) for p in map(os.fspath, paths): p_drive, p_path = splitdrive(p) if p_path and p_path[0] in seps: # Second path is absolute if p_drive or not result_drive: result_drive = p_drive result_path = p_path continue elif p_drive and p_drive != result_drive: if p_drive.lower() != result_drive.lower(): # Different drives => ignore the first path entirely result_drive = p_drive result_path = p_path continue # Same drive in different case result_drive = p_drive # Second path is relative to the first if result_path and result_path[-1] not in seps: result_path = result_path + sep result_path = result_path + p_path ## add separator between UNC and non-absolute path if (result_path and result_path[0] not in seps and result_drive and result_drive[-1:] != colon): return result_drive + sep + result_path return result_drive + result_path except (TypeError, AttributeError, BytesWarning): genericpath._check_arg_types('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 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. 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. """ p = os.fspath(p) if len(p) >= 2: if isinstance(p, bytes): sep = b'\\' altsep = b'/' colon = b':' else: sep = '\\' altsep = '/' colon = ':' normp = p.replace(altsep, sep) if (normp[0:2] == sep*2) and (normp[2:3] != sep): # is a UNC path: # vvvvvvvvvvvvvvvvvvvv drive letter or UNC path # \\machine\mountpoint\directory\etc\... # directory ^^^^^^^^^^^^^^^ index = normp.find(sep, 2) if index == -1: return p[:0], p index2 = normp.find(sep, index + 1) # a UNC path can't have two slashes in a row # (after the initial two) if index2 == index + 1: return p[:0], p if index2 == -1: index2 = len(p) return p[:index2], p[index2:] if normp[1:2] == colon: return p[:2], p[2:] return p[:0], p # 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. def split(p): """Split a pathname. Return tuple (head, tail) where tail is everything after the final slash. Either part may be empty.""" p = os.fspath(p) seps = _get_bothseps(p) d, p = splitdrive(p) # set i to index beyond p's last slash i = len(p) while i and p[i-1] not in seps: i -= 1 head, tail = p[:i], p[i:] # now tail has no slashes # remove trailing slashes from head, unless it's all slashes head = head.rstrip(seps) or head return d + head, tail # 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. 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__ # Return the tail (basename) part of a path. def basename(p): """Returns the final component of a pathname""" return split(p)[1] # Return the head (dirname) part of a path. def dirname(p): """Returns the directory component of a pathname""" return split(p)[0] # Is a path a symbolic link? # This will always return false on systems where os.lstat doesn't exist. def islink(path): """Test whether a path is a symbolic link. This will always return false for Windows prior to 6.0. """ try: st = os.lstat(path) except (OSError, ValueError, AttributeError): return False return stat.S_ISLNK(st.st_mode) # Being true for dangling symbolic links is also useful. def lexists(path): """Test whether a path exists. Returns True for broken symbolic links""" try: st = os.lstat(path) except (OSError, ValueError): return False return True # 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. try: from nt import _getvolumepathname except ImportError: _getvolumepathname = None def ismount(path): """Test whether a path is a mount point (a drive root, the root of a share, or a mounted volume)""" path = os.fspath(path) seps = _get_bothseps(path) path = abspath(path) root, rest = splitdrive(path) if root and root[0] in seps: return (not rest) or (rest in seps) if rest in seps: return True if _getvolumepathname: return path.rstrip(seps) == _getvolumepathname(path).rstrip(seps) else: return False # 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.) def expanduser(path): """Expand ~ and ~user constructs. If user or $HOME is unknown, do nothing.""" 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 isinstance(path, bytes): userhome = os.fsencode(userhome) if i != 1: #~user userhome = join(dirname(userhome), path[1:i]) return userhome + path[i:] # 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 '^|<>='. def expandvars(path): """Expand shell variables of the forms $var, ${var} and %var%. Unknown variables are left unchanged.""" 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: # no expansion within single quotes 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: # variable or '%' 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: # variable or '$$' 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 # 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! def normpath(path): """Normalize path, eliminating double slashes, etc.""" path = os.fspath(path) if isinstance(path, bytes): sep = b'\\' altsep = b'/' curdir = b'.' pardir = b'..' special_prefixes = (b'\\\\.\\', b'\\\\?\\') else: sep = '\\' altsep = '/' curdir = '.' pardir = '..' special_prefixes = ('\\\\.\\', '\\\\?\\') if path.startswith(special_prefixes): # in the case of paths with these prefixes: # \\.\ -> device names # \\?\ -> literal paths # do not do any normalization, but return the path # unchanged apart from the call to os.fspath() return path path = path.replace(altsep, sep) prefix, path = splitdrive(path) # collapse initial backslashes if path.startswith(sep): prefix += sep path = path.lstrip(sep) 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 prefix.endswith(sep): del comps[i] else: i += 1 else: i += 1 # If the path is now empty, substitute '.' if not prefix and not comps: comps.append(curdir) return prefix + sep.join(comps) def _abspath_fallback(path): """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. """ 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) # Return an absolute path. try: from nt import _getfullpathname except ImportError: # not running on Windows - mock up something sensible abspath = _abspath_fallback else: # use native Windows method on Windows def abspath(path): """Return the absolute version of a path.""" try: return normpath(_getfullpathname(path)) except (OSError, ValueError): return _abspath_fallback(path) try: from nt import _getfinalpathname, readlink as _nt_readlink except ImportError: # realpath is a no-op on systems without _getfinalpathname support. realpath = abspath else: def _readlink_deep(path): # 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 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) # Links may be relative, so resolve them against their # own location if not isabs(path): # 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 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: # Stop on reparse points that are not symlinks break return path def _getfinalpathname_nonstrict(path): # 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 # 67: ERROR_BAD_NET_NAME (implies remote server unavailable) # 87: ERROR_INVALID_PARAMETER # 123: ERROR_INVALID_NAME # 1920: ERROR_CANT_ACCESS_FILE # 1921: ERROR_CANT_RESOLVE_FILENAME (implies unfollowable symlink) allowed_winerror = 1, 2, 3, 5, 21, 32, 50, 67, 87, 123, 1920, 1921 # Non-strict algorithm is to find as much of the target directory # as we can and join the rest. tail = '' 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: # The OS could not resolve this path fully, so we attempt # to follow the link ourselves. If we succeed, join the tail # and return. new_path = _readlink_deep(path) if new_path != path: return join(new_path, tail) if tail else new_path except OSError: # If we fail to readlink(), let's keep traversing pass path, name = split(path) # TODO (bpo-38186): Request the real file name from the directory # entry using FindFirstFileW. For now, we will return the path # as best we have it if path and not name: return path + tail tail = join(name, tail) if tail else name return tail def realpath(path): path = normpath(path) if isinstance(path, bytes): prefix = b'\\\\?\\' unc_prefix = b'\\\\?\\UNC\\' new_unc_prefix = b'\\\\' cwd = os.getcwdb() # bpo-38081: Special case for realpath(b'nul') if normcase(path) == normcase(os.fsencode(devnull)): return b'\\\\.\\NUL' else: prefix = '\\\\?\\' unc_prefix = '\\\\?\\UNC\\' new_unc_prefix = '\\\\' cwd = os.getcwd() # bpo-38081: Special case for realpath('nul') 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 OSError as ex: initial_winerror = ex.winerror path = _getfinalpathname_nonstrict(path) # The path returned by _getfinalpathname will always start with \\?\ - # strip off that prefix unless it was already provided on the original # path. if not had_prefix and path.startswith(prefix): # For UNC paths, the prefix will actually be \\?\UNC\ # Handle that case as well. if path.startswith(unc_prefix): spath = new_unc_prefix + path[len(unc_prefix):] else: spath = path[len(prefix):] # Ensure that the non-prefixed path resolves to the same path try: if _getfinalpathname(spath) == path: path = spath except OSError as ex: # If the path does not exist and originally did not exist, then # strip the prefix anyway. if ex.winerror == initial_winerror: path = spath return path # Win9x family and earlier have no Unicode filename support. supports_unicode_filenames = (hasattr(sys, "getwindowsversion") and sys.getwindowsversion()[3] >= 2) def relpath(path, start=None): """Return a relative version of a path""" 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 = splitdrive(start_abs) path_drive, path_rest = splitdrive(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] # Work out how much of the filepath is shared by start and path. 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 # 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. def commonpath(paths): """Given a sequence of path names, returns the longest common sub-path.""" 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 = [splitdrive(p.replace(altsep, sep).lower()) for p in paths] split_paths = [p.split(sep) for d, p in drivesplits] try: isabs, = set(p[:1] == sep for d, p in drivesplits) except ValueError: raise ValueError("Can't mix absolute and relative paths") from None # 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 len(set(d for d, p in drivesplits)) != 1: raise ValueError("Paths don't have the same drive") drive, path = splitdrive(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)] prefix = drive + sep if isabs else drive return prefix + sep.join(common) except (TypeError, AttributeError): genericpath._check_arg_types('commonpath', *paths) raise try: # The genericpath.isdir implementation uses os.stat and checks the mode # attribute to tell whether or not the path is a directory. # This is overkill on Windows - just pass the path to GetFileAttributes # and check the attribute from there. from nt import _isdir as isdir except ImportError: # Use genericpath.isdir as imported above. pass