* UNIX file systems allow any byte sequence that doesn't contain / or \0 as file and directory names
* User interfaces have to render that as strings, so they must decode
* There is no meta data about what the file encoding is
Many programs use the encoding from the current locale, which is mostly a good assumption, but the way that locales scope (basically per process) has nothing to do with how file names are scoped.
So, many programs make some assumptions. Some models are:
1) Assume file names are encoded in the current locale
2) Assume file names are encoded in UTF-8
3) Don't assume anything
The "correct" model would be 3), but it's not very useful. People want to be able to sort and display file names, which generally isn't very useful with binary data.
Which is why most programs, including python, use 1) or 2), and sometimes offer some kind of kludge for when the assumption doesn't hold -- and sometimes not.
IMHO a file system should store an encoding for the file names contained in it, and validate on writes that the names are correct. But of course that would be a huge POSIX incompatibility, and thus won't happen.
People just live with the current models, because they tend to be good enough. Mostly.