#!/usr/bin/env python """Determine the filename for a JSON model. This is a utility script that will parse a service's metadata file and determine where this file should be placed. It hides the details of botocore's internal file layout for JSON models. It its simplest usage you give it a path to a file and it prints the location within botocore:: $ scripts/get-model-filename /tmp/myfile.json /Users/foo/botocore/botocore/data/aws/cloudwatch/2010-08-01.normal.json If you want, you can use the ``-c`` option to actually copy the file to this location. When this option is specified, the parent directory will be created if it does not exist. $ scripts/get-model-filename -c /tmp/myfile.json /Users/foo/botocore/data/aws/cloudwatch/2010-08-01.normal.json Copied: /tmp/myfile.json -> /Users/foo/botocore/data/aws/cloudwatch/2010-08-01.normal.json """ # Note we're using optparse for 2.6 compat. # We don't have particularly complicated command line # parsing requirements so we'll deal with it. import optparse import unittest import sys import json import shutil import os try: from StringIO import StringIO except ImportError: # Python3 we need to import from the io module. from io import StringIO def determine_json_data_path(fileobj): """Determine the filepath for a JSON model. This is a utility function that will introspect a JSON file and determine where it should be placed in botocore/data/aws/. """ parsed = json.load(fileobj) if 'metadata' in parsed: return _determine_path_from_metadata(parsed) else: raise ValueError("Could not determine file path for JSON model, model " "is missing required 'metadata' key.") def _determine_path_from_metadata(parsed): metadata = parsed['metadata'] if 'serviceAbbreviation' in metadata: service_name = metadata['serviceAbbreviation'].lower().replace(' ', '') if service_name.startswith('amazon'): service_name = service_name[6:] elif service_name.startswith('aws'): service_name = service_name[3:] else: service_name = metadata['endpointPrefix'] # There's one single special case where we can't automatically # figure out, and that's elasticloadbalancing -> elb so we have # to hardcode this here. if service_name == 'elasticloadbalancing': service_name = 'elb' api_version = metadata['apiVersion'] return os.path.join( os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'botocore', 'data', 'aws', service_name, api_version + '.normal.json') class TestDeterminePath(unittest.TestCase): def given_metadata(self, metadata): self.metadata = {'metadata': metadata} def assert_filename_is(self, filename): source_file = StringIO(json.dumps(self.metadata)) actual = determine_json_data_path(source_file) self.assertTrue(actual.endswith(filename)) def test_can_determine_path_from_metadata(self): self.given_metadata( {'apiVersion': '2015-01-01', 'endpointPrefix': 'foo'}) self.assert_filename_is('botocore/data/aws/foo/2015-01-01.normal.json') self.given_metadata( {'apiVersion': '2015-01-01', 'endpointPrefix': 'foo', 'serviceAbbreviation': 'Amazon Bar'}) self.assert_filename_is('botocore/data/aws/bar/2015-01-01.normal.json') self.given_metadata( {'apiVersion': '2015-01-01', 'endpointPrefix': 'foo', 'serviceAbbreviation': 'AWS Baz'}) self.assert_filename_is('botocore/data/aws/baz/2015-01-01.normal.json') self.given_metadata( {'apiVersion': '2015-01-01', 'endpointPrefix': 'foo', 'serviceAbbreviation': 'something else'}) self.assert_filename_is( 'botocore/data/aws/somethingelse/2015-01-01.normal.json') # The special casing of elasticloadbalancing -> elb. self.given_metadata( {'apiVersion': '2015-01-01', 'endpointPrefix': 'elasticloadbalancing'}), self.assert_filename_is( 'botocore/data/aws/elb/2015-01-01.normal.json') def main(): parser = optparse.OptionParser(usage=__doc__) parser.add_option( '-c', '--copy', action='store_true', default=False, help='Copy the file to the appropriate location. ') parser.add_option( '-t', '--test', action='store_true', default=False, help='Dev use only, run the unit tests.') opts, args = parser.parse_args() if opts.test: sys.argv[:] = [sys.argv[0]] unittest.main() return 0 if len(args) != 1: sys.stderr.write("Must provide the filename of the JSON model " "as an argument.\n") return 1 source_filename = args[0] if not source_filename.endswith('.normal.json'): sys.stderr.write("Only the *.normal.json files are supported.\n") return 1 f = open(source_filename) try: filename = determine_json_data_path(f) sys.stdout.write(filename) sys.stdout.write("\n") finally: f.close() if opts.copy: if not os.path.isdir(os.path.dirname(filename)): os.makedirs(os.path.dirname(filename)) shutil.copy(source_filename, filename) sys.stdout.write("Copied: %s -> %s\n" % (source_filename, filename)) return 0 if __name__ == '__main__': sys.exit(main())