diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 94a25f7..5ace414 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -3,4 +3,4 @@ - \ No newline at end of file + diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..4fb5a96 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,16 @@ +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v2.4.0 + hooks: + - id: check-added-large-files + args: ['--maxkb=100'] + - id: check-ast + - id: check-byte-order-marker + - id: check-case-conflict + - id: check-docstring-first + - id: check-executables-have-shebangs + - id: check-merge-conflict + - id: end-of-file-fixer + - id: fix-encoding-pragma + - id: mixed-line-ending + - id: trailing-whitespace diff --git a/README.md b/README.md index 40cf7eb..fe98044 100644 --- a/README.md +++ b/README.md @@ -33,12 +33,12 @@ Usage: yaml-plist.py [] **Notes:** -1. With `plistyamlplist.py`, if you do not specify an `output-file` value, the script determines if the `input-file` is - a plist or yaml file. If a plist file, the `input-file` name will be appended with `.yaml` for the output file. If a +1. With `plistyamlplist.py`, if you do not specify an `output-file` value, the script determines if the `input-file` is + a plist or yaml file. If a plist file, the `input-file` name will be appended with `.yaml` for the output file. If a yaml file, the output file name will be the `input-file` name with `.yaml` removed. -2. With `plist_yaml.py`, if you do not specify an `output-file` value, the `input-file` name will be appended with +2. With `plist_yaml.py`, if you do not specify an `output-file` value, the `input-file` name will be appended with `.yaml` for the output file. -3. With `yaml_plist.py`, if you do not specify an `output-file` value, and the `input-file` name ends with `.yaml`, +3. With `yaml_plist.py`, if you do not specify an `output-file` value, and the `input-file` name ends with `.yaml`, the output file name will be the `input-file` name with `.yaml` removed. 4. With `plist_yaml.py`, you may have to first convert a binary plist to text format using `plutil`. @@ -73,8 +73,8 @@ If you have a folder named `YAML` in your path, and you do not supply a destinat will determine if a corresponding folder exists in the path without 'YAML'. For example, consider the following file: /Users/myuser/gitrepo/YAML/product/com.something.plist.yaml - -If the folder `/Users/myuser/gitrepo/product` exists, the converted file will be created/overwritten at: + +If the folder `/Users/myuser/gitrepo/product` exists, the converted file will be created/overwritten at: /Users/myuser/gitrepo/product/com.something.plist diff --git a/plistyamlplist.py b/plistyamlplist.py index 7f3f3bc..7316ab6 100755 --- a/plistyamlplist.py +++ b/plistyamlplist.py @@ -1,8 +1,8 @@ #!/usr/bin/env python +# -*- coding: utf-8 -*- -""" -If this script is run directly, it takes an input file and an output file from -the command line. It detects if the input file is a YAML or a PLIST file, +"""If this script is run directly, it takes an input file and an output file +from the command line. It detects if the input file is a YAML or a PLIST file, and converts to the other format: plistyamlplist.py @@ -19,22 +19,26 @@ def usage(): - """print help""" + """print help.""" print("Usage: plistyamlplist.py \n") - print("If is a PLIST and is omitted,\n" - " is converted to .yaml\n") - print("If ends in .yaml or .yml and is omitted,\n" - ".yaml is converted to PLIST format with name \n") + print( + "If is a PLIST and is omitted,\n" + " is converted to .yaml\n" + ) + print( + "If ends in .yaml or .yml and is omitted,\n" + ".yaml is converted to PLIST format with name \n" + ) def check_if_plist(in_path): - """rather than restrict by filename, check if the file is a plist by reading - the second line of the file for the PLIST declaration""" + """rather than restrict by filename, check if the file is a plist by + reading the second line of the file for the PLIST declaration.""" with open(in_path) as fp: for i, line in enumerate(fp): if i == 1: # print line - if line.find('PLIST 1.0') == -1: + if line.find("PLIST 1.0") == -1: is_plist = False else: is_plist = True @@ -57,13 +61,13 @@ def check_for_yaml_folder(check_path): print('Path exists : {}'.format(out_path)) return out_path else: - print('Path does not exist : {}'.format(out_path)) - print('Please create this folder and try again') + print("Path does not exist : {}".format(out_path)) + print("Please create this folder and try again") exit(1) def main(): - """get the command line inputs if running this script directly""" + """get the command line inputs if running this script directly.""" if len(sys.argv) < 2: usage() exit(1) @@ -72,7 +76,7 @@ def main(): try: sys.argv[2] except IndexError: - if in_path.endswith('.yaml') or in_path.endswith('.yml'): + if in_path.endswith(".yaml") or in_path.endswith(".yml"): out_dir = check_for_yaml_folder(in_path) if out_dir: filename, file_extension = os.path.splitext(os.path.basename(in_path)) @@ -91,7 +95,7 @@ def main(): out_path = sys.argv[2] # auto-determine which direction the conversion should go - if in_path.endswith('.yaml'): + if in_path.endswith(".yaml"): yaml_plist(in_path, out_path) else: if check_if_plist(in_path): @@ -100,8 +104,8 @@ def main(): print("\nERROR: Input File is neither PLIST nor YAML format.\n") usage() exit(1) - print('Wrote to : {}'.format(out_path)) + print("Wrote to : {}".format(out_path)) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plistyamlplist_lib/plist_yaml.py b/plistyamlplist_lib/plist_yaml.py index 9fd028f..dc63b38 100755 --- a/plistyamlplist_lib/plist_yaml.py +++ b/plistyamlplist_lib/plist_yaml.py @@ -1,9 +1,9 @@ #!/usr/bin/env python +# -*- coding: utf-8 -*- -""" -If this script is run directly, it takes an input file and an output file from -the command line. The input file must be in PLIST format. -The output file will be in YAML format: +"""If this script is run directly, it takes an input file and an output file +from the command line. The input file must be in PLIST format. The output file +will be in YAML format: plist_yaml.py @@ -12,49 +12,92 @@ """ import sys +from collections import OrderedDict + +try: + from plistlib import Data # Python 3 + from plistlib import load as load_plist +except ImportError: + from plistlib import Data # Python 2 + from plistlib import readPlist as load_plist + import yaml -from plistlib import readPlist, Data -def normalize_types(input): - """ - This allows YAML and JSON to store Data fields as strings. However, this - operation is irreversible. Only use if read-only access to the plist is - required. +def represent_ordereddict(dumper, data): + value = [] + + for item_key, item_value in data.items(): + node_key = dumper.represent_data(item_key) + node_value = dumper.represent_data(item_value) + + value.append((node_key, node_value)) + + return yaml.nodes.MappingNode(u"tag:yaml.org,2002:map", value) + + +def normalize_types(input_data): + """This allows YAML and JSON to store Data fields as strings. + + However, this operation is irreversible. Only use if read-only + access to the plist is required. """ - if isinstance(input, Data): return input.data - if isinstance(input, list): + if isinstance(input_data, Data): + return input_data.data + if isinstance(input_data, list): retval = [] - for child in input: + for child in input_data: retval.append(normalize_types(child)) return retval - if isinstance(input, dict): + if isinstance(input_data, dict): retval = {} - for key, child in input.iteritems(): - retval[key] = normalize_types(child) + for key in input_data: + retval[key] = normalize_types(input_data[key]) return retval - return input + return input_data + + +def sort_autopkg_processes(recipe): + """If input is an AutoPkg recipe, adjust the processor dictionaries such + that the Arguments key is ordered last. + + This usually puts the Processor key first, which makes the process + list more human-readable. + """ + if "Process" in recipe: + process = recipe["Process"] + new_process = [] + for processor in process: + if "Arguments" in processor: + processor = OrderedDict(processor) + processor.move_to_end("Arguments") + new_process.append(processor) + recipe["Process"] = new_process + return recipe def convert(xml): - """Do the conversion""" - return yaml.dump(xml, width=float('inf'), default_flow_style=False) + """Do the conversion.""" + yaml.add_representer(OrderedDict, represent_ordereddict) + return yaml.dump(xml, width=float("inf"), default_flow_style=False) def plist_yaml(in_path, out_path): - """Convert plist to yaml""" - in_file = open(in_path, 'r') - input = readPlist(in_file) + """Convert plist to yaml.""" + with open(in_path, "rb") as in_file: + input_data = load_plist(in_file) - normalized = normalize_types(input) + normalized = normalize_types(input_data) + if sys.version_info.major == 3 and in_path.endswith((".recipe", ".recipe.plist")): + normalized = sort_autopkg_processes(normalized) output = convert(normalized) - out_file = open(out_path, 'w') + out_file = open(out_path, "w") out_file.writelines(output) def main(): - """Get the command line inputs if running this script directly""" + """Get the command line inputs if running this script directly.""" if len(sys.argv) < 2: print("Usage: plist_yaml.py ") sys.exit(1) @@ -71,5 +114,5 @@ def main(): plist_yaml(in_path, out_path) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plistyamlplist_lib/yaml_plist.py b/plistyamlplist_lib/yaml_plist.py index 358bbe9..d944c9b 100755 --- a/plistyamlplist_lib/yaml_plist.py +++ b/plistyamlplist_lib/yaml_plist.py @@ -1,9 +1,9 @@ #!/usr/bin/env python +# -*- coding: utf-8 -*- -""" -If this script is run directly, it takes an input file and an output file from -the command lineThe input file must be in YAML format. -The output file will be in PLIST format: +"""If this script is run directly, it takes an input file and an output file +from the command lineThe input file must be in YAML format. The output file +will be in PLIST format: yaml_plist.py @@ -19,27 +19,29 @@ from plistlib import writePlistToString +import yaml + -def convert(yaml): - """Do the conversion""" - lines = writePlistToString(yaml).splitlines() - lines.append('') +def convert(data): + """Do the conversion.""" + lines = writePlistToString(data).splitlines() + lines.append("") return "\n".join(lines) def yaml_plist(in_path, out_path): - """Convert yaml to plist""" - in_file = open(in_path, 'r') - out_file = open(out_path, 'w') + """Convert yaml to plist.""" + in_file = open(in_path, "r") + out_file = open(out_path, "w") - input = yaml.safe_load(in_file) - output = convert(input) + input_data = yaml.safe_load(in_file) + output = convert(input_data) out_file.writelines(output) def main(): - """Get the command line inputs if running this script directly""" + """Get the command line inputs if running this script directly.""" if len(sys.argv) < 2: print("Usage: yaml_plist.py ") sys.exit(1) @@ -60,5 +62,5 @@ def main(): yaml_plist(in_path, out_path) -if __name__ == '__main__': +if __name__ == "__main__": main()