jsonpatch 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. #!/tmp/build/80754af9/jsonpatch_1615747632069/_h_env_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_plac/bin/python
  2. # -*- coding: utf-8 -*-
  3. import sys
  4. import os.path
  5. import json
  6. import jsonpatch
  7. import tempfile
  8. import argparse
  9. parser = argparse.ArgumentParser(
  10. description='Apply a JSON patch on a JSON file')
  11. parser.add_argument('ORIGINAL', type=argparse.FileType('r'),
  12. help='Original file')
  13. parser.add_argument('PATCH', type=argparse.FileType('r'),
  14. nargs='?', default=sys.stdin,
  15. help='Patch file (read from stdin if omitted)')
  16. parser.add_argument('--indent', type=int, default=None,
  17. help='Indent output by n spaces')
  18. parser.add_argument('-b', '--backup', action='store_true',
  19. help='Back up ORIGINAL if modifying in-place')
  20. parser.add_argument('-i', '--in-place', action='store_true',
  21. help='Modify ORIGINAL in-place instead of to stdout')
  22. parser.add_argument('-v', '--version', action='version',
  23. version='%(prog)s ' + jsonpatch.__version__)
  24. parser.add_argument('-u', '--preserve-unicode', action='store_true',
  25. help='Output Unicode character as-is without using Code Point')
  26. def main():
  27. try:
  28. patch_files()
  29. except KeyboardInterrupt:
  30. sys.exit(1)
  31. def patch_files():
  32. """ Diffs two JSON files and prints a patch """
  33. args = parser.parse_args()
  34. doc = json.load(args.ORIGINAL)
  35. patch = json.load(args.PATCH)
  36. result = jsonpatch.apply_patch(doc, patch)
  37. if args.in_place:
  38. dirname = os.path.abspath(os.path.dirname(args.ORIGINAL.name))
  39. try:
  40. # Attempt to replace the file atomically. We do this by
  41. # creating a temporary file in the same directory as the
  42. # original file so we can atomically move the new file over
  43. # the original later. (This is done in the same directory
  44. # because atomic renames do not work across mount points.)
  45. fd, pathname = tempfile.mkstemp(dir=dirname)
  46. fp = os.fdopen(fd, 'w')
  47. atomic = True
  48. except OSError:
  49. # We failed to create the temporary file for an atomic
  50. # replace, so fall back to non-atomic mode by backing up
  51. # the original (if desired) and writing a new file.
  52. if args.backup:
  53. os.rename(args.ORIGINAL.name, args.ORIGINAL.name + '.orig')
  54. fp = open(args.ORIGINAL.name, 'w')
  55. atomic = False
  56. else:
  57. # Since we're not replacing the original file in-place, write
  58. # the modified JSON to stdout instead.
  59. fp = sys.stdout
  60. # By this point we have some sort of file object we can write the
  61. # modified JSON to.
  62. json.dump(result, fp, indent=args.indent, ensure_ascii=not(args.preserve_unicode))
  63. fp.write('\n')
  64. if args.in_place:
  65. # Close the new file. If we aren't replacing atomically, this
  66. # is our last step, since everything else is already in place.
  67. fp.close()
  68. if atomic:
  69. try:
  70. # Complete the atomic replace by linking the original
  71. # to a backup (if desired), fixing up the permissions
  72. # on the temporary file, and moving it into place.
  73. if args.backup:
  74. os.link(args.ORIGINAL.name, args.ORIGINAL.name + '.orig')
  75. os.chmod(pathname, os.stat(args.ORIGINAL.name).st_mode)
  76. os.rename(pathname, args.ORIGINAL.name)
  77. except OSError:
  78. # In the event we could not actually do the atomic
  79. # replace, unlink the original to move it out of the
  80. # way and finally move the temporary file into place.
  81. os.unlink(args.ORIGINAL.name)
  82. os.rename(pathname, args.ORIGINAL.name)
  83. if __name__ == "__main__":
  84. main()