7

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
bearcat
  • 706
  • 1
  • 7
  • 12
  • Why does `envs\.some.env` not suffice? For common uses like `Path('envs\\.some.env').open()` vs `Path('.\\envs\\.some.env').open()` they should be the same thing. – Hans Musgrave Jul 02 '20 at 00:33
  • `.\\` is the current directory, it can be omitted. – metatoaster Jul 02 '20 at 00:33
  • Thanks for the quick responses. Using the django-environ module, the only way I could make it work was with the dot present for the current directory. Then I wanted to migrate to a pathlib solution. – bearcat Jul 02 '20 at 00:41
  • Then just simply `os.path.join('.', str(some_env))` at the end. – metatoaster Jul 02 '20 at 00:53
  • 1
    Yes that would work, and there are places where the os library has more functionality than pathlib. This may be one of those places. I'm wondering if there is a pathlib approach for this. – bearcat Jul 02 '20 at 03:30
  • 1
    I had this *exact* question just now because I am creating a path I'm passing to a nodejs `require()` statement, where `./something.js` and `something.js` are interpreted differently. – Boris Verkhovskiy Jul 05 '20 at 16:36

2 Answers2

3

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”.
scy
  • 7,132
  • 2
  • 27
  • 35
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
Mike T
  • 41,085
  • 18
  • 152
  • 203