You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
108 lines
3.8 KiB
108 lines
3.8 KiB
#!/home/patrickd/swarms/swarmsenv/bin/python3.12
|
|
# -*- coding: utf-8 -*-
|
|
|
|
import sys
|
|
import os.path
|
|
import json
|
|
import jsonpatch
|
|
import tempfile
|
|
import argparse
|
|
|
|
|
|
parser = argparse.ArgumentParser(
|
|
description='Apply a JSON patch on a JSON file')
|
|
parser.add_argument('ORIGINAL', type=argparse.FileType('r'),
|
|
help='Original file')
|
|
parser.add_argument('PATCH', type=argparse.FileType('r'),
|
|
nargs='?', default=sys.stdin,
|
|
help='Patch file (read from stdin if omitted)')
|
|
parser.add_argument('--indent', type=int, default=None,
|
|
help='Indent output by n spaces')
|
|
parser.add_argument('-b', '--backup', action='store_true',
|
|
help='Back up ORIGINAL if modifying in-place')
|
|
parser.add_argument('-i', '--in-place', action='store_true',
|
|
help='Modify ORIGINAL in-place instead of to stdout')
|
|
parser.add_argument('-v', '--version', action='version',
|
|
version='%(prog)s ' + jsonpatch.__version__)
|
|
parser.add_argument('-u', '--preserve-unicode', action='store_true',
|
|
help='Output Unicode character as-is without using Code Point')
|
|
|
|
def main():
|
|
try:
|
|
patch_files()
|
|
except KeyboardInterrupt:
|
|
sys.exit(1)
|
|
|
|
|
|
def patch_files():
|
|
""" Diffs two JSON files and prints a patch """
|
|
args = parser.parse_args()
|
|
doc = json.load(args.ORIGINAL)
|
|
patch = json.load(args.PATCH)
|
|
result = jsonpatch.apply_patch(doc, patch)
|
|
|
|
if args.in_place:
|
|
dirname = os.path.abspath(os.path.dirname(args.ORIGINAL.name))
|
|
|
|
try:
|
|
# Attempt to replace the file atomically. We do this by
|
|
# creating a temporary file in the same directory as the
|
|
# original file so we can atomically move the new file over
|
|
# the original later. (This is done in the same directory
|
|
# because atomic renames do not work across mount points.)
|
|
|
|
fd, pathname = tempfile.mkstemp(dir=dirname)
|
|
fp = os.fdopen(fd, 'w')
|
|
atomic = True
|
|
|
|
except OSError:
|
|
# We failed to create the temporary file for an atomic
|
|
# replace, so fall back to non-atomic mode by backing up
|
|
# the original (if desired) and writing a new file.
|
|
|
|
if args.backup:
|
|
os.rename(args.ORIGINAL.name, args.ORIGINAL.name + '.orig')
|
|
fp = open(args.ORIGINAL.name, 'w')
|
|
atomic = False
|
|
|
|
else:
|
|
# Since we're not replacing the original file in-place, write
|
|
# the modified JSON to stdout instead.
|
|
|
|
fp = sys.stdout
|
|
|
|
# By this point we have some sort of file object we can write the
|
|
# modified JSON to.
|
|
|
|
json.dump(result, fp, indent=args.indent, ensure_ascii=not(args.preserve_unicode))
|
|
fp.write('\n')
|
|
|
|
if args.in_place:
|
|
# Close the new file. If we aren't replacing atomically, this
|
|
# is our last step, since everything else is already in place.
|
|
|
|
fp.close()
|
|
|
|
if atomic:
|
|
try:
|
|
# Complete the atomic replace by linking the original
|
|
# to a backup (if desired), fixing up the permissions
|
|
# on the temporary file, and moving it into place.
|
|
|
|
if args.backup:
|
|
os.link(args.ORIGINAL.name, args.ORIGINAL.name + '.orig')
|
|
os.chmod(pathname, os.stat(args.ORIGINAL.name).st_mode)
|
|
os.rename(pathname, args.ORIGINAL.name)
|
|
|
|
except OSError:
|
|
# In the event we could not actually do the atomic
|
|
# replace, unlink the original to move it out of the
|
|
# way and finally move the temporary file into place.
|
|
|
|
os.unlink(args.ORIGINAL.name)
|
|
os.rename(pathname, args.ORIGINAL.name)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|