comparison cutils/treesum.py @ 205:63088d3675bb

Add a "GENERATOR" line with control flats to treesum.py
author Franz Glasner <fzglas.hg@dom66.de>
date Wed, 22 Jan 2025 17:45:37 +0100
parents 07f1d79e6674
children 73d22943da5a
comparison
equal deleted inserted replaced
204:07f1d79e6674 205:63088d3675bb
73 "--full-mode", action="store_true", dest="metadata_full_mode", 73 "--full-mode", action="store_true", dest="metadata_full_mode",
74 help="Consider all mode bits as returned from stat(2) when " 74 help="Consider all mode bits as returned from stat(2) when "
75 "computing directory digests. " 75 "computing directory digests. "
76 "Note that mode bits on symbolic links itself are not " 76 "Note that mode bits on symbolic links itself are not "
77 "considered.") 77 "considered.")
78 gp.add_argument(
79 "--generator", choices=("normal", "full", "none"),
80 default="normal",
81 help="""Put a `GENERATOR' line into the output.
82 `full' prints full Python and OS/platform version information,
83 `normal' prints just whether Python 2 or Python 3 is used, and `none'
84 suppresses the output completely. The default is `normal'.""")
78 gp.add_argument( 85 gp.add_argument(
79 "--logical", "-L", dest="logical", action="store_true", 86 "--logical", "-L", dest="logical", action="store_true",
80 default=None, 87 default=None,
81 help="Follow symbolic links given on command line arguments." 88 help="Follow symbolic links given on command line arguments."
82 " Note that this is a different setting as to follow symbolic" 89 " Note that this is a different setting as to follow symbolic"
247 append_output=False, 254 append_output=False,
248 base64=False, 255 base64=False,
249 comment=[], 256 comment=[],
250 follow_directory_symlinks=False, 257 follow_directory_symlinks=False,
251 full_mode=False, 258 full_mode=False,
259 generator="normal",
252 logical=None, 260 logical=None,
253 minimal=None, 261 minimal=None,
254 mode=False, 262 mode=False,
255 mmap=None, 263 mmap=None,
256 mtime=False, 264 mtime=False,
263 algorithm=util.argv2algo(algorithm), 271 algorithm=util.argv2algo(algorithm),
264 append_output=append_output, 272 append_output=append_output,
265 base64=base64, 273 base64=base64,
266 comment=comment, 274 comment=comment,
267 follow_directory_symlinks=follow_directory_symlinks, 275 follow_directory_symlinks=follow_directory_symlinks,
276 generator=generator,
268 logical=logical, 277 logical=logical,
269 minimal=minimal, 278 minimal=minimal,
270 mmap=mmap, 279 mmap=mmap,
271 metadata_full_mode=full_mode, 280 metadata_full_mode=full_mode,
272 metadata_mode=mode, 281 metadata_mode=mode,
319 for d in opts.directories: 328 for d in opts.directories:
320 329
321 V1DirectoryTreesumGenerator( 330 V1DirectoryTreesumGenerator(
322 opts.algorithm, opts.mmap, opts.base64, 331 opts.algorithm, opts.mmap, opts.base64,
323 opts.logical, opts.follow_directory_symlinks, 332 opts.logical, opts.follow_directory_symlinks,
333 opts.generator,
324 opts.metadata_mode, 334 opts.metadata_mode,
325 opts.metadata_full_mode, 335 opts.metadata_full_mode,
326 opts.metadata_mtime, 336 opts.metadata_mtime,
327 opts.size_only, 337 opts.size_only,
328 opts.print_size, 338 opts.print_size,
333 343
334 class V1DirectoryTreesumGenerator(object): 344 class V1DirectoryTreesumGenerator(object):
335 345
336 def __init__(self, algorithm, use_mmap, use_base64, 346 def __init__(self, algorithm, use_mmap, use_base64,
337 handle_root_logical, follow_directory_symlinks, 347 handle_root_logical, follow_directory_symlinks,
348 with_generator,
338 with_metadata_mode, with_metadata_full_mode, 349 with_metadata_mode, with_metadata_full_mode,
339 with_metadata_mtime, size_only, print_size, utf8_mode, 350 with_metadata_mtime, size_only, print_size, utf8_mode,
340 minimal=None,): 351 minimal=None,):
341 super(V1DirectoryTreesumGenerator, self).__init__() 352 super(V1DirectoryTreesumGenerator, self).__init__()
342 self._algorithm = algorithm 353 self._algorithm = algorithm
343 self._use_mmap = use_mmap 354 self._use_mmap = use_mmap
344 self._use_base64 = use_base64 355 self._use_base64 = use_base64
345 self._handle_root_logical = handle_root_logical 356 self._handle_root_logical = handle_root_logical
346 self._follow_directory_symlinks = follow_directory_symlinks 357 self._follow_directory_symlinks = follow_directory_symlinks
358 self._with_generator = with_generator
347 self._with_metadata_mode = with_metadata_mode 359 self._with_metadata_mode = with_metadata_mode
348 self._with_metadata_full_mode = with_metadata_full_mode 360 self._with_metadata_full_mode = with_metadata_full_mode
349 self._with_metadata_mtime = with_metadata_mtime 361 self._with_metadata_mtime = with_metadata_mtime
350 self._size_only = size_only 362 self._size_only = size_only
351 self._print_size = print_size 363 self._print_size = print_size
362 self._outfp.resetdigest() 374 self._outfp.resetdigest()
363 self._outfp.write(format_bsd_line("VERSION", "1", None, False)) 375 self._outfp.write(format_bsd_line("VERSION", "1", None, False))
364 self._outfp.write(format_bsd_line( 376 self._outfp.write(format_bsd_line(
365 "FSENCODING", util.n(walk.getfsencoding().upper()), None, False)) 377 "FSENCODING", util.n(walk.getfsencoding().upper()), None, False))
366 self._outfp.flush() 378 self._outfp.flush()
379
380 if self._with_generator == "none":
381 pass # do nothing
382 elif self._with_generator == "normal":
383 self._outfp.write(format_bsd_line(
384 "GENERATOR", None, b"PY2" if util.PY2 else b"PY3", False))
385 elif self._with_generator == "full":
386 import platform
387 info = "%s %s, %s" % (platform.python_implementation(),
388 platform.python_version(),
389 platform.platform())
390 self._outfp.write(format_bsd_line(
391 "GENERATOR", None, info.encode("utf-8"), False))
392 else:
393 raise NotImplementedError(
394 "not implemented: %s" % (self._with_generator,))
367 395
368 # 396 #
369 # Note: Given non-default flags that are relevant for 397 # Note: Given non-default flags that are relevant for
370 # directory traversal. 398 # directory traversal.
371 # 399 #
765 if what in (b"FSENCODING", b"ISOTIMESTAMP", b"FLAGS", b"VERSION", 793 if what in (b"FSENCODING", b"ISOTIMESTAMP", b"FLAGS", b"VERSION",
766 b"CRC32"): 794 b"CRC32"):
767 assert filename is None 795 assert filename is None
768 return util.interpolate_bytes(b"%s = %s%s", what, util.b(value), ls) 796 return util.interpolate_bytes(b"%s = %s%s", what, util.b(value), ls)
769 assert filename is not None 797 assert filename is not None
770 if what in (b"COMMENT", b"ERROR"): 798 if what in (b"COMMENT", b"ERROR", b"GENERATOR"):
771 return util.interpolate_bytes( 799 return util.interpolate_bytes(
772 b"%s (%s)%s", what, util.b(filename, "utf-8"), ls) 800 b"%s (%s)%s", what, util.b(filename, "utf-8"), ls)
773 if not isinstance(filename, bytes): 801 if not isinstance(filename, bytes):
774 filename = util.fsencode(filename) 802 filename = util.fsencode(filename)
775 if what == b"SIZE": 803 if what == b"SIZE":
798 826
799 """ 827 """
800 828
801 PATTERN0 = re.compile(br"\A[ \t]*\r?\n\Z") # empty lines 829 PATTERN0 = re.compile(br"\A[ \t]*\r?\n\Z") # empty lines
802 PATTERN1 = re.compile(br"\A(VERSION|FSENCODING|FLAGS|TIMESTAMP|ISOTIMESTAMP|CRC32)[ \t]*=[ \t]*([^ \t]+)[ \t]*\r?\n\Z") # noqa: E501 line too long 830 PATTERN1 = re.compile(br"\A(VERSION|FSENCODING|FLAGS|TIMESTAMP|ISOTIMESTAMP|CRC32)[ \t]*=[ \t]*([^ \t]+)[ \t]*\r?\n\Z") # noqa: E501 line too long
803 PATTERN2 = re.compile(br"\A(ROOT|COMMENT|ERROR)[ \t]*\((.*)\)[ \t]*\r?\n\Z") # noqa: E501 line too long 831 PATTERN2 = re.compile(br"\A(ROOT|COMMENT|ERROR|GENERATOR)[ \t]*\((.*)\)[ \t]*\r?\n\Z") # noqa: E501 line too long
804 PATTERN3 = re.compile(br"\ASIZE[ \t]*\((.*)\)[ \t]*=[ \t]*(\d+)[ \t]*\r?\n\Z") # noqa: E501 line too long 832 PATTERN3 = re.compile(br"\ASIZE[ \t]*\((.*)\)[ \t]*=[ \t]*(\d+)[ \t]*\r?\n\Z") # noqa: E501 line too long
805 PATTERN4 = re.compile(br"\A([A-Za-z0-9_-]+)[ \t]*\((.*)\)[ \t]*=[ \t]*([A-Za-z0-9=+/]+)(,(\d+))?[ \t]*\r?\n\Z") # noqa: E501 line too long 833 PATTERN4 = re.compile(br"\A([A-Za-z0-9_-]+)[ \t]*\((.*)\)[ \t]*=[ \t]*([A-Za-z0-9=+/]+)(,(\d+))?[ \t]*\r?\n\Z") # noqa: E501 line too long
806 834
807 def __init__(self, _fp, _filename, _own_fp): 835 def __init__(self, _fp, _filename, _own_fp):
808 self._fp = _fp 836 self._fp = _fp
925 return (util.n(mo.group(1)), util.n(mo.group(2))) 953 return (util.n(mo.group(1)), util.n(mo.group(2)))
926 else: 954 else:
927 mo = self.PATTERN2.search(line) 955 mo = self.PATTERN2.search(line)
928 if mo: 956 if mo:
929 self._update_crc(line) 957 self._update_crc(line)
930 if mo.group(1) in (b"COMMENT", b"ERROR"): 958 if mo.group(1) in (b"COMMENT", b"ERROR", b"GENERATOR"):
931 return (util.u(mo.group(1)), util.u(mo.group(2), "utf-8")) 959 return (util.u(mo.group(1)), util.u(mo.group(2), "utf-8"))
932 elif mo.group(1) == b"ROOT": 960 elif mo.group(1) == b"ROOT":
933 return ("ROOT", mo.group(2)) 961 return ("ROOT", mo.group(2))
934 assert False, line 962 assert False, line
935 else: 963 else: