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

1#!/usr/bin/env python3 

2 

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. 

12 

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 

21 

22 Notes 

23 ----- 

24 Copyright 2017 Thomas Guymer [1]_ 

25 

26 References 

27 ---------- 

28 .. [1] PyGuymer3, https://github.com/Guymer/PyGuymer3 

29 """ 

30 

31 # Import standard modules ... 

32 import os 

33 import unicodedata 

34 

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, "") 

39 

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, "") 

46 

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}" 

62 

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 

66 

67 # Change Unicode encoding if needed ... 

68 if ensureNFC and not unicodedata.is_normalized("NFC", path): 

69 path = unicodedata.normalize("NFC", path) 

70 

71 # Return answer ... 

72 return path