Eryk Sun <eryk...@gmail.com> added the comment:
> 3. Worth fixing in `ntpath.expanduser()` To expand on the suggestion in msg390507, here's a demo that implements getting the profile path of a local user account, with support for the special profile directories "Default", "Public", and "ProgramData" (i.e. ALLUSERSPROFILE). ERROR_NONE_MAPPED = 1332 def get_profile_path(name): if not isinstance(name, str): raise TypeError(f'name must be str, not {type(name).__name__}') profile_list = r'Software\Microsoft\Windows NT\CurrentVersion\ProfileList' if name.lower() in ('default', 'public', 'programdata'): subkey = profile_list value = name else: try: sid = lookup_account_name(name)[0] except OSError as e: if e.winerror != ERROR_NONE_MAPPED: raise raise KeyError(f'name not found: {name}') subkey = f'{profile_list}\\{sid}' value = 'ProfileImagePath' try: with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, subkey) as hkey: path, dtype = winreg.QueryValueEx(hkey, value) except FileNotFoundError: raise KeyError(f'name not found: {name}') if dtype == winreg.REG_SZ: return path if dtype == winreg.REG_EXPAND_SZ: return winreg.ExpandEnvironmentStrings(path) raise TypeError('profile path value must be a string type (1 or 2), ' f'not {dtype}') For example: >>> print(get_profile_path('administrator')) C:\Users\Administrator >>> print(get_profile_path('default')) C:\Users\Default >>> print(get_profile_path('public')) C:\Users\Public >>> print(get_profile_path('programdata')) C:\ProgramData >>> print(get_profile_path('system')) C:\Windows\system32\config\systemprofile >>> print(get_profile_path('localservice')) C:\Windows\ServiceProfiles\LocalService >>> print(get_profile_path('networkservice')) C:\Windows\ServiceProfiles\NetworkService For lookup_account_name(), _winapi.LookupAccountName(system_name, account_name) has to be implemented. It should convert the SID result to string form via ConvertSidToStringSidW() and return the tuple (sid_string, domain_name, account_type). Here's a ctypes prototype implementation of lookup_account_name(): import ctypes lsalookup = ctypes.WinDLL( 'api-ms-win-security-lsalookup-l2-1-0', use_last_error=True) sddl = ctypes.WinDLL('api-ms-win-security-sddl-l1-1-0', use_last_error=True) heap = ctypes.WinDLL('api-ms-win-core-heap-l2-1-0', use_last_error=True) ERROR_INSUFFICIENT_BUFFER = 122 def lookup_account_name(name): sid = (ctypes.c_char * 1)() cb = ctypes.c_ulong() cch = ctypes.c_ulong() name_use = ctypes.c_ulong() lsalookup.LookupAccountNameW(None, name, sid, ctypes.byref(cb), None, ctypes.byref(cch), ctypes.byref(name_use)) error = ctypes.get_last_error() if error != ERROR_INSUFFICIENT_BUFFER: raise ctypes.WinError(error) sid = (ctypes.c_char * cb.value)() domain_name = (ctypes.c_wchar * cch.value)() success = lsalookup.LookupAccountNameW(None, name, sid, ctypes.byref(cb), domain_name, ctypes.byref(cch), ctypes.byref(name_use)) if not success: raise ctypes.WinError(ctypes.get_last_error()) ssid = ctypes.c_wchar_p() if not sddl.ConvertSidToStringSidW(sid, ctypes.byref(ssid)): raise ctypes.WinError(ctypes.get_last_error()) string_sid = ssid.value heap.LocalFree(ssid) return string_sid, domain_name.value, name_use.value ---------- nosy: +eryksun _______________________________________ Python tracker <rep...@bugs.python.org> <https://bugs.python.org/issue42998> _______________________________________ _______________________________________________ Python-bugs-list mailing list Unsubscribe: https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com