Coverage for /Users/davegaeddert/Development/dropseed/plain/plain/plain/assets/compile.py: 26%

46 statements  

« prev     ^ index     » next       coverage.py v7.6.1, created at 2024-10-16 22:04 -0500

1import gzip 

2import hashlib 

3import os 

4import shutil 

5 

6from plain.runtime import settings 

7 

8from .finders import find_assets 

9from .fingerprints import AssetsFingerprintsManifest 

10 

11FINGERPRINT_LENGTH = 7 

12 

13SKIP_COMPRESS_EXTENSIONS = ( 

14 # Images 

15 "jpg", 

16 "jpeg", 

17 "png", 

18 "gif", 

19 "webp", 

20 # Compressed files 

21 "zip", 

22 "gz", 

23 "tgz", 

24 "bz2", 

25 "tbz", 

26 "xz", 

27 "br", 

28 # Fonts 

29 "woff", 

30 "woff2", 

31 # Video 

32 "3gp", 

33 "3gpp", 

34 "asf", 

35 "avi", 

36 "m4v", 

37 "mov", 

38 "mp4", 

39 "mpeg", 

40 "mpg", 

41 "webm", 

42 "wmv", 

43) 

44 

45 

46def get_compiled_path(): 

47 """ 

48 Get the path at runtime to the compiled assets directory. 

49 There's no reason currently for this to be a user-facing setting. 

50 """ 

51 return settings.PLAIN_TEMP_PATH / "assets" / "compiled" 

52 

53 

54def compile_assets(*, target_dir, keep_original, fingerprint, compress): 

55 manifest = AssetsFingerprintsManifest() 

56 

57 for url_path, asset in find_assets().items(): 

58 resolved_path, compiled_paths = compile_asset( 

59 asset=asset, 

60 target_dir=target_dir, 

61 keep_original=keep_original, 

62 fingerprint=fingerprint, 

63 compress=compress, 

64 ) 

65 if resolved_path != url_path: 

66 manifest[url_path] = resolved_path 

67 

68 yield url_path, resolved_path, compiled_paths 

69 

70 if manifest: 

71 manifest.save() 

72 

73 

74def compile_asset(*, asset, target_dir, keep_original, fingerprint, compress): 

75 """ 

76 Compile an asset to multiple output paths. 

77 """ 

78 compiled_paths = [] 

79 

80 # The expected destination for the original asset 

81 target_path = os.path.join(target_dir, asset.url_path) 

82 

83 # Keep track of where the final, resolved asset ends up 

84 resolved_url_path = asset.url_path 

85 

86 # Make sure all the expected directories exist 

87 os.makedirs(os.path.dirname(target_path), exist_ok=True) 

88 

89 base, extension = os.path.splitext(asset.url_path) 

90 

91 # First, copy the original asset over 

92 if keep_original: 

93 shutil.copy(asset.absolute_path, target_path) 

94 compiled_paths.append(target_path) 

95 

96 if fingerprint: 

97 # Fingerprint it with an md5 hash 

98 # (maybe need a setting with fnmatch patterns for files to NOT fingerprint? 

99 # that would allow pre-fingerprinted files to be used as-is, and keep source maps etc in tact) 

100 with open(asset.absolute_path, "rb") as f: 

101 content = f.read() 

102 fingerprint_hash = hashlib.md5(content, usedforsecurity=False).hexdigest()[ 

103 :FINGERPRINT_LENGTH 

104 ] 

105 

106 fingerprinted_basename = f"{base}.{fingerprint_hash}{extension}" 

107 fingerprinted_path = os.path.join(target_dir, fingerprinted_basename) 

108 shutil.copy(asset.absolute_path, fingerprinted_path) 

109 compiled_paths.append(fingerprinted_path) 

110 

111 resolved_url_path = os.path.relpath(fingerprinted_path, target_dir) 

112 

113 if compress and extension not in SKIP_COMPRESS_EXTENSIONS: 

114 for path in compiled_paths.copy(): 

115 gzip_path = f"{path}.gz" 

116 with gzip.GzipFile(gzip_path, "wb") as f: 

117 with open(path, "rb") as f2: 

118 f.write(f2.read()) 

119 compiled_paths.append(gzip_path) 

120 

121 return resolved_url_path, compiled_paths