comparison cutils/treesum.py @ 331:9ee84624587f

treesum: move the handling of base64 digest output into the writers completely
author Franz Glasner <fzglas.hg@dom66.de>
date Fri, 28 Mar 2025 15:16:12 +0100
parents 61cbae10103c
children 5b98810fe367
comparison
equal deleted inserted replaced
330:61cbae10103c 331:9ee84624587f
537 writerstyle = TabularTreesumWriter 537 writerstyle = TabularTreesumWriter
538 else: 538 else:
539 raise NotImplementedError("`output_style'") 539 raise NotImplementedError("`output_style'")
540 540
541 with out_cm as outfp: 541 with out_cm as outfp:
542 writer = writerstyle(outfp) 542 writer = writerstyle(outfp, use_base64=opts.base64)
543 for d in opts.directories: 543 for d in opts.directories:
544 V1DirectoryTreesumGenerator( 544 V1DirectoryTreesumGenerator(
545 opts.algorithm, opts.mmap, opts.base64, 545 opts.algorithm, opts.mmap,
546 opts.follow_symlinks, 546 opts.follow_symlinks,
547 opts.generator, 547 opts.generator,
548 opts.metadata_mode, 548 opts.metadata_mode,
549 opts.metadata_full_mode, 549 opts.metadata_full_mode,
550 opts.metadata_mtime, 550 opts.metadata_mtime,
556 writer, d, comment=opts.comment) 556 writer, d, comment=opts.comment)
557 557
558 558
559 class V1DirectoryTreesumGenerator(object): 559 class V1DirectoryTreesumGenerator(object):
560 560
561 def __init__(self, algorithm, use_mmap, use_base64, 561 def __init__(self, algorithm, use_mmap,
562 follow_symlinks, 562 follow_symlinks,
563 with_generator, 563 with_generator,
564 with_metadata_mode, with_metadata_full_mode, 564 with_metadata_mode, with_metadata_full_mode,
565 with_metadata_mtime, size_only, print_size, utf8_mode, 565 with_metadata_mtime, size_only, print_size, utf8_mode,
566 minimal=None, 566 minimal=None,
567 fnmatcher=None): 567 fnmatcher=None):
568 super(V1DirectoryTreesumGenerator, self).__init__() 568 super(V1DirectoryTreesumGenerator, self).__init__()
569 self._algorithm = algorithm 569 self._algorithm = algorithm
570 self._use_mmap = use_mmap 570 self._use_mmap = use_mmap
571 self._use_base64 = use_base64
572 self._follow_symlinks = follow_symlinks 571 self._follow_symlinks = follow_symlinks
573 self._with_generator = with_generator 572 self._with_generator = with_generator
574 self._with_metadata_mode = with_metadata_mode 573 self._with_metadata_mode = with_metadata_mode
575 self._with_metadata_full_mode = with_metadata_full_mode 574 self._with_metadata_full_mode = with_metadata_full_mode
576 self._with_metadata_mtime = with_metadata_mtime 575 self._with_metadata_mtime = with_metadata_mtime
676 sz = "" if self._print_size else None 675 sz = "" if self._print_size else None
677 self._writer.write_file_digest( 676 self._writer.write_file_digest(
678 self._algorithm[1], 677 self._algorithm[1],
679 b"./@/", 678 b"./@/",
680 dir_dgst.digest(), 679 dir_dgst.digest(),
681 self._use_base64,
682 size=sz) 680 size=sz)
683 self._writer.flush() 681 self._writer.flush()
684 else: 682 else:
685 self._generate(os.path.normpath(root), tuple()) 683 self._generate(os.path.normpath(root), tuple())
686 self._writer.finish() 684 self._writer.finish()
819 "utf-8")) 817 "utf-8"))
820 self._writer.write_file_digest( 818 self._writer.write_file_digest(
821 collector.algorithm or "MD5", 819 collector.algorithm or "MD5",
822 opath, 820 opath,
823 None, 821 None,
824 use_base64=self._use_base64,
825 size=sz) 822 size=sz)
826 return (errno.ESRCH, None, None, None) 823 return (errno.ESRCH, None, None, None)
827 # We got all required infos without errors 824 # We got all required infos without errors
828 self._writer.write_accept_treesum_file(fpath) 825 self._writer.write_accept_treesum_file(fpath)
829 if self._size_only: 826 if self._size_only:
830 self._writer.write_size(opath, collector.size) 827 self._writer.write_size(opath, collector.size)
831 else: 828 else:
832 self._writer.write_file_digest( 829 self._writer.write_file_digest(
833 collector.algorithm, opath, collector.digest, 830 collector.algorithm, opath, collector.digest,
834 use_base64=self._use_base64, size=sz) 831 size=sz)
835 return (0, 832 return (0,
836 collector.algorithm, 833 collector.algorithm,
837 collector.digest, 834 collector.digest,
838 collector.size) 835 collector.size)
839 else: 836 else:
849 if self._size_only: 846 if self._size_only:
850 self._writer.write_size(opath, None) 847 self._writer.write_size(opath, None)
851 else: 848 else:
852 self._writer.write_file_digest( 849 self._writer.write_file_digest(
853 self._algorithm[1], opath, None, 850 self._algorithm[1], opath, None,
854 use_base64=self._use_base64, size=None) 851 size=None)
855 return (eno, None, None, None) 852 return (eno, None, None, None)
856 # 853 #
857 # No treesum file: just process normally with digesting 854 # No treesum file: just process normally with digesting
858 # 855 #
859 if self._utf8_mode: 856 if self._utf8_mode:
947 self._writer.write_file_digest( 944 self._writer.write_file_digest(
948 self._algorithm[1], 945 self._algorithm[1],
949 util.interpolate_bytes( 946 util.interpolate_bytes(
950 b"%s/./@%s", opath, special_tag), 947 b"%s/./@%s", opath, special_tag),
951 linkdgst.digest(), 948 linkdgst.digest(),
952 self._use_base64,
953 size=sz) 949 size=sz)
954 else: 950 else:
955 # 951 #
956 # Follow the symlink to special file and/or handle a 952 # Follow the symlink to special file and/or handle a
957 # special file 953 # special file
988 self._writer.write_file_digest( 984 self._writer.write_file_digest(
989 self._algorithm[1], 985 self._algorithm[1],
990 util.interpolate_bytes( 986 util.interpolate_bytes(
991 b"%s/./%s", opath, special_tag), 987 b"%s/./%s", opath, special_tag),
992 b"", 988 b"",
993 self._use_base64,
994 size=sz) 989 size=sz)
995 elif fso.is_dir: 990 elif fso.is_dir:
996 assert fso.stat is not None # because .is_dir is True 991 assert fso.stat is not None # because .is_dir is True
997 if fso.is_symlink and not self._follow_symlinks.directory: 992 if fso.is_symlink and not self._follow_symlinks.directory:
998 linktgt = walk.WalkDirEntry.from_readlink( 993 linktgt = walk.WalkDirEntry.from_readlink(
1029 else: 1024 else:
1030 sz = "" if self._print_size else None 1025 sz = "" if self._print_size else None
1031 self._writer.write_file_digest( 1026 self._writer.write_file_digest(
1032 self._algorithm[1], 1027 self._algorithm[1],
1033 util.interpolate_bytes(b"%s/./@/", opath), 1028 util.interpolate_bytes(b"%s/./@/", opath),
1034 linkdgst.digest(), 1029 linkdgst.digest())
1035 self._use_base64)
1036 else: 1030 else:
1037 # 1031 #
1038 # Follow the symlink to dir or handle a "real" directory 1032 # Follow the symlink to dir or handle a "real" directory
1039 # 1033 #
1040 1034
1123 sz = "" if self._print_size else None 1117 sz = "" if self._print_size else None
1124 self._writer.write_file_digest( 1118 self._writer.write_file_digest(
1125 self._algorithm[1], 1119 self._algorithm[1],
1126 util.interpolate_bytes(b"%s/./@", opath), 1120 util.interpolate_bytes(b"%s/./@", opath),
1127 linkdgst.digest(), 1121 linkdgst.digest(),
1128 self._use_base64,
1129 size=sz) 1122 size=sz)
1130 else: 1123 else:
1131 # 1124 #
1132 # Follow the symlink to file or handle a "real" file 1125 # Follow the symlink to file or handle a "real" file
1133 # 1126 #
1215 dgst)) 1208 dgst))
1216 sz = (fso.stat.st_size if self._print_size 1209 sz = (fso.stat.st_size if self._print_size
1217 else None) 1210 else None)
1218 self._writer.write_file_digest( 1211 self._writer.write_file_digest(
1219 self._algorithm[1], opath, dgst, 1212 self._algorithm[1], opath, dgst,
1220 use_base64=self._use_base64,
1221 size=sz) 1213 size=sz)
1222 self._writer.flush() 1214 self._writer.flush()
1223 opath = join_output_path(top, None) 1215 opath = join_output_path(top, None)
1224 if opath: 1216 if opath:
1225 if self._utf8_mode: 1217 if self._utf8_mode:
1236 if self._size_only: 1228 if self._size_only:
1237 self._writer.write_size(opath, dir_size) 1229 self._writer.write_size(opath, dir_size)
1238 else: 1230 else:
1239 sz = dir_size if self._print_size else None 1231 sz = dir_size if self._print_size else None
1240 self._writer.write_file_digest( 1232 self._writer.write_file_digest(
1241 self._algorithm[1], opath, dir_dgst.digest(), 1233 self._algorithm[1], opath, dir_dgst.digest(), size=sz)
1242 use_base64=self._use_base64, size=sz)
1243 self._writer.flush() 1234 self._writer.flush()
1244 return (0, self._algorithm[1], dir_dgst.digest(), dir_size) 1235 return (0, self._algorithm[1], dir_dgst.digest(), dir_size)
1245 1236
1246 1237
1247 def join_output_path(top, name): 1238 def join_output_path(top, name):
1294 1285
1295 """ 1286 """
1296 1287
1297 LS = util.b(os.linesep) 1288 LS = util.b(os.linesep)
1298 1289
1299 def __init__(self, outfp): 1290 def __init__(self, outfp, use_base64=False):
1300 self._outfp = outfp 1291 self._outfp = outfp
1292 self.use_base64 = use_base64
1301 self.reset_crc() 1293 self.reset_crc()
1302 1294
1303 @property 1295 @property
1304 def crc(self): 1296 def crc(self):
1305 return self._crc 1297 return self._crc
1334 class TaggedTreesumWriter(WriterBase): 1326 class TaggedTreesumWriter(WriterBase):
1335 1327
1336 """Writer to write treesum digest files in a format similar to BSD 1328 """Writer to write treesum digest files in a format similar to BSD
1337 digest files. 1329 digest files.
1338 1330
1339 Provides high-level methods to write data lines. 1331 Provides high-level methods to write data lines and some very common
1332 attributes that control some aspects of the output format.
1340 1333
1341 """ 1334 """
1342 1335
1343 def __init__(self, outfp, **kwds): 1336 def __init__(self, outfp, **kwds):
1344 # IGNORE **kwds 1337 super(TaggedTreesumWriter, self).__init__(outfp, **kwds)
1345 super(TaggedTreesumWriter, self).__init__(outfp)
1346 1338
1347 def start(self, version): 1339 def start(self, version):
1348 """Begin a new block, reset the current CRC and write the VERSION 1340 """Begin a new block, reset the current CRC and write the VERSION
1349 tag. 1341 tag.
1350 1342
1417 assert isinstance(filename, bytes) 1409 assert isinstance(filename, bytes)
1418 self.write(b"ACCEPT-TREESUM (") 1410 self.write(b"ACCEPT-TREESUM (")
1419 self.write(filename) 1411 self.write(filename)
1420 self.writeln(b")") 1412 self.writeln(b")")
1421 1413
1422 def write_file_digest(self, algorithm, filename, digest, 1414 def write_file_digest(self, algorithm, filename, digest, size=None):
1423 use_base64=False, size=None):
1424 assert isinstance(filename, bytes) 1415 assert isinstance(filename, bytes)
1425 if digest is not None: 1416 if digest is not None:
1426 digest = (base64.b64encode(digest) 1417 digest = (base64.b64encode(digest)
1427 if use_base64 1418 if self.use_base64
1428 else binascii.hexlify(digest)) 1419 else binascii.hexlify(digest))
1429 self.write(util.b(algorithm)) 1420 self.write(util.b(algorithm))
1430 self.write(b" (") 1421 self.write(b" (")
1431 self.write(filename) 1422 self.write(filename)
1432 self.write(b")") 1423 self.write(b")")
1455 Provides high-level methods to write data lines. 1446 Provides high-level methods to write data lines.
1456 1447
1457 """ 1448 """
1458 1449
1459 def __init__(self, outfp, **kwds): 1450 def __init__(self, outfp, **kwds):
1460 super(TabularTreesumWriter, self).__init__(outfp) 1451 super(TabularTreesumWriter, self).__init__(outfp, **kwds)
1461 1452
1462 def start(self, version): 1453 def start(self, version):
1463 """Begin a new block, reset the current CRC and write the VERSION 1454 """Begin a new block, reset the current CRC and write the VERSION
1464 tag. 1455 tag.
1465 1456
1523 def write_accept_treesum_file(self, filename): 1514 def write_accept_treesum_file(self, filename):
1524 assert isinstance(filename, bytes) 1515 assert isinstance(filename, bytes)
1525 self.write(b"ACCEPT-TREESUM\t") 1516 self.write(b"ACCEPT-TREESUM\t")
1526 self.writeln(filename) 1517 self.writeln(filename)
1527 1518
1528 def write_file_digest(self, algorithm, filename, digest, 1519 def write_file_digest(self, algorithm, filename, digest, size=None):
1529 use_base64=False, size=None):
1530 assert isinstance(filename, bytes) 1520 assert isinstance(filename, bytes)
1531 if digest is not None: 1521 if digest is not None:
1532 digest = (base64.b64encode(digest) 1522 digest = (base64.b64encode(digest)
1533 if use_base64 1523 if self.use_base64
1534 else binascii.hexlify(digest)) 1524 else binascii.hexlify(digest))
1535 self.write(util.b(algorithm)) 1525 self.write(util.b(algorithm))
1536 self.write(b":") 1526 self.write(b":")
1537 if digest is not None: 1527 if digest is not None:
1538 self.write(digest) 1528 self.write(digest)