Why you would use paths

TLDR

paths exists because path handling in Python is:

  1. Inconsistent\ vs /, absolute vs relative, string vs Path
  2. Manual — find root, resolve, normalize, convert, repeat
  3. Error-prone — works on your machine, breaks on theirs
  4. Tedious — same boilerplate in every project

paths makes it:

  1. Consistent — normalized slashes, project-relative paths, cross-platform by default
  2. Automatic — root detection, type conversion, caller detection
  3. Reliable — same rp on every machine, every OS
  4. Simple — one line instead of thirteen

Stop fighting with paths. Start using them.


File paths are a pain to work with.

Sometimes pure hell, even.

I got gutted the moment I started trying to write cross-platform code. Slashes going the wrong way, paths breaking when my teammate ran the same script, logs full of absolute paths that meant nothing on another machine. Well shit, man. Whoops.

Find the project root, resolve the path, make it relative, normalize the slashes, cast it to a string, pass it in. Over and over. Just to achieve simple cross-platform compatibility.

I don't want to inspect the stack to find the caller path.

I don't want to .resolve() every single path.

I just want path handling to be easy, consistent, and have some basic level of standardization.

So I made paths.

Drop-in upgrade from pathlib

Skpath wraps pathlib.Path. Everything pathlib.Path does, Skpath does too -- same methods, same interface, same behavior. You don't have to relearn anything.

What Skpath adds on top:

If you already use pathlib, switching to Skpath is a one-word change in your imports with zero risk of breaking existing code.

Automatic path detection — Skpath() with no arguments

Need to know the path of the current file? Don't inspect the stack yourself.

# get the current file's path as an Skpath, automatically
here = Skpath()

That's it. Skpath() with no arguments detects the caller's file path. Works in scripts, modules, notebooks, and test runners.

Skpath paths

Every Skpath stores three paths:

rp is the same on every machine, every OS, as long as the project structure is the same.

This changes everything -- and is a huge jump in path standardization.

@autopath — make any function path-safe

The fastest way to make your codebase cross-platform: slap @autopath() on any function that takes paths.

from suitkaise.paths import autopath, AnyPath

@autopath()
def process_file(path: AnyPath):
    # path is now an Skpath, regardless of what was passed in
    # str, Path, or Skpath -- all converted automatically
    print(path.rp)  # always cross-platform

Pass a str, a Path, or an Skpath -- @autopath() reads the type annotation and converts it for you. Your function just works, no matter what the caller gives it.

Combined with AnyPath (a union of str, Path, and Skpath), you can upgrade your entire codebase to use Skpath incrementally without breaking anything that already passes strings or Paths.

This is the "pit of success" -- once @autopath() is on a function, it's impossible to accidentally use a platform-specific path inside it.

What about just using pathlib?

pathlib is great. It handles slash differences internally and gives you a nice object to work with.

But it doesn't know about your project. It doesn't auto-detect the root. It doesn't give you a consistent path that works everywhere. And it doesn't convert types for you.

Skpath wraps pathlib.Path and adds project awareness, so you don't have to make it aware of things yourself.

The paths module also adds a bunch of cool things like @autopath and AnyPath to help you in your quest to make paths easy and standardized for your entire team.

pathlib.Path handles cross-platform normalization internally, but the moment you convert to a string (for logging, storing, or passing to a library), you're back to platform-specific slashes. What's the point?

path = Path("config/settings.yaml")
str(path)

# "config/settings.yaml" on Mac, "config\\settings.yaml" on Windows

Here is a set of problems that paths solves.

1. \ vs /
2. Relative paths
3. Project root related issues
4. Figuring out if you need to use a Path or a str
5. Comparing paths
6. General path handling is still so manual
7. Path IDs for storage
8. Finding where code lives
9. Project structure at a glance
10. Filename validation and sanitization
11. File operations without the boilerplate
12. Temporary root override for testing
13. Using paths as dict keys or in sets