38

I have this small program and it needs to create a small .txt file in their 'My Documents' Folder. Here's the code I have for that:

textfile=open('C:\Users\MYNAME\Documents','w')
lines=['stuff goes here']
textfile.writelines(lines)
textfile.close()

The problem is that if other people use it, how do I change the MYNAME to their account name?

martineau
  • 119,623
  • 25
  • 170
  • 301

3 Answers3

52

Use os.path.expanduser(path), see http://docs.python.org/library/os.path.html

e.g. expanduser('~/filename')

This works on both Unix and Windows, according to the docs.

Edit: forward slash due to Sven's comment.

Joe
  • 46,419
  • 33
  • 155
  • 245
  • 8
    Better use a forward slash in the path: `'~/filename'`. Otherwise, you will get unexpected results for things like `'~\name'`, where the `\n` will be replaced by a newline character. – Sven Marnach Jun 03 '11 at 13:27
  • 1
    @Sven - I wasn't sure. I don't use Windows, not sure how Python handles Windows path delimiters. – Joe Jun 03 '11 at 13:31
  • 17
    On Windows a `os.path.expanduser('~/filename')` call results in something like `'C:\\Documents and Settings\\/filename'` which is *not* the path of something in the user's `My Documents` folder. – martineau Jun 10 '11 at 02:29
  • 3
    Note that this *does not* work if the user has moved their My Documents folder so that it no longer sits under `~` (eg, if it's on a network share, or a different drive). – sapi Jul 11 '14 at 07:00
  • 3
    this doesn't work. what you need is `df = shell.SHGetDesktopFolder() pidl = df.ParseDisplayName(0, None, "::{450d8fba-ad25-11d0-98a8-0800361b1103}")[1] mydocs = shell.SHGetPathFromIDList(pidl)` – v.oddou Sep 09 '14 at 05:20
  • 1
    @v.oddou You should write this as another answer. Remember that the question doesn't specify Windows or Unix. – Joe Sep 09 '14 at 07:56
  • there it should be fine as a comment :) also for windows again, if future readers/googlers are interested in using the `shell.SHGetFolderPath()` approach: `CSIDL_MYDOCUMENTS` is not defined, so use the constant `5` !! – v.oddou Sep 09 '14 at 08:05
  • 1
    If you *just* want the "My Documents" folder, you need to use `expanduser("~")`, not just `expanduser("")`. The latter returns the empty string. – Jack M Aug 14 '18 at 09:48
29

This works without any extra libs:

import ctypes.wintypes
CSIDL_PERSONAL = 5       # My Documents
SHGFP_TYPE_CURRENT = 0   # Get current, not default value

buf= ctypes.create_unicode_buffer(ctypes.wintypes.MAX_PATH)
ctypes.windll.shell32.SHGetFolderPathW(None, CSIDL_PERSONAL, None, SHGFP_TYPE_CURRENT, buf)

print(buf.value)

Also works if documents location and/or default save location is changed by user.

axil
  • 1,558
  • 16
  • 17
  • This answer is quite similar to http://stackoverflow.com/a/3859336/1543290 - however they defined `SHGFP_TYPE_CURRENT= 0 # Want current, not default value` instead of this answer's `SHGFP_TYPE_CURRENT = 1 # Get current, not default value`! Which is true? According to http://stackoverflow.com/a/23852297/1543290 , it seems that `0` is correct – Zvika Dec 07 '15 at 20:29
  • 1
    @Zvika it is not right or wrong. It depends on what you need. If you need current value, use 1, if default, use 0. If the location is changed by the user then obviously the current value is more essential. – axil Dec 10 '15 at 10:14
  • I want to use `current` and not `default value` Which value is right for this? According to this answer's code comment - `1`, according to the other answer - `0` – Zvika Dec 13 '15 at 11:22
  • Use `shell32 = ctypes.OleDLL('shell32')`. Using `windll` (1) doesn't check the `HRESULT` code and (2) leaves you at the mercy of whatever library has set for `restype`, `argtypes`, and `errcheck` for `windll.shell32.SHGetFolderPathW`. The entire idea of the `cdll`, `windll`, and `oledll` loaders was bad from the start. – Eryk Sun Sep 13 '16 at 18:07
  • 1
    @EspoirMurhabazi Not sure what you mean. – axil Jan 31 '18 at 16:13
  • This https://stackoverflow.com/a/23852297/3676401 says 0 is current, 1 is default. – stblassitude Apr 09 '21 at 08:31
6

On Windows, you can use something similar what is shown in the accepted answer to the question: Python, get windows special folders for currently logged-in user.

For the My Documents folder path, useshellcon.CSIDL_PERSONALin the shell.SHGetFolderPath() function call instead of shellcon.CSIDL_MYPICTURES.

So, assuming you have the PyWin32 extensions1 installed, this might work (see caveat in Update section below):

>>> from win32com.shell import shell, shellcon
>>> shell.SHGetFolderPath(0, shellcon.CSIDL_PERSONAL, None, 0)
u'<path\\to\\folder>'

Update: I just read something that said that CSIDL_PERSONAL won't return the correct folder if the user has changed the default save folder in the Win7 Documents library. This is referring to what you can do in library's Properties dialog:

screenshot of library properties dialog
The checkmark means that the path is set as the default save location.

I currently am unware of a way to call the SHLoadLibraryFromKnownFolder() function through PyWin32 (there currently isn't a shell.SHLoadLibraryFromKnownFolder. However it should be possible to do so using the ctypes module.

1Installers for the latest versions of the Python for Windows Extensions are currently available from:  http://sourceforge.net/projects/pywin32

Community
  • 1
  • 1
martineau
  • 119,623
  • 25
  • 170
  • 301