Use the PurePath.relative_to() method to produce a relative path.
You weren't very clear as to how the base path is determined; here are two options:
Copysecondparent = path.parent.parent
homedir = pathlib.Path(r'C:\users\user1')
then just use str() on the path.relative_to(secondparent) or path.relative_to(homedir) result.
Demo:
Copy>>> import pathlib
>>> path = pathlib.Path(r'C:\users\user1\documents\importantdocuments')
>>> secondparent = path.parent.parent
>>> homedir = pathlib.Path(r'C:\users\user1')
>>> str(path.relative_to(secondparent))
'documents\\importantdocuments'
>>> str(path.relative_to(homedir))
'documents\\importantdocuments'
Answer from Martijn Pieters on Stack OverflowUse the PurePath.relative_to() method to produce a relative path.
You weren't very clear as to how the base path is determined; here are two options:
Copysecondparent = path.parent.parent
homedir = pathlib.Path(r'C:\users\user1')
then just use str() on the path.relative_to(secondparent) or path.relative_to(homedir) result.
Demo:
Copy>>> import pathlib
>>> path = pathlib.Path(r'C:\users\user1\documents\importantdocuments')
>>> secondparent = path.parent.parent
>>> homedir = pathlib.Path(r'C:\users\user1')
>>> str(path.relative_to(secondparent))
'documents\\importantdocuments'
>>> str(path.relative_to(homedir))
'documents\\importantdocuments'
This works on any OS and every version of Python:
Copyimport os
os.path.join(os.path.basename(os.path.dirname(p)),os.path.basename(p))
This works on python 3:
Copystr(p.relative_to(p.parent.parent))
Help understanding relative paths in python
pathlib's relative_to should behave like os.path.relpath
Making paths absolute in os.path and pathlib - Core Development - Discussions on Python.org
python - How to find the relative path between two directories? - Stack Overflow
Videos
I have a project roughly setup like so
project -data --input.txt -src --main.py
inside main.py I have a line
filename = '../data/input.txt'
and then I do logic with that data
my question is the following:
if I run from the command line
project$ src/python main.py
I get a FileNotFoundError on '../data/input.txt'
However if I run this from the command line
project/src$ python main.py
the function behaves as expected.
Can someone explain what is happening? Also what can I do to make both commands work?
os.path.relpath(path1, path2) # that's it
Just use the relpath() function of the os module.
import os
os.path.relpath(pathA, pathB)
As per the docs,
os.path.relpath(path[, start])
Return a relative filepath to path either from the current directory or from an optional start directory. This is a path computation: the filesystem is not accessed to confirm the existence or nature of path or start.
The first section solves the OP's problem, though if like me, he really wanted the solution relative to a common root then the second section solves it for him. The third section describes how I originally approached it and is kept for interest sake.
Relative Paths
Recently, as in Python 3.4-6, the os.path module has been extended to accept pathlib.Path objects. In the following case however it does not return a Path object and one is forced to wrap the result.
foo = Path("C:\\foo")
baz = Path("C:\\baz")
Path(os.path.relpath(foo, baz))
> Path("..\\foo")
Common Path
My suspicion is that you're really looking a path relative to a common root. If that is the case the following, from EOL, is more useful
Path(os.path.commonpath([foo, baz]))
> Path('c:/root')
Common Prefix
Before I'd struck upon os.path.commonpath I'd used os.path.comonprefix.
foo = Path("C:\\foo")
baz = Path("C:\\baz")
baz.relative_to(os.path.commonprefix([baz,foo]))
> Path('baz')
But be forewarned you are not supposed to use it in this context (See commonprefix : Yes, that old chestnut)
foo = Path("C:\\route66\\foo")
baz = Path("C:\\route44\\baz")
baz.relative_to(os.path.commonprefix([baz,foo]))
> ...
> ValueError : `c:\\route44\baz` does not start with `C:\\route`
but rather the following one from J. F. Sebastian.
Path(*os.path.commonprefix([foo.parts, baz.parts]))
> Path('c:/root')
... or if you're feeling verbose ...
from itertools import takewhile
Path(*[set(i).pop() for i in (takewhile(lambda x : x[0]==x[1], zip(foo.parts, baz.parts)))])
Python 3.12 added the walk_up parameter to the Path.relative_to method. Now you can do:
>>> from pathlib import Path
>>> foo = Path.home() / 'foo'
>>> bar = Path.home() / 'bar'
>>> bar.relative_to(foo, walk_up=True)
PosixPath('../bar')