PurePath.joinpath

p / "a" / "b" and p.joinpath("a", "b") are the same thing. Both follow os.path.join: separators are inserted for you, extra slashes vanish, and any absolute segment throws away what came before it.

pathlib methodPython 3.4+ (with_segments 3.12+)Live demo
Common call
base / 'sub' / 'file.txt'
Returns
a new path; str / path works too
Replaces
os.path.join(base, "sub", "file.txt")
Watch out
base / '/etc' is just /etc
PurePath.joinpath(*pathsegments)
→ PurePath

Demo

Live evaluation
Append one segment.
Try:
Inputs
basestrleft side
childstrright side
Code
from pathlib import PurePosixPath
PurePosixPath('/srv/app') / 'static/logo.svg'
Result
PurePosixPath('/srv/app/static/logo.svg')

An absolute segment resets the path: '/srv/app' / '/etc/passwd' is '/etc/passwd', which is why joining user input onto a base folder is not a safety check. An empty segment adds nothing, and '..' is kept literally. In the last joinpath case '/src/' starts with a slash, so 'project/' is discarded.

Parameters

NameTypeRequiredDescription
*pathsegmentsstr | os.PathLikenoSegments to append, in order. Each may contain separators itself.

Return value

PurePath — A new path of the same class.

Common patterns

Join a list of segments
Unpack the list into joinpath.
from pathlib import Path
parts = ['2026', '09', 'report.csv']
p = Path('archive').joinpath(*parts)
Keep user-supplied names inside a folder
Join, resolve, then verify the result is still under the base.
from pathlib import Path
base = Path('uploads').resolve()
target = (base / user_name).resolve()
if not target.is_relative_to(base):
    raise ValueError('path escapes the upload folder')
Subclass that survives joining (3.12+)
Override with_segments to pass extra state to derived paths.
from pathlib import Path
class TaggedPath(Path):
    def __init__(self, *args, tag=None):
        super().__init__(*args)
        self.tag = tag
    def with_segments(self, *pathsegments):
        return type(self)(*pathsegments, tag=self.tag)

Examples

1. Chaining /
from pathlib import PurePosixPath PurePosixPath('/srv') / 'www' / 'index.html'
Returns
PurePosixPath('/srv/www/index.html')
2. joinpath with several segments
from pathlib import PurePosixPath PurePosixPath('/srv').joinpath('www', 'index.html')
Returns
PurePosixPath('/srv/www/index.html')
3. A str on the left
from pathlib import PurePosixPath '/srv' / PurePosixPath('www')
Returns
PurePosixPath('/srv/www')
4. Joining two paths
from pathlib import PurePosixPath PurePosixPath('a/b') / PurePosixPath('c/d')
Returns
PurePosixPath('a/b/c/d')
5. An absolute segment wins
from pathlib import PurePosixPath PurePosixPath('/usr').joinpath('/etc', 'hosts')
Returns
PurePosixPath('/etc/hosts')
6. Non-path operands are rejected
from pathlib import PurePosixPath PurePosixPath('logs') / 2026
Returns
TypeError: unsupported operand type(s) for /: 'PurePosixPath' and 'int'
7. with_segments builds same-class paths
from pathlib import PurePosixPath PurePosixPath('/x').with_segments('a', 'b')
Returns
PurePosixPath('a/b')

Pitfalls

1. Joining a number
Segments must be str or path-like; format numbers yourself.
/ int
from pathlib import PurePosixPath
PurePosixPath('logs') / 2026
TypeError: unsupported operand type(s) for /: 'PurePosixPath' and 'int'
/ str(int)
from pathlib import PurePosixPath
PurePosixPath('logs') / str(2026)
PurePosixPath('logs/2026')
2. Leading slash on the second segment
'/static' is absolute, so it replaces the base. Strip the slash, or join the parts individually.
/ '/static'
from pathlib import PurePosixPath
PurePosixPath('/srv/app') / '/static/logo.svg'
PurePosixPath('/static/logo.svg')
lstrip('/')
from pathlib import PurePosixPath
PurePosixPath('/srv/app') / '/static/logo.svg'.lstrip('/')
PurePosixPath('/srv/app/static/logo.svg')
3. Using + to join
Paths do not concatenate with +; / is the join operator.
path + str
from pathlib import PurePosixPath
PurePosixPath('/srv') + '/www'
TypeError: unsupported operand type(s) for +: 'PurePosixPath' and 'str'
path / str
from pathlib import PurePosixPath
PurePosixPath('/srv') / 'www'
PurePosixPath('/srv/www')

When to use

Use it
  • Building any path from pieces
  • joinpath(*parts) when the segments are in a list
Reach for something else
  • Security checks on user input → resolve() + is_relative_to()
  • Changing the extension or file name → with_suffix / with_name

Notes

CPython impl
PurePath.joinpath and __truediv__ both call self.with_segments(self, *segments); the parsing that follows uses os.path.join rules (posixpath.join / ntpath.join)
with_segments
Added in 3.12: every derived path (/, parent, iterdir(), glob() …) is created through it, so subclasses can override it to carry state
__rtruediv__
'a' / PurePosixPath('b') works because str has no __truediv__, so Python falls back to the path's __rtruediv__

FAQ

Use the / operator: Path('data') / 'raw' / 'file.csv'. joinpath('raw', 'file.csv') does the same with several segments in one call.