'How to create a pathlib relative path with a dot starting point?

I needed to create a relative path starting with the current directory as a "." dot

For example, in windows ".\envs\.some.env" or "./envs/.some.env" elsewhere

I wanted to do this using pathlib. A solution was found, but it has a kludgy replace statement. Is there a better way to do this using pathlib?

The usage was django-environ, and the goal was to support multiple env files. The working folder contained an envs folder with the multiple env files within that folder.

import environ
from pathlib import Path
import os

domain_env = Path.cwd()

dotdot = Path("../")
some_env = dotdot / "envs" / ".some.env"

envsome = environ.Env()
envsome.read_env(envsome.str(str(domain_env), str(some_env).replace("..", ".")))  

print(str(some_env))
print(str(some_env).replace("..", "."))

dot = Path("./")    # Path(".") gives the same result
some_env = dot / "envs" / ".some.env"

print(str(some_env))

On windows gives:

..\envs\.some.env
.\envs\.some.env
envs\.some.env


Solution 1:[1]

The best I could find was (as suggested by metatoaster in the question’s comments):

os.path.join(".", some_env)

where some_env may either be a string or bytes or (since Python 3.6) any path-like object (which means you don’t have to convert your Path objects to strings anymore).

And since some people are wondering why this might be useful:

  • A shell script foo.sh in the current directory cannot be called by foo.sh (except when . is in $PATH), but must instead be invoked using ./foo.sh.
  • As Boris explained, in a Node.js require() statement there’s a difference between foo and ./foo.
  • I myself needed this in order to create a file name argument to ExifTool that’s guaranteed to not be parsed as a command line option, even if the file is for example called -quiet. ExifTool doesn’t adhere to the Unix convention of having a -- argument that denotes all the following argument to not be options, and the official suggestion by its author is to “[p]ut the directory name before the file name if the file name begins with a dash”.

Solution 2:[2]

Here's a multi-platform idea:

import ntpath
import os
import posixpath
from pathlib import Path, PurePosixPath, PureWindowsPath


def dot_path(pth):
    """Return path str that may start with '.' if relative."""
    if pth.is_absolute():
        return os.fsdecode(pth)
    if isinstance(pth, PureWindowsPath):
        return ntpath.join(".", pth)
    elif isinstance(pth, PurePosixPath):
        return posixpath.join(".", pth)
    else:
        return os.path.join(".", pth)


print(dot_path(PurePosixPath("file.txt")))    # ./file.txt
print(dot_path(PureWindowsPath("file.txt")))  # .\file.txt
print(dot_path(Path("file.txt")))             # one of the above, depending on host OS
print(dot_path(Path("file.txt").resolve()))   # (e.g.) /path/to/file.txt

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 scy
Solution 2 Mike T