How to get basedir of filename without a path?

  • Using Python 3.11 on Windows 10. My path separators are backslash or “\”.
  • I’m writing a function to get the base path. It works if the file has a path. But when it doesn’t have a path I want the function to return “.\”
  • FYI, this function is in my custom module util.py which I import into my program. Other functions work fine if that matters.
  • Purpose: get the base directory for where my program is so I can write a log filename there. After I get the base dir I will append the log filename to it like so: c:\done\logsmtp.txt.
Input: start.txt
Output of function: .\
Input: c:\done\start.txt
Output: c:\done

Here’s my function. I have tried several things, read several web pages to no avail. The problem is the backslash tries to escape the next character, and even when I return a raw string I still get an error.

def getfiledir(infn):
    tfn=os.path.dirname(infn) # This var used for debugging.
    if len(tfn)>0:
        return tfn
    else:
        return r'.\' # This gets an error right after trying to start the program.
        # return '\\' # Also gets error

Thank you again!

Look into pathlib. It’s amazing for dealing with paths.

If I’m understanding your question correctly, pathlib.PurePath.parent is what you’re looking for.

Maybe calling os.path.abspath(infn) would help here?
I do agree with @Monarch that using pathlib is often nicer.

Do you know about a module’s __file__ attribute?

1 Like

When I want to know the directory where the script is, I always use os.path.dirname(__file__).

2 Likes

The concern you’re trying to work around is that dirname() returns an empty string for a filename that has no directory component. A trailing separator doesn’t matter here, since dirname() always removes trailing separators. If you need the current working directory to be explicitly referenced in this case, without actually resolving it, then use the following expression: os.path.normpath(os.path.dirname(infn)). For an empty string, normpath() returns the reserved name of the current working directory, which is “.” on all platforms that are currently supported.

Eryk has pointed out that dirname() returns an empty string for a
plain “local” filename like “foo.py”, which you need to accomodate.

The pathlib stuff is to modern way people work with paths; I still
find it cumbersome myself and use dirname() and basename() et al.

I just wanted to point at this in your example code:

 return r'.\' # This gets an error right after trying to start 

Raw strings are great, except that they’re not a raw as you might
naively imagine. While they do not recognise the usual slosh escapes
like \a for a BEL character or \n for an NL/newline (which is why we
use them, so that C:\a\b is not C:\b) they do allow
escaping the quote:

 s = r'a\'b`

puts the string a'b into x. Because \\ is not an escape (which is
why raw strings are so handy for Windows paths containing sloshes) it is
not possible to end a raw string in a slosh. Hence your syntax error.

Reference:

Personally, I don’t think it’s a great design. The rule is effectively “you can’t end the string with an odd number of backslashes, and the string can only contain the enclosing quote if it’s immediately preceded by a backslash” (since the backslash is parsed for the purpose of figuring out where the literal ends, but not treated as an escape when interpreting the contents):

>>> r'\''
"\\'"

Plus, as a consequence of all that, you still have to understand the basic idea of a backslash sequence.

It would have been much simpler IMO to just say “you can’t contain the enclosing quote at all” - especially since that’s easy to work around with literal concatenation:

>>> r"A string with an apostrophe ' " r'and also "double quotes"'
'A string with an apostrophe \' and also "double quotes"'

I mean, really, how often do you want the result string to have a backslash immediately followed by a quote, compared to the times when you want the quote not following a backslash?

Well I can always append a chr(92) which is the backslash character. And that works.

In general you’re returning the os.path.dirname() result, which strips trailing slashes except for a root directory. So returning just “.” is correct. You don’t need to return ".\\". The calling function has to be designed to join via the os.sep path separator when it appends the filename. The os.path.join() function implements this for you, e.g. os.path.join(getfiledir(fn), "logsmtp.txt").