Coverage for pyguymer3/make_path_safe.py: 91%
23 statements
« prev ^ index » next coverage.py v7.9.2, created at 2025-07-08 18:47 +0000
« prev ^ index » next coverage.py v7.9.2, created at 2025-07-08 18:47 +0000
1#!/usr/bin/env python3
3# Define function ...
4def make_path_safe(
5 path,
6 /,
7 *,
8 allowHidden = False,
9 ensureNFC = True,
10):
11 """Make a path safe for using on a filesystem.
13 Parameters
14 ----------
15 path : str
16 the path to make safe
17 allowHidden : bool, optional
18 allow the path to be hidden if it starts with a period (default False)
19 ensureNFC : bool, optional
20 ensure that the Unicode encoding is NFC
22 Notes
23 -----
24 Copyright 2017 Thomas Guymer [1]_
26 References
27 ----------
28 .. [1] PyGuymer3, https://github.com/Guymer/PyGuymer3
29 """
31 # Import standard modules ...
32 import os
33 import unicodedata
35 # Remove "bad" characters according to me ...
36 # NOTE: This just avoids accidents with comments and variables in a shell.
37 for badChar in ["#", "$"]:
38 path = path.replace(badChar, "")
40 # Remove more bad characters according to Wikipedia (not covered by either
41 # myself earlier or Microsoft later) ...
42 # NOTE: See https://en.wikipedia.org/wiki/Filename#Reserved_characters_and_words
43 # NOTE: See https://en.wikipedia.org/wiki/Filename#Comparison_of_filename_limitations
44 for badChar in ["%"]:
45 path = path.replace(badChar, "")
47 # Remove more bad characters according to Microsoft (not covered by either
48 # myself earlier or Wikipedia earlier) ...
49 # NOTE: See https://learn.microsoft.com/en-gb/windows/win32/fileio/naming-a-file#naming-conventions
50 for badChar in ["\\", "/", ":", "*", "?", "\"", "<", ">", "|"]:
51 path = path.replace(badChar, "")
52 for badChar in [chr(i) for i in range(32)]:
53 path = path.replace(badChar, "")
54 path = path.rstrip(" .")
55 if allowHidden:
56 path = path.lstrip(" ")
57 else:
58 path = path.lstrip(" .")
59 root, ext = os.path.splitext(path)
60 if root in ["CON", "PRN", "AUX", "NUL", "COM0", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", "LPT0", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9"]:
61 path = f"reservedName{ext}"
63 # Check if the path is now empty ...
64 if len(path) == 0:
65 raise Exception("\"path\" did not contain any good characters") from None
67 # Change Unicode encoding if needed ...
68 if ensureNFC and not unicodedata.is_normalized("NFC", path):
69 path = unicodedata.normalize("NFC", path)
71 # Return answer ...
72 return path