diff --git a/.yapfignore b/.yapfignore index 51728a0bd5656..373b0859caf7f 100644 --- a/.yapfignore +++ b/.yapfignore @@ -5,6 +5,3 @@ third_party/blink/tools/blinkpy/third_party/* third_party/blink/web_tests/external/wpt/* tools/valgrind/asan/third_party/asan_symbolize.py - -# TODO(crbug.com/1116155): Enable this for formatting by yapf. -tools/json_schema_compiler/* diff --git a/tools/json_schema_compiler/cc_generator.py b/tools/json_schema_compiler/cc_generator.py index a64e36c72ae85..1269ad43a94ca 100644 --- a/tools/json_schema_compiler/cc_generator.py +++ b/tools/json_schema_compiler/cc_generator.py @@ -9,7 +9,9 @@ import schema_util import util_cc_helper from cpp_namespace_environment import CppNamespaceEnvironment + class CCGenerator(object): + def __init__(self, type_generator): self._type_generator = type_generator @@ -20,12 +22,12 @@ class CCGenerator(object): class _Generator(object): """A .cc generator for a namespace. """ + def __init__(self, namespace, cpp_type_generator): assert type(namespace.environment) is CppNamespaceEnvironment self._namespace = namespace self._type_helper = cpp_type_generator - self._util_cc_helper = ( - util_cc_helper.UtilCCHelper(self._type_helper)) + self._util_cc_helper = (util_cc_helper.UtilCCHelper(self._type_helper)) self._generate_error_messages = namespace.compiler_options.get( 'generate_error_messages', False) @@ -37,77 +39,75 @@ class _Generator(object): self._namespace.unix_name) c = Code() - (c.Append(cpp_util.CHROMIUM_LICENSE) - .Append() + (c.Append(cpp_util.CHROMIUM_LICENSE) \ + .Append() \ .Append(cpp_util.GENERATED_FILE_MESSAGE % - cpp_util.ToPosixPath(self._namespace.source_file)) - .Append() + cpp_util.ToPosixPath(self._namespace.source_file)) \ + .Append() \ .Append('#include "%s/%s.h"' % (cpp_util.ToPosixPath(self._namespace.source_file_dir), - self._namespace.short_filename)) - .Append() - .Append('#include <memory>') - .Append('#include <optional>') - .Append('#include <ostream>') - .Append('#include <string>') - .Append('#include <string_view>') - .Append('#include <utility>') - .Append('#include <vector>') - .Append() - .Append('#include "base/check.h"') - .Append('#include "base/check_op.h"') - .Append('#include "base/notreached.h"') - .Append('#include "base/strings/string_number_conversions.h"') - .Append('#include "base/strings/utf_string_conversions.h"') - .Append('#include "base/values.h"') - .Append(self._util_cc_helper.GetIncludePath()) - .Cblock(self._GenerateManifestKeysIncludes()) + self._namespace.short_filename)) \ + .Append() \ + .Append('#include <memory>') \ + .Append('#include <optional>') \ + .Append('#include <ostream>') \ + .Append('#include <string>') \ + .Append('#include <string_view>') \ + .Append('#include <utility>') \ + .Append('#include <vector>') \ + .Append() \ + .Append('#include "base/check.h"') \ + .Append('#include "base/check_op.h"') \ + .Append('#include "base/notreached.h"') \ + .Append('#include "base/strings/string_number_conversions.h"') \ + .Append('#include "base/strings/utf_string_conversions.h"') \ + .Append('#include "base/values.h"') \ + .Append(self._util_cc_helper.GetIncludePath()) \ + .Cblock(self._GenerateManifestKeysIncludes()) \ .Cblock(self._type_helper.GenerateIncludes(include_soft=True, - generate_error_messages=self._generate_error_messages)) - .Append() - .Append('using base::UTF8ToUTF16;') - .Append() + generate_error_messages=self._generate_error_messages)) \ + .Append() \ + .Append('using base::UTF8ToUTF16;') \ + .Append() \ .Concat(cpp_util.OpenNamespace(cpp_namespace)) ) if self._namespace.properties: - (c.Append('//') - .Append('// Properties') - .Append('//') + (c.Append('//') \ + .Append('// Properties') \ + .Append('//') \ .Append() ) for prop in self._namespace.properties.values(): property_code = self._type_helper.GeneratePropertyValues( - prop, - 'const %(type)s %(name)s = %(value)s;', - nodoc=True) + prop, 'const %(type)s %(name)s = %(value)s;', nodoc=True) if property_code: c.Cblock(property_code) if self._namespace.types: - (c.Append('//') - .Append('// Types') - .Append('//') - .Append() + (c.Append('//') \ + .Append('// Types') \ + .Append('//') \ + .Append() \ .Cblock(self._GenerateTypes(None, self._namespace.types.values())) ) if self._namespace.manifest_keys: - (c.Append('//') - .Append('// Manifest Keys') - .Append('//') - .Append() + (c.Append('//') \ + .Append('// Manifest Keys') \ + .Append('//') \ + .Append() \ .Cblock(self._GenerateManifestKeys()) ) if self._namespace.functions: - (c.Append('//') - .Append('// Functions') - .Append('//') + (c.Append('//') \ + .Append('// Functions') \ + .Append('//') \ .Append() ) for function in self._namespace.functions.values(): c.Cblock(self._GenerateFunction(function)) if self._namespace.events: - (c.Append('//') - .Append('// Events') - .Append('//') + (c.Append('//') \ + .Append('// Events') \ + .Append('//') \ .Append() ) for event in self._namespace.events.values(): @@ -124,33 +124,33 @@ class _Generator(object): if type_.functions: # Wrap functions within types in the type's namespace. - (c.Append('namespace %s {' % classname) + (c.Append('namespace %s {' % classname) \ .Append()) for function in type_.functions.values(): c.Cblock(self._GenerateFunction(function)) c.Append('} // namespace %s' % classname) elif type_.property_type == PropertyType.ARRAY: c.Cblock(self._GenerateType(cpp_namespace, type_.item_type)) - elif type_.property_type in (PropertyType.CHOICES, - PropertyType.OBJECT): + elif type_.property_type in (PropertyType.CHOICES, PropertyType.OBJECT): if cpp_namespace is None: classname_in_namespace = classname else: classname_in_namespace = '%s::%s' % (cpp_namespace, classname) if type_.property_type == PropertyType.OBJECT: - c.Cblock(self._GeneratePropertyFunctions(classname_in_namespace, - type_.properties.values())) + c.Cblock( + self._GeneratePropertyFunctions(classname_in_namespace, + type_.properties.values())) else: c.Cblock(self._GenerateTypes(classname_in_namespace, type_.choices)) - (c.Append('%s::%s()' % (classname_in_namespace, classname)) - .Cblock(self._GenerateInitializersAndBody(type_)) + (c.Append('%s::%s()' % (classname_in_namespace, classname)) \ + .Cblock(self._GenerateInitializersAndBody(type_)) \ .Append('%s::~%s() = default;' % (classname_in_namespace, classname)) ) # Note: we use 'rhs' because some API objects have a member 'other'. (c.Append('%s::%s(%s&& rhs) noexcept = default;' % - (classname_in_namespace, classname, classname)) + (classname_in_namespace, classname, classname)) \ .Append('%s& %s::operator=(%s&& rhs) noexcept = default;' % (classname_in_namespace, classname_in_namespace, classname)) @@ -158,8 +158,8 @@ class _Generator(object): if type_.origin.from_manifest_keys: c.Cblock( - self._GenerateManifestKeyConstants( - classname_in_namespace, type_.properties.values())) + self._GenerateManifestKeyConstants(classname_in_namespace, + type_.properties.values())) # Manifest key parsing for CHOICES types relies on the Populate() # method. Thus, if it wouldn't be generated below, ensure it's # created here. @@ -167,34 +167,39 @@ class _Generator(object): # branches determining which values to construct here, we should pull # this out to a helper that just returns a set of method categories to # generate. - if (type_.property_type is PropertyType.CHOICES and - not type_.origin.from_json): - c.Cblock(self._GenerateTypePopulateFromValue( - classname_in_namespace, type_)) + if (type_.property_type is PropertyType.CHOICES + and not type_.origin.from_json): + c.Cblock( + self._GenerateTypePopulateFromValue(classname_in_namespace, + type_)) if type_.origin.from_json: c.Cblock(self._GenerateClone(classname_in_namespace, type_)) if type_.property_type is not PropertyType.CHOICES: c.Cblock(self._GenerateTypePopulate(classname_in_namespace, type_)) - c.Cblock(self._GenerateTypePopulateFromValue( - classname_in_namespace, type_)) + c.Cblock( + self._GenerateTypePopulateFromValue(classname_in_namespace, type_)) if type_.property_type is not PropertyType.CHOICES: - c.Cblock(self._GenerateTypeFromValue( - classname_in_namespace, type_, is_dict=True)) + c.Cblock( + self._GenerateTypeFromValue(classname_in_namespace, + type_, + is_dict=True)) - c.Cblock(self._GenerateTypeFromValue( - classname_in_namespace, type_, is_dict=False)) + c.Cblock( + self._GenerateTypeFromValue(classname_in_namespace, + type_, + is_dict=False)) if type_.origin.from_client: c.Cblock(self._GenerateTypeToValue(classname_in_namespace, type_)) if type_.origin.from_manifest_keys: c.Cblock( - self._GenerateParseFromDictionary( - classname, classname_in_namespace, type_)) + self._GenerateParseFromDictionary(classname, classname_in_namespace, + type_)) elif type_.property_type == PropertyType.ENUM: - (c.Cblock(self._GenerateEnumToString(cpp_namespace, type_)) - .Cblock(self._GenerateEnumFromString(cpp_namespace, type_)) + (c.Cblock(self._GenerateEnumToString(cpp_namespace, type_)) \ + .Cblock(self._GenerateEnumFromString(cpp_namespace, type_)) \ .Cblock(self._GenerateEnumParseErrorMessage(cpp_namespace, type_)) ) @@ -216,14 +221,14 @@ class _Generator(object): items.append('%s(0.0)' % prop.unix_name) elif t.property_type == PropertyType.BOOLEAN: items.append('%s(false)' % prop.unix_name) - elif (t.property_type == PropertyType.ANY or - t.property_type == PropertyType.ARRAY or - t.property_type == PropertyType.BINARY or - t.property_type == PropertyType.CHOICES or - t.property_type == PropertyType.OBJECT or - t.property_type == PropertyType.FUNCTION or - t.property_type == PropertyType.REF or - t.property_type == PropertyType.STRING): + elif (t.property_type == PropertyType.ANY + or t.property_type == PropertyType.ARRAY + or t.property_type == PropertyType.BINARY + or t.property_type == PropertyType.CHOICES + or t.property_type == PropertyType.OBJECT + or t.property_type == PropertyType.FUNCTION + or t.property_type == PropertyType.REF + or t.property_type == PropertyType.STRING): # TODO(miket): It would be nice to initialize CHOICES, but we # don't presently have the semantics to indicate which one of a set # should be the default. @@ -245,33 +250,32 @@ class _Generator(object): underlying_type = self._type_helper.FollowRef(type_) c = Code() - if (type_.property_type == PropertyType.ARRAY and - self._type_helper.FollowRef(type_.item_type).property_type in ( - PropertyType.ANY, - PropertyType.OBJECT)): + if (type_.property_type == PropertyType.ARRAY + and self._type_helper.FollowRef(type_.item_type).property_type + in (PropertyType.ANY, PropertyType.OBJECT)): if is_optional: - (c.Sblock('if (%(name)s) {') - .Append('out.%(name)s.emplace();') - .Append('out.%(name)s->reserve(%(name)s->size());') - .Sblock('for (const auto& element : *%(name)s) {') + (c.Sblock('if (%(name)s) {') \ + .Append('out.%(name)s.emplace();') \ + .Append('out.%(name)s->reserve(%(name)s->size());') \ + .Sblock('for (const auto& element : *%(name)s) {') \ .Append(self._util_cc_helper.AppendToContainer( - '*out.%(name)s', 'element.Clone()')) - .Eblock('}') + '*out.%(name)s', 'element.Clone()')) \ + .Eblock('}') \ .Eblock('}')) else: - (c.Append('out.%(name)s.reserve(%(name)s.size());') - .Sblock('for (const auto& element : %(name)s) {') + (c.Append('out.%(name)s.reserve(%(name)s.size());') \ + .Sblock('for (const auto& element : %(name)s) {') \ .Append(self._util_cc_helper.AppendToContainer( - 'out.%(name)s', 'element.Clone()')) + 'out.%(name)s', 'element.Clone()')) \ .Eblock('}')) - elif (underlying_type.property_type == PropertyType.OBJECT or - underlying_type.property_type == PropertyType.ANY or - underlying_type.property_type == PropertyType.CHOICES or - (underlying_type.property_type == PropertyType.FUNCTION and not - type_.is_serializable_function)): + elif (underlying_type.property_type == PropertyType.OBJECT + or underlying_type.property_type == PropertyType.ANY + or underlying_type.property_type == PropertyType.CHOICES + or (underlying_type.property_type == PropertyType.FUNCTION + and not type_.is_serializable_function)): if is_optional: - (c.Sblock('if (%(name)s) {') - .Append('out.%(name)s = %(name)s->Clone();') + (c.Sblock('if (%(name)s) {') \ + .Append('out.%(name)s = %(name)s->Clone();') \ .Eblock('}')) else: c.Append('out.%(name)s = %(name)s.Clone();') @@ -291,19 +295,22 @@ class _Generator(object): if type_.property_type is PropertyType.CHOICES: for choice in type_.choices: - c.Concat(self._GenerateCloneItem( - choice, 'as_%s' % choice.unix_name, is_optional=True)) + c.Concat( + self._GenerateCloneItem(choice, + 'as_%s' % choice.unix_name, + is_optional=True)) else: for prop in type_.properties.values(): - c.Concat(self._GenerateCloneItem( - prop.type_, prop.type_.unix_name, is_optional=prop.optional)) + c.Concat( + self._GenerateCloneItem(prop.type_, + prop.type_.unix_name, + is_optional=prop.optional)) - (c.Append('return out;') - .Eblock('}') + (c.Append('return out;') \ + .Eblock('}') \ .Substitute({'namespace': cpp_namespace, 'classname': classname})) return c - def _GenerateTypePopulate(self, cpp_namespace, type_): """Generates the function for populating a type given a pointer to it. @@ -312,8 +319,8 @@ class _Generator(object): """ classname = cpp_util.Classname(schema_util.StripNamespace(type_.name)) c = Code() - (c.Append('// static') - .Append('bool %(namespace)s::Populate(') + (c.Append('// static') \ + .Append('bool %(namespace)s::Populate(') \ .Sblock(' %s) {' % self._GenerateParams( ('const base::Value::Dict& dict', '%(name)s& out')))) @@ -330,18 +337,18 @@ class _Generator(object): c.Append('out.additional_properties.Merge(dict.Clone());') else: cpp_type = self._type_helper.GetCppType(type_.additional_properties) - (c.Sblock('for (const auto item : dict) {') - .Append('%s tmp;' % cpp_type) + (c.Sblock('for (const auto item : dict) {') \ + .Append('%s tmp;' % cpp_type) \ .Concat(self._GeneratePopulateVariableFromValue( type_.additional_properties, 'item.second', 'tmp', - 'false')) - .Append('out.additional_properties[item.first] = tmp;') + 'false')) \ + .Append('out.additional_properties[item.first] = tmp;') \ .Eblock('}') ) c.Append('return true;') - (c.Eblock('}') + (c.Eblock('}') \ .Substitute({'namespace': cpp_namespace, 'name': classname})) return c @@ -354,49 +361,49 @@ class _Generator(object): """ classname = cpp_util.Classname(schema_util.StripNamespace(type_.name)) c = Code() - (c.Append('// static') - .Append('bool %(namespace)s::Populate(') + (c.Append('// static') \ + .Append('bool %(namespace)s::Populate(') \ .Sblock(' %s) {' % self._GenerateParams( ('const base::Value& value', '%(name)s& out')))) if type_.property_type is PropertyType.CHOICES: for choice in type_.choices: (c.Sblock('if (%s) {' % self._GenerateValueIsTypeExpression('value', - choice)) + choice)) \ .Concat(self._GeneratePopulateVariableFromValue( choice, 'value', 'out.as_%s' % choice.unix_name, 'false', - is_ptr=True)) - .Append('return true;') + is_ptr=True)) \ + .Append('return true;') \ .Eblock('}') ) (c.Concat(self._AppendError16( 'u"expected %s, got " + %s' % (" or ".join(choice.name for choice in type_.choices), - self._util_cc_helper.GetValueTypeString('value')))) + self._util_cc_helper.GetValueTypeString('value')))) \ .Append('return false;')) elif type_.property_type == PropertyType.OBJECT: - (c.Sblock('if (!value.is_dict()) {') + (c.Sblock('if (!value.is_dict()) {') \ .Concat(self._AppendError16( 'u"expected dictionary, got " + ' + - self._util_cc_helper.GetValueTypeString('value'))) - .Append('return false;') + self._util_cc_helper.GetValueTypeString('value'))) \ + .Append('return false;') \ .Eblock('}')) - (c.Append('return Populate(%s);' % - self._GenerateArgs(('value.GetDict()', 'out')))) + (c.Append('return Populate(%s);' % self._GenerateArgs( + ('value.GetDict()', 'out')))) - (c.Eblock('}') + (c.Eblock('}') \ .Substitute({'namespace': cpp_namespace, 'name': classname})) return c def _GenerateValueIsTypeExpression(self, var, type_): real_type = self._type_helper.FollowRef(type_) if real_type.property_type is PropertyType.CHOICES: - return '(%s)' % ' || '.join(self._GenerateValueIsTypeExpression(var, - choice) - for choice in real_type.choices) + return '(%s)' % ' || '.join( + self._GenerateValueIsTypeExpression(var, choice) + for choice in real_type.choices) return '%s.type() == %s' % (var, cpp_util.GetValueType(real_type)) def _GenerateTypePopulateProperty(self, prop, src, dst): @@ -410,32 +417,32 @@ class _Generator(object): c.Append('const base::Value* %(value_var)s = %(src)s.Find("%(key)s");') if prop.optional: (c.Sblock( - 'if (%(value_var)s) {') + 'if (%(value_var)s) {') \ .Concat(self._GeneratePopulatePropertyFromValue( prop, '(*%s)' % value_var, dst, 'false'))) underlying_type = self._type_helper.FollowRef(prop.type_) if underlying_type.property_type == PropertyType.ENUM: - (c.Append('} else {') + (c.Append('} else {') \ .Append('%%(dst)s.%%(name)s = %s;' % self._type_helper.GetEnumDefaultValue(underlying_type, self._namespace))) c.Eblock('}') else: (c.Sblock( - 'if (!%(value_var)s) {') - .Concat(self._AppendError16('u"\'%%(key)s\' is required"')) - .Append('return false;') - .Eblock('}') + 'if (!%(value_var)s) {') \ + .Concat(self._AppendError16('u"\'%%(key)s\' is required"')) \ + .Append('return false;') \ + .Eblock('}') \ .Concat(self._GeneratePopulatePropertyFromValue( prop, '(*%s)' % value_var, dst, 'false')) ) c.Append() c.Substitute({ - 'value_var': value_var, - 'key': prop.name, - 'src': src, - 'dst': dst, - 'name': prop.unix_name + 'value_var': value_var, + 'key': prop.name, + 'src': src, + 'dst': dst, + 'name': prop.unix_name }) return c @@ -448,14 +455,14 @@ class _Generator(object): param_type = ('base::Value::Dict' if is_dict else 'base::Value') c = Code() - (c.Append(f'// static') + (c.Append(f'// static') \ .Append('{return_type} ' '{classname}::FromValue(const {param_type}& value) {{'.format( return_type=return_type, classname=cpp_namespace, param_type=param_type)) ) - c.Sblock(); + c.Sblock() # TODO(crbug.com/40235429): Once the deprecated version of this method is # removed, we should consider making Populate return an optional, rather # than using an out param. @@ -463,8 +470,8 @@ class _Generator(object): c.Append('std::u16string error;') c.Append(f'{classname} out;') - c.Append('bool result = Populate(%s);' % - self._GenerateArgs(('value', 'out'))) + c.Append('bool result = Populate(%s);' % self._GenerateArgs( + ('value', 'out'))) c.Sblock('if (!result) {') if self._generate_error_messages: @@ -522,8 +529,8 @@ class _Generator(object): assert self._namespace.manifest_keys.property_type == PropertyType.OBJECT return self._GenerateType(None, self._namespace.manifest_keys) - def _GenerateParseFromDictionary( - self, classname, classname_in_namespace, type_): + def _GenerateParseFromDictionary(self, classname, classname_in_namespace, + type_): # type: (str, str, Type) -> Code """Generates a function that deserializes the type from the passed dictionary. E.g. for type "Foo", generates Foo::ParseFromDictionary(). @@ -534,19 +541,20 @@ class _Generator(object): ('Manifest type %s must be an object, but it is: %s' % (type_.name, type_.property_type)) return self._GenerateParseFromDictionaryForRootManifestType( - classname, classname_in_namespace, type_.properties.values()) + classname, classname_in_namespace, type_.properties.values()) return self._GenerateParseFromDictionaryForChildManifestType( - type_, classname, classname_in_namespace, type_.properties.values()) + type_, classname, classname_in_namespace, type_.properties.values()) - def _GenerateParseFromDictionaryForRootManifestType( - self, classname, classname_in_namespace, properties): + def _GenerateParseFromDictionaryForRootManifestType(self, classname, + classname_in_namespace, + properties): # type: (str, str, List[Property]) -> Code """Generates definition for ManifestKeys::ParseFromDictionary. """ params = [ - 'const base::Value::Dict& root_dict', - '%(classname)s& out' + 'const base::Value::Dict& root_dict', + '%(classname)s& out', ] c = Code() @@ -568,27 +576,26 @@ class _Generator(object): for prop in properties: c.Cblock( - self._ParsePropertyFromDictionary(prop, is_root_manifest_type=True)) + self._ParsePropertyFromDictionary(prop, is_root_manifest_type=True)) c.Append('return true;') c.Eblock('}') c.Substitute({ - 'classname_in_namespace': classname_in_namespace, - 'classname': classname + 'classname_in_namespace': classname_in_namespace, + 'classname': classname }) return c - def _GenerateParseFromDictionaryForChildManifestType( - self, type_, classname, classname_in_namespace, properties): + def _GenerateParseFromDictionaryForChildManifestType(self, type_, classname, + classname_in_namespace, + properties): # type: (str, str, List[Property]) -> Code """Generates T::ParseFromDictionary for a child manifest type. """ params = [ - 'const base::Value::Dict& root_dict', - 'std::string_view key', - '%(classname)s& out', - 'std::u16string& error', - 'std::vector<std::string_view>& error_path_reversed' + 'const base::Value::Dict& root_dict', 'std::string_view key', + '%(classname)s& out', 'std::u16string& error', + 'std::vector<std::string_view>& error_path_reversed' ] c = Code() @@ -604,14 +611,13 @@ class _Generator(object): # For CHOICES, we leverage the specialized helper in manifest_parse_util. if type_.property_type is PropertyType.CHOICES: - c.Append( - 'return ::json_schema_compiler::manifest_parse_util::\n' - ' ParseChoicesFromDictionary(root_dict, key, out, error,\n' - ' error_path_reversed);') + c.Append('return ::json_schema_compiler::manifest_parse_util::\n' + ' ParseChoicesFromDictionary(root_dict, key, out, error,\n' + ' error_path_reversed);') c.Eblock('}') return c.Substitute({ - 'classname_in_namespace': classname_in_namespace, - 'classname': classname + 'classname_in_namespace': classname_in_namespace, + 'classname': classname }) # Otherwise, this must be an object, and we parse each property @@ -620,12 +626,10 @@ class _Generator(object): ('Manifest type %s must be an object, but it is: %s' % (type_.name, type_.property_type)) - c.Append( - 'const base::Value* value = ' - '::json_schema_compiler::manifest_parse_util::FindKeyOfType(' - 'root_dict, key, base::Value::Type::DICT, error, ' - 'error_path_reversed);' - ) + c.Append('const base::Value* value = ' + '::json_schema_compiler::manifest_parse_util::FindKeyOfType(' + 'root_dict, key, base::Value::Type::DICT, error, ' + 'error_path_reversed);') c.Sblock('if (!value)') c.Append('return false;') if len(properties) > 0: @@ -638,13 +642,13 @@ class _Generator(object): for prop in properties: c.Cblock( - self._ParsePropertyFromDictionary(prop, is_root_manifest_type=False)) + self._ParsePropertyFromDictionary(prop, is_root_manifest_type=False)) c.Append('return true;') c.Eblock('}') c.Substitute({ - 'classname_in_namespace': classname_in_namespace, - 'classname': classname + 'classname_in_namespace': classname_in_namespace, + 'classname': classname }) return c @@ -653,54 +657,49 @@ class _Generator(object): """Generates the code to parse a single property from a dictionary. """ supported_property_types = { - PropertyType.ARRAY, - PropertyType.BOOLEAN, - PropertyType.DOUBLE, - PropertyType.INT64, - PropertyType.INTEGER, - PropertyType.CHOICES, - PropertyType.OBJECT, - PropertyType.STRING, - PropertyType.ENUM + PropertyType.ARRAY, + PropertyType.BOOLEAN, + PropertyType.DOUBLE, + PropertyType.INT64, + PropertyType.INTEGER, + PropertyType.CHOICES, + PropertyType.OBJECT, + PropertyType.STRING, + PropertyType.ENUM, } c = Code() underlying_type = self._type_helper.FollowRef(property.type_) underlying_property_type = underlying_type.property_type - underlying_item_type = ( - self._type_helper.FollowRef(underlying_type.item_type) - if underlying_property_type is PropertyType.ARRAY - else None) + underlying_item_type = (self._type_helper.FollowRef( + underlying_type.item_type) if underlying_property_type + is PropertyType.ARRAY else None) assert (underlying_property_type in supported_property_types), ( - 'Property type not implemented for %s, type: %s, namespace: %s' % - (underlying_property_type, underlying_type.name, - underlying_type.namespace.name)) + 'Property type not implemented for %s, type: %s, namespace: %s' % + (underlying_property_type, underlying_type.name, + underlying_type.namespace.name)) property_constant = cpp_util.UnixNameToConstantName(property.unix_name) out_expression = 'out.%s' % property.unix_name def get_enum_params(enum_type, include_optional_param): # type: (Type, bool) -> List[str] - enum_name = cpp_util.Classname( - schema_util.StripNamespace(enum_type.name)) - cpp_type_namespace = ('' - if enum_type.namespace == self._namespace - else '%s::' % enum_type.namespace.unix_name) + enum_name = cpp_util.Classname(schema_util.StripNamespace(enum_type.name)) + cpp_type_namespace = ('' if enum_type.namespace == self._namespace else + '%s::' % enum_type.namespace.unix_name) params = [ - 'dict', - '%s' % property_constant, - '&%sParse%s' % (cpp_type_namespace, enum_name) + 'dict', + '%s' % property_constant, + '&%sParse%s' % (cpp_type_namespace, enum_name) ] if include_optional_param: params.append('true' if property.optional else 'false') params += [ - '%s' % self._type_helper.GetEnumDefaultValue(enum_type, - self._namespace), - '%s' % out_expression, - 'error', - 'error_path_reversed' + '%s' % + self._type_helper.GetEnumDefaultValue(enum_type, self._namespace), + '%s' % out_expression, 'error', 'error_path_reversed' ] return params @@ -715,21 +714,18 @@ class _Generator(object): func_name = 'ParseEnumArrayFromDictionary' else: params = [ - 'dict', - '%s' % property_constant, - '%s' % out_expression, - 'error', - 'error_path_reversed' + 'dict', + '%s' % property_constant, + '%s' % out_expression, 'error', 'error_path_reversed' ] func_name = 'ParseFromDictionary' - c.Sblock( - 'if (!::json_schema_compiler::manifest_parse_util::%s(%s)) {' - % (func_name, self._GenerateParams(params, generate_error_messages=False)) - ) + c.Sblock('if (!::json_schema_compiler::manifest_parse_util::%s(%s)) {' % + (func_name, + self._GenerateParams(params, generate_error_messages=False))) if is_root_manifest_type: c.Append('::json_schema_compiler::manifest_parse_util::' - 'PopulateFinalError(error, error_path_reversed);') + 'PopulateFinalError(error, error_path_reversed);') else: c.Append('error_path_reversed.push_back(key);') c.Append('return false;') @@ -742,8 +738,8 @@ class _Generator(object): into a base::Value::Dict. """ c = Code() - (c.Sblock('base::Value::Dict %s::ToValue() const {' % cpp_namespace) - .Append('base::Value::Dict to_value_result;') + (c.Sblock('base::Value::Dict %s::ToValue() const {' % cpp_namespace) \ + .Append('base::Value::Dict to_value_result;') \ .Append() ) @@ -754,19 +750,19 @@ class _Generator(object): if underlying_type.property_type == PropertyType.ENUM: # Optional enum values are generated with default initialisation (e.g. # kNone), potentially from another namespace. - c.Sblock('if (%s != %s) {' % - (prop_var, - self._type_helper.GetEnumDefaultValue(underlying_type, - self._namespace))) + c.Sblock('if (%s != %s) {' % (prop_var, + self._type_helper.GetEnumDefaultValue( + underlying_type, self._namespace))) else: c.Sblock('if (%s) {' % prop_var) - c.Cblock(self._CreateValueFromType( - 'to_value_result.Set("%s", %%s);' % prop.name, - prop.name, - prop.type_, - prop_var, - is_ptr=prop.optional)) + c.Cblock( + self._CreateValueFromType('to_value_result.Set("%s", %%s);' % + prop.name, + prop.name, + prop.type_, + prop_var, + is_ptr=prop.optional)) if prop.optional: c.Eblock('}') @@ -775,17 +771,17 @@ class _Generator(object): if type_.additional_properties.property_type == PropertyType.ANY: c.Append('to_value_result.Merge(additional_properties.Clone());') else: - (c.Sblock('for (const auto& it : additional_properties) {') + (c.Sblock('for (const auto& it : additional_properties) {') \ .Cblock(self._CreateValueFromType( 'to_value_result.Set(it.first, %s);', type_.additional_properties.name, type_.additional_properties, - 'it.second')) + 'it.second')) \ .Eblock('}') ) - return (c.Append() - .Append('return to_value_result;') + return (c.Append() \ + .Append('return to_value_result;') \ .Eblock('}')) def _GenerateChoiceTypeToValue(self, cpp_namespace, type_): @@ -793,8 +789,7 @@ class _Generator(object): into a base::Value. """ c = Code() - c.Sblock('base::Value %s::ToValue() const {' % - cpp_namespace) + c.Sblock('base::Value %s::ToValue() const {' % cpp_namespace) c.Append('base::Value result;') for choice in type_.choices: choice_var = 'as_%s' % choice.unix_name @@ -802,25 +797,24 @@ class _Generator(object): # Enum class values cannot be checked as a boolean, so they require # specific handling when checking if they are engaged, by comparing it # against kNone. - if (self._type_helper.FollowRef(choice).property_type == - PropertyType.ENUM): + if (self._type_helper.FollowRef(choice).property_type == PropertyType.ENUM + ): comparison_expr = '{enum_var} != {default_value}'.format( - enum_var=choice_var, - default_value=self._type_helper.GetEnumDefaultValue(choice, - self._namespace)) + enum_var=choice_var, + default_value=self._type_helper.GetEnumDefaultValue( + choice, self._namespace)) else: comparison_expr = choice_var - (c.Sblock('if (%s) {' % comparison_expr) + (c.Sblock('if (%s) {' % comparison_expr) \ .Append('DCHECK(result.is_none()) << "Cannot set multiple choices for ' '%s";' % type_.unix_name).Cblock(self._CreateValueFromType( 'result = base::Value(%s);', choice.name, choice, choice_var, - True)) + True)) \ .Eblock('}')) (c.Append('DCHECK(!result.is_none()) << "Must set at least one choice for ' - '%s";' % - type_.unix_name).Append('return result;').Eblock('}')) + '%s";' % type_.unix_name).Append('return result;').Eblock('}')) return c def _GenerateFunction(self, function): @@ -834,18 +828,18 @@ class _Generator(object): # to not use the name. if function_namespace == 'SendMessage': function_namespace = 'PassMessage' - (c.Append('namespace %s {' % function_namespace) + (c.Append('namespace %s {' % function_namespace) \ .Append() ) # Params::Populate function if function.params: c.Concat(self._GeneratePropertyFunctions('Params', function.params)) - (c.Append('Params::Params() = default;') - .Append('Params::~Params() = default;') - .Append('Params::Params(Params&& rhs) noexcept = default;') - .Append('Params& Params::operator=(Params&& rhs) noexcept = default;') - .Append() + (c.Append('Params::Params() = default;') \ + .Append('Params::~Params() = default;') \ + .Append('Params::Params(Params&& rhs) noexcept = default;') \ + .Append('Params& Params::operator=(Params&& rhs) noexcept = default;') \ + .Append() \ .Cblock(self._GenerateFunctionParamsCreate(function)) ) if self._generate_error_messages: @@ -855,7 +849,7 @@ class _Generator(object): if function.returns_async: c.Concat( self._GenerateAsyncResponseArguments('Results', - function.returns_async.params)) + function.returns_async.params)) c.Append('} // namespace %s' % function_namespace) return c @@ -864,10 +858,10 @@ class _Generator(object): # TODO(kalman): use event.unix_name not Classname. c = Code() event_namespace = cpp_util.Classname(event.name) - (c.Append('namespace %s {' % event_namespace) - .Append() - .Cblock(self._GenerateEventNameConstant(event)) - .Cblock(self._GenerateAsyncResponseArguments(None, event.params)) + (c.Append('namespace %s {' % event_namespace) \ + .Append() \ + .Cblock(self._GenerateEventNameConstant(event)) \ + .Cblock(self._GenerateAsyncResponseArguments(None, event.params)) \ .Append('} // namespace %s' % event_namespace) ) return c @@ -898,18 +892,18 @@ class _Generator(object): enum_list_var = '%s_list' % prop_name # Scope the std::vector variable declaration inside braces. - (c.Sblock('{') - .Append('std::vector<std::string> %s;' % enum_list_var) - .Sblock('for (const auto& it : %s) {' % varname) + (c.Sblock('{') \ + .Append('std::vector<std::string> %s;' % enum_list_var) \ + .Sblock('for (const auto& it : %s) {' % varname) \ .Append('%s.emplace_back(%sToString(it));' % (enum_list_var, - maybe_namespace)) + maybe_namespace)) \ .Eblock('}')) # Because the std::vector above is always created for both required and # optional enum arrays, |is_ptr| is set to false and uses the # std::vector to create the values. (c.Append(code % - self._GenerateCreateValueFromType(type_, enum_list_var, False)) + self._GenerateCreateValueFromType(type_, enum_list_var, False)) \ .Eblock('}')) return c @@ -924,15 +918,15 @@ class _Generator(object): is_ptr: Whether |type_| is optional. """ underlying_type = self._type_helper.FollowRef(type_) - if (underlying_type.property_type == PropertyType.CHOICES or - underlying_type.property_type == PropertyType.OBJECT): + if (underlying_type.property_type == PropertyType.CHOICES + or underlying_type.property_type == PropertyType.OBJECT): if is_ptr: return '(%s)->ToValue()' % var else: return '(%s).ToValue()' % var - elif (underlying_type.property_type == PropertyType.ANY or - (underlying_type.property_type == PropertyType.FUNCTION and - not underlying_type.is_serializable_function)): + elif (underlying_type.property_type == PropertyType.ANY + or (underlying_type.property_type == PropertyType.FUNCTION + and not underlying_type.is_serializable_function)): if is_ptr: vardot = '(%s)->' % var else: @@ -950,14 +944,14 @@ class _Generator(object): elif underlying_type.property_type == PropertyType.ARRAY: if is_ptr: var = '*%s' % var - underlying_item_cpp_type = ( - self._type_helper.GetCppType(underlying_type.item_type)) + underlying_item_cpp_type = (self._type_helper.GetCppType( + underlying_type.item_type)) if underlying_item_cpp_type != 'base::Value': return '%s' % self._util_cc_helper.CreateValueFromArray(var) else: return '(%s).Clone()' % var - elif (underlying_type.property_type.is_fundamental or - underlying_type.is_serializable_function): + elif (underlying_type.property_type.is_fundamental + or underlying_type.is_serializable_function): if is_ptr: var = '*%s' % var return '%s' % var @@ -980,12 +974,12 @@ class _Generator(object): c.Sblock('if (%(var)s.size() > %(total)d) {') else: c.Sblock('if (%(var)s.size() < %(required)d' - ' || %(var)s.size() > %(total)d) {') + ' || %(var)s.size() > %(total)d) {') (c.Concat(self._AppendError16( 'u"expected %%(total)d arguments, got " ' - '+ base::NumberToString16(%%(var)s.size())')) - .Append('return %(failure_value)s;') - .Eblock('}') + '+ base::NumberToString16(%%(var)s.size())')) \ + .Append('return %(failure_value)s;') \ + .Eblock('}') \ .Substitute({ 'var': var, 'failure_value': failure_value, @@ -1002,14 +996,14 @@ class _Generator(object): """ c = Code() - (c.Append('// static') + (c.Append('// static') \ .Sblock('std::optional<Params> Params::Create(%s) {' % self._GenerateParams([ 'const base::Value::List& args'])) ) failure_value = 'std::nullopt' - (c.Concat(self._GenerateParamsCheck(function, 'args', failure_value)) + (c.Concat(self._GenerateParamsCheck(function, 'args', failure_value)) \ .Append('Params params;') ) @@ -1023,22 +1017,22 @@ class _Generator(object): # there may be other arguments following it. c.Append() value_var = param.unix_name + '_value' - (c.Append('if (%(i)s < args.size() &&') - .Sblock(' !args[%(i)s].is_none()) {') - .Append('const base::Value& %(value_var)s = args[%(i)s];') + (c.Append('if (%(i)s < args.size() &&') \ + .Sblock(' !args[%(i)s].is_none()) {') \ + .Append('const base::Value& %(value_var)s = args[%(i)s];') \ .Concat(self._GeneratePopulatePropertyFromValue( - param, value_var, 'params', failure_value)) + param, value_var, 'params', failure_value)) \ .Eblock('}') ) if not param.optional: - (c.Sblock('else {') - .Concat(self._AppendError16('u"\'%%(key)s\' is required"')) - .Append('return %s;' % failure_value) + (c.Sblock('else {') \ + .Concat(self._AppendError16('u"\'%%(key)s\' is required"')) \ + .Append('return %s;' % failure_value) \ .Eblock('}')) c.Substitute({'value_var': value_var, 'i': i, 'key': param.name}) - (c.Append() - .Append('return params;') - .Eblock('}') + (c.Append() \ + .Append('return params;') \ + .Eblock('}') \ .Append() ) @@ -1054,38 +1048,34 @@ class _Generator(object): # and everything should be handled by a single Create function. c = Code() - (c.Append('// static') + (c.Append('// static') \ .Sblock('base::expected<Params, std::u16string> ' 'Params::Create(const base::Value::List& args) {') ) - (c.Append('std::u16string error;') - .Append('auto result = Params::Create(args, error);') - .Sblock('if (!result) {') - .Append('DCHECK(!error.empty());') - .Append('return base::unexpected(std::move(error));') - .Eblock('}') - .Append('return std::move(result).value();') - .Eblock('}') + (c.Append('std::u16string error;') \ + .Append('auto result = Params::Create(args, error);') \ + .Sblock('if (!result) {') \ + .Append('DCHECK(!error.empty());') \ + .Append('return base::unexpected(std::move(error));') \ + .Eblock('}') \ + .Append('return std::move(result).value();') \ + .Eblock('}') \ .Append()) return c - - def _GeneratePopulatePropertyFromValue(self, - prop, - src_var, - dst_class_var, + def _GeneratePopulatePropertyFromValue(self, prop, src_var, dst_class_var, failure_value): """Generates code to populate property |prop| of |dst_class_var| (a pointer) from a Value. See |_GeneratePopulateVariableFromValue| for semantics. """ - return self._GeneratePopulateVariableFromValue(prop.type_, - src_var, - '%s.%s' % (dst_class_var, - prop.unix_name), - failure_value, - is_ptr=prop.optional) + return self._GeneratePopulateVariableFromValue( + prop.type_, + src_var, + '%s.%s' % (dst_class_var, prop.unix_name), + failure_value, + is_ptr=prop.optional) def _GeneratePopulateVariableFromValue(self, type_, @@ -1101,24 +1091,23 @@ class _Generator(object): underlying_type = self._type_helper.FollowRef(type_) - if (underlying_type.property_type.is_fundamental or - underlying_type.is_serializable_function): + if (underlying_type.property_type.is_fundamental + or underlying_type.is_serializable_function): is_string_or_function = ( - underlying_type.property_type == PropertyType.STRING - or (underlying_type.property_type == PropertyType.FUNCTION - and underlying_type.is_serializable_function)) - c.Append('auto%s temp = %s;' % ( - '*' if is_string_or_function else '', - cpp_util.GetAsFundamentalValue(underlying_type, src_var) - )) + underlying_type.property_type == PropertyType.STRING + or (underlying_type.property_type == PropertyType.FUNCTION + and underlying_type.is_serializable_function)) + c.Append('auto%s temp = %s;' % + ('*' if is_string_or_function else '', + cpp_util.GetAsFundamentalValue(underlying_type, src_var))) if is_string_or_function: - (c.Sblock('if (!temp) {') + (c.Sblock('if (!temp) {') \ .Concat(self._AppendError16( 'u"\'%%(key)s\': expected ' + '%s, got " + %s' % ( type_.name, self._util_cc_helper.GetValueTypeString('%%(src_var)s'))))) else: - (c.Sblock('if (!temp.has_value()) {') + (c.Sblock('if (!temp.has_value()) {') \ .Concat(self._AppendError16( 'u"\'%%(key)s\': expected ' + '%s, got " + %s' % ( type_.name, @@ -1137,36 +1126,36 @@ class _Generator(object): c.Append('%(dst_var)s = std::make_unique<%(cpp_type)s>(*temp);') else: c.Append('%(dst_var)s = ' + - 'std::make_unique<%(cpp_type)s>(temp.value());') + 'std::make_unique<%(cpp_type)s>(temp.value());') else: c.Append('%(dst_var)s = *temp;') elif underlying_type.property_type == PropertyType.OBJECT: if is_ptr: - (c.Sblock('if (!%(src_var)s.is_dict()) {') + (c.Sblock('if (!%(src_var)s.is_dict()) {') \ .Concat(self._AppendError16( 'u"\'%%(key)s\': expected dictionary, got " + ' + - self._util_cc_helper.GetValueTypeString('%%(src_var)s'))) + self._util_cc_helper.GetValueTypeString('%%(src_var)s'))) \ .Append('return %(failure_value)s;') ) - (c.Eblock('}') - .Sblock('else {') - .Append('%(cpp_type)s temp;') + (c.Eblock('}') \ + .Sblock('else {') \ + .Append('%(cpp_type)s temp;') \ .Append('if (!%%(cpp_type)s::Populate(%s))' % self._GenerateArgs( - ('%(src_var)s.GetDict()', 'temp'))) - .Append(' return %(failure_value)s;') - .Append('%(dst_var)s = std::move(temp);') + ('%(src_var)s.GetDict()', 'temp'))) \ + .Append(' return %(failure_value)s;') \ + .Append('%(dst_var)s = std::move(temp);') \ .Eblock('}') ) else: - (c.Sblock('if (!%(src_var)s.is_dict()) {') + (c.Sblock('if (!%(src_var)s.is_dict()) {') \ .Concat(self._AppendError16( 'u"\'%%(key)s\': expected dictionary, got " + ' + - self._util_cc_helper.GetValueTypeString('%%(src_var)s'))) - .Append('return %(failure_value)s;') - .Eblock('}') + self._util_cc_helper.GetValueTypeString('%%(src_var)s'))) \ + .Append('return %(failure_value)s;') \ + .Eblock('}') \ .Append('if (!%%(cpp_type)s::Populate(%s)) {' % self._GenerateArgs( - ('%(src_var)s.GetDict()', '%(dst_var)s'))) - .Append(' return %(failure_value)s;') + ('%(src_var)s.GetDict()', '%(dst_var)s'))) \ + .Append(' return %(failure_value)s;') \ .Append('}') ) elif underlying_type.property_type == PropertyType.FUNCTION: @@ -1179,33 +1168,33 @@ class _Generator(object): c.Append('%(dst_var)s.emplace();') else: (c.Sblock('if (!%(src_var)s.is_dict() || ' + - '!%(src_var)s.GetDict().empty()) {') + '!%(src_var)s.GetDict().empty()) {') \ .Concat(self._AppendError16( 'u"\'%%(key)s\': expected dictionary, got " + ' + - self._util_cc_helper.GetValueTypeString('%%(src_var)s'))) - .Append('return %(failure_value)s;') + self._util_cc_helper.GetValueTypeString('%%(src_var)s'))) \ + .Append('return %(failure_value)s;') \ .Eblock('}') ) elif underlying_type.property_type == PropertyType.ANY: c.Append('%(dst_var)s = %(src_var)s.Clone();') elif underlying_type.property_type == PropertyType.ARRAY: # util_cc_helper deals with optional and required arrays - (c.Sblock('if (!%(src_var)s.is_list()) {') + (c.Sblock('if (!%(src_var)s.is_list()) {') \ .Concat(self._AppendError16( 'u"\'%%(key)s\': expected list, got " + ' + - self._util_cc_helper.GetValueTypeString('%%(src_var)s'))) + self._util_cc_helper.GetValueTypeString('%%(src_var)s'))) \ .Append('return %(failure_value)s;') ) c.Eblock('}') c.Sblock('else {') item_type = self._type_helper.FollowRef(underlying_type.item_type) if item_type.property_type == PropertyType.ENUM: - c.Concat(self._GenerateListValueToEnumArrayConversion( - item_type, - src_var, - dst_var, - failure_value, - is_ptr=is_ptr)) + c.Concat( + self._GenerateListValueToEnumArrayConversion(item_type, + src_var, + dst_var, + failure_value, + is_ptr=is_ptr)) else: args = ['%(src_var)s.GetList()', '%(dst_var)s'] if self._generate_error_messages: @@ -1214,15 +1203,13 @@ class _Generator(object): item_cpp_type = self._type_helper.GetCppType(item_type) if item_cpp_type != 'base::Value': - c.Append('if (!%s(%s)) {' % ( - self._util_cc_helper.PopulateArrayFromListFunction(is_ptr), - self._GenerateArgs(args, generate_error_messages=False))) + c.Append('if (!%s(%s)) {' % + (self._util_cc_helper.PopulateArrayFromListFunction(is_ptr), + self._GenerateArgs(args, generate_error_messages=False))) c.Sblock() if self._generate_error_messages: - c.Append( - 'array_parse_error = u"Error at key \'%(key)s\': " + ' - 'array_parse_error;' - ) + c.Append('array_parse_error = u"Error at key \'%(key)s\': " + ' + 'array_parse_error;') c.Concat(self._AppendError16('array_parse_error')) c.Append('return %(failure_value)s;') c.Eblock('}') @@ -1231,29 +1218,28 @@ class _Generator(object): c.Eblock('}') elif underlying_type.property_type == PropertyType.CHOICES: if is_ptr: - (c.Append('%(cpp_type)s temp;') + (c.Append('%(cpp_type)s temp;') \ .Append('if (!%%(cpp_type)s::Populate(%s))' % self._GenerateArgs( - ('%(src_var)s', 'temp'))) - .Append(' return %(failure_value)s;') + ('%(src_var)s', 'temp'))) \ + .Append(' return %(failure_value)s;') \ .Append('%(dst_var)s = std::move(temp);') ) else: (c.Append('if (!%%(cpp_type)s::Populate(%s))' % self._GenerateArgs( - ('%(src_var)s', '%(dst_var)s'))) + ('%(src_var)s', '%(dst_var)s'))) \ .Append(' return %(failure_value)s;')) elif underlying_type.property_type == PropertyType.ENUM: - c.Concat(self._GenerateStringToEnumConversion(underlying_type, - src_var, - dst_var, - failure_value)) + c.Concat( + self._GenerateStringToEnumConversion(underlying_type, src_var, + dst_var, failure_value)) elif underlying_type.property_type == PropertyType.BINARY: - (c.Sblock('if (!%(src_var)s.is_blob()) {') + (c.Sblock('if (!%(src_var)s.is_blob()) {') \ .Concat(self._AppendError16( 'u"\'%%(key)s\': expected binary, got " + ' + - self._util_cc_helper.GetValueTypeString('%%(src_var)s'))) + self._util_cc_helper.GetValueTypeString('%%(src_var)s'))) \ .Append('return %(failure_value)s;') ) - (c.Eblock('}') + (c.Eblock('}') \ .Sblock('else {') ) c.Append('%(dst_var)s = %(src_var)s.GetBlob();') @@ -1262,14 +1248,15 @@ class _Generator(object): raise NotImplementedError(type_) if c.IsEmpty(): return c - return Code().Sblock('{').Concat(c.Substitute({ - 'cpp_type': self._type_helper.GetCppType(type_), - 'src_var': src_var, - 'dst_var': dst_var, - 'failure_value': failure_value, - 'key': type_.name, - 'parent_key': type_.parent.name, - })).Eblock('}') + return Code().Sblock('{').Concat( + c.Substitute({ + 'cpp_type': self._type_helper.GetCppType(type_), + 'src_var': src_var, + 'dst_var': dst_var, + 'failure_value': failure_value, + 'key': type_.name, + 'parent_key': type_.parent.name, + })).Eblock('}') def _GenerateListValueToEnumArrayConversion(self, item_type, @@ -1287,21 +1274,18 @@ class _Generator(object): accessor = '->' cpp_type = self._type_helper.GetCppType(item_type) c.Append('%s.emplace();' % dst_var) - (c.Sblock('for (const auto& it : (%s).GetList()) {' % src_var) - .Append('%s tmp;' % self._type_helper.GetCppType(item_type)) + (c.Sblock('for (const auto& it : (%s).GetList()) {' % src_var) \ + .Append('%s tmp;' % self._type_helper.GetCppType(item_type)) \ .Concat(self._GenerateStringToEnumConversion(item_type, '(it)', 'tmp', - failure_value)) - .Append('%s%spush_back(tmp);' % (dst_var, accessor)) + failure_value)) \ + .Append('%s%spush_back(tmp);' % (dst_var, accessor)) \ .Eblock('}') ) return c - def _GenerateStringToEnumConversion(self, - type_, - src_var, - dst_var, + def _GenerateStringToEnumConversion(self, type_, src_var, dst_var, failure_value): """Returns Code that converts a string type in |src_var| to an enum with type |type_| in |dst_var|. In the generated code, if |src_var| is not @@ -1314,32 +1298,32 @@ class _Generator(object): cpp_type_namespace = '' if type_.namespace != self._namespace: namespace = cpp_util.GetCppNamespace( - type_.namespace.environment.namespace_pattern, - type_.namespace.unix_name) + type_.namespace.environment.namespace_pattern, + type_.namespace.unix_name) cpp_type_namespace = '%s::' % namespace (c.Append('const std::string* %s = %s.GetIfString();' % (enum_as_string, - src_var)) - .Sblock('if (!%s) {' % enum_as_string) + src_var)) \ + .Sblock('if (!%s) {' % enum_as_string) \ .Concat(self._AppendError16( 'u"\'%%(key)s\': expected string, got " + ' + - self._util_cc_helper.GetValueTypeString('%%(src_var)s'))) - .Append('return %s;' % failure_value) - .Eblock('}') + self._util_cc_helper.GetValueTypeString('%%(src_var)s'))) \ + .Append('return %s;' % failure_value) \ + .Eblock('}') \ .Append('%s = %sParse%s(*%s);' % (dst_var, cpp_type_namespace, cpp_util.Classname(type_.name), - enum_as_string)) + enum_as_string)) \ .Sblock('if (%s == %s) {' % (dst_var, self._type_helper.GetEnumDefaultValue(type_, - self._namespace))) + self._namespace))) \ .Concat(self._AppendError16( 'u\"\'%%(key)s\': "' + ( ' + %sGet%sParseError(*%s)' % (cpp_type_namespace, cpp_util.Classname(type_.name), - enum_as_string)))) - .Append('return %s;' % failure_value) - .Eblock('}') + enum_as_string)))) \ + .Append('return %s;' % failure_value) \ + .Eblock('}') \ .Substitute({'src_var': src_var, 'key': type_.name}) ) return c @@ -1368,17 +1352,18 @@ class _Generator(object): maybe_namespace = '' if cpp_namespace is None else '%s::' % cpp_namespace c.Sblock('const char* %sToString(%s enum_param) {' % - (maybe_namespace, classname)) + (maybe_namespace, classname)) c.Sblock('switch (enum_param) {') for enum_value in self._type_helper.FollowRef(type_).enum_values: name = enum_value.name - (c.Append('case %s: ' % self._type_helper.GetEnumValue(type_, enum_value)) + (c.Append('case %s: ' % + self._type_helper.GetEnumValue(type_, enum_value)) \ .Append(' return "%s";' % name)) - (c.Append('case %s:' % self._type_helper.GetEnumNoneValue(type_)) - .Append(' return "";') - .Eblock('}') - .Append('NOTREACHED_IN_MIGRATION();') - .Append('return "";') + (c.Append('case %s:' % self._type_helper.GetEnumNoneValue(type_)) \ + .Append(' return "";') \ + .Eblock('}') \ + .Append('NOTREACHED_IN_MIGRATION();') \ + .Append('return "";') \ .Eblock('}') ) return c @@ -1395,17 +1380,17 @@ class _Generator(object): maybe_namespace = '' if cpp_namespace is None else '%s::' % cpp_namespace c.Sblock('%s%s %sParse%s(std::string_view enum_string) {' % - (maybe_namespace, classname, maybe_namespace, classname)) + (maybe_namespace, classname, maybe_namespace, classname)) for _, enum_value in enumerate( - self._type_helper.FollowRef(type_).enum_values): + self._type_helper.FollowRef(type_).enum_values): # This is broken up into all ifs with no else ifs because we get # "fatal error C1061: compiler limit : blocks nested too deeply" # on Windows. name = enum_value.name - (c.Append('if (enum_string == "%s")' % name) + (c.Append('if (enum_string == "%s")' % name) \ .Append(' return %s;' % self._type_helper.GetEnumValue(type_, enum_value))) - (c.Append('return %s;' % self._type_helper.GetEnumNoneValue(type_)) + (c.Append('return %s;' % self._type_helper.GetEnumNoneValue(type_)) \ .Eblock('}') ) return c @@ -1425,12 +1410,10 @@ class _Generator(object): c.Sblock( 'std::u16string %sGet%sParseError(std::string_view enum_string) {' % (maybe_namespace, classname)) - error_message = ( - 'u\"expected \\"' + - '\\" or \\"'.join( - enum_value.name - for enum_value in self._type_helper.FollowRef(type_).enum_values) + - '\\", got \\"" + UTF8ToUTF16(enum_string) + u"\\""') + error_message = ('u\"expected \\"' + '\\" or \\"'.join( + enum_value.name + for enum_value in self._type_helper.FollowRef(type_).enum_values) + + '\\", got \\"" + UTF8ToUTF16(enum_string) + u"\\""') c.Append('return %s;' % error_message) c.Eblock('}') return c @@ -1450,26 +1433,27 @@ class _Generator(object): c.Concat(self._GeneratePropertyFunctions(function_scope, params)) (c.Sblock('base::Value::List %(function_scope)s' - 'Create(%(declaration_list)s) {') - .Append('base::Value::List create_results;') + 'Create(%(declaration_list)s) {') \ + .Append('base::Value::List create_results;') \ .Append('create_results.reserve(%d);' % len(params) if len(params) else '') ) declaration_list = [] for param in params: - declaration_list.append(cpp_util.GetParameterDeclaration( - param, self._type_helper.GetCppType(param.type_))) - c.Cblock(self._CreateValueFromType( - 'create_results.Append(%s);', - param.name, - param.type_, - param.unix_name)) + declaration_list.append( + cpp_util.GetParameterDeclaration( + param, self._type_helper.GetCppType(param.type_))) + c.Cblock( + self._CreateValueFromType('create_results.Append(%s);', param.name, + param.type_, param.unix_name)) c.Append('return create_results;') c.Eblock('}') c.Substitute({ 'function_scope': ('%s::' % function_scope) if function_scope else '', - 'declaration_list': ', '.join(declaration_list), - 'param_names': ', '.join(param.unix_name for param in params) + 'declaration_list': + ', '.join(declaration_list), + 'param_names': + ', '.join(param.unix_name for param in params) }) return c @@ -1477,8 +1461,8 @@ class _Generator(object): """Generates a constant string array for the event name. """ c = Code() - c.Append('const char kEventName[] = "%s.%s";' % ( - self._namespace.name, event.name)) + c.Append('const char kEventName[] = "%s.%s";' % + (self._namespace.name, event.name)) return c def _InitializePropertyToDefault(self, prop, dst): @@ -1490,13 +1474,10 @@ class _Generator(object): """ c = Code() underlying_type = self._type_helper.FollowRef(prop.type_) - if (underlying_type.property_type == PropertyType.ENUM and - prop.optional): - c.Append('%s.%s = %s;' % ( - dst, - prop.unix_name, - self._type_helper.GetEnumDefaultValue(underlying_type, - self._namespace))) + if (underlying_type.property_type == PropertyType.ENUM and prop.optional): + c.Append('%s.%s = %s;' % (dst, prop.unix_name, + self._type_helper.GetEnumDefaultValue( + underlying_type, self._namespace))) return c def _AppendError16(self, error16): @@ -1509,8 +1490,7 @@ class _Generator(object): c.Append('error = %s;' % error16) return c - def _GenerateParams( - self, params, generate_error_messages=None): + def _GenerateParams(self, params, generate_error_messages=None): """Builds the parameter list for a function, given an array of parameters. If |generate_error_messages| is specified, it overrides |self._generate_error_messages|. diff --git a/tools/json_schema_compiler/code_util.py b/tools/json_schema_compiler/code_util.py index 156d3b8d43d9e..8a7094e5528d8 100644 --- a/tools/json_schema_compiler/code_util.py +++ b/tools/json_schema_compiler/code_util.py @@ -2,19 +2,22 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. + class Code(object): """A convenience object for constructing code. Logically each object should be a block of code. All methods except |Render| and |IsEmpty| return self. """ + def __init__(self, indent_size=2, comment_length=80): self._code = [] self._indent_size = indent_size self._comment_length = comment_length self._line_prefixes = [] - def Append(self, line='', + def Append(self, + line='', substitute=True, indent_level=None, new_line=True, @@ -110,8 +113,11 @@ class Code(object): self.Append(line) return self - def Comment(self, comment, comment_prefix='// ', - wrap_indent=0, new_line=True): + def Comment(self, + comment, + comment_prefix='// ', + wrap_indent=0, + new_line=True): """Adds the given string as a comment. Will split the comment if it's too long. Use mainly for variable length @@ -119,6 +125,7 @@ class Code(object): Unaffected by code.Substitute(). """ + # Helper function to trim a comment to the maximum length, and return one # line and the remainder of the comment. def trim_comment(comment, max_len): @@ -196,6 +203,7 @@ class Code(object): class Line(object): """A line of code. """ + def __init__(self, value, substitute=True): self.value = value self.substitute = substitute diff --git a/tools/json_schema_compiler/code_util_test.py b/tools/json_schema_compiler/code_util_test.py index f164e4f5076a4..34856955658cc 100755 --- a/tools/json_schema_compiler/code_util_test.py +++ b/tools/json_schema_compiler/code_util_test.py @@ -6,7 +6,9 @@ from code_util import Code import unittest + class CodeTest(unittest.TestCase): + def testAppend(self): c = Code() c.Append('line') @@ -14,61 +16,56 @@ class CodeTest(unittest.TestCase): def testBlock(self): c = Code() - (c.Append('line') - .Sblock('sblock') - .Append('inner') - .Append('moreinner') - .Sblock('moresblock') - .Append('inner') - .Eblock('out') - .Append('inner') + (c.Append('line') \ + .Sblock('sblock') \ + .Append('inner') \ + .Append('moreinner') \ + .Sblock('moresblock') \ + .Append('inner') \ + .Eblock('out') \ + .Append('inner') \ .Eblock('out') ) self.assertEqual( - 'line\n' - 'sblock\n' - ' inner\n' - ' moreinner\n' - ' moresblock\n' - ' inner\n' - ' out\n' - ' inner\n' - 'out', - c.Render()) + 'line\n' + 'sblock\n' + ' inner\n' + ' moreinner\n' + ' moresblock\n' + ' inner\n' + ' out\n' + ' inner\n' + 'out', c.Render()) def testConcat(self): b = Code() - (b.Sblock('2') - .Append('2') + (b.Sblock('2') \ + .Append('2') \ .Eblock('2') ) c = Code() - (c.Sblock('1') - .Concat(b) - .Append('1') + (c.Sblock('1') \ + .Concat(b) \ + .Append('1') \ .Eblock('1') ) - self.assertMultiLineEqual( - '1\n' - ' 2\n' - ' 2\n' - ' 2\n' - ' 1\n' - '1', - c.Render()) + self.assertMultiLineEqual('1\n' + ' 2\n' + ' 2\n' + ' 2\n' + ' 1\n' + '1', c.Render()) d = Code() a = Code() a.Concat(d) self.assertEqual('', a.Render()) a.Concat(c) - self.assertEqual( - '1\n' - ' 2\n' - ' 2\n' - ' 2\n' - ' 1\n' - '1', - a.Render()) + self.assertEqual('1\n' + ' 2\n' + ' 2\n' + ' 2\n' + ' 1\n' + '1', a.Render()) def testConcatErrors(self): c = Code() @@ -89,11 +86,9 @@ class CodeTest(unittest.TestCase): c.Append('%(var1)s %(var2)s %(var3)s') c.Append('%(var2)s %(var1)s %(var3)s') c.Substitute({'var1': 'one', 'var2': 'two', 'var3': 'three'}) - self.assertEqual( - 'one two one\n' - 'one two three\n' - 'two one three', - c.Render()) + self.assertEqual('one two one\n' + 'one two three\n' + 'two one three', c.Render()) def testSubstituteErrors(self): # No unnamed placeholders allowed when substitute is run @@ -118,14 +113,13 @@ class CodeTest(unittest.TestCase): def testComment(self): long_comment = ('This comment is ninety one characters in longness, ' - 'that is, using a different word, length.') + 'that is, using a different word, length.') c = Code() c.Comment(long_comment) self.assertEqual( '// This comment is ninety one characters ' 'in longness, that is, using a different\n' - '// word, length.', - c.Render()) + '// word, length.', c.Render()) c = Code() c.Sblock('sblock') c.Comment(long_comment) @@ -139,27 +133,26 @@ class CodeTest(unittest.TestCase): 'eblock\n' '// This comment is ninety one characters in ' 'longness, that is, using a different\n' - '// word, length.', - c.Render()) + '// word, length.', c.Render()) # Words that cannot be broken up are left as too long. long_word = 'x' * 100 c = Code() c.Comment('xxx') c.Comment(long_word) c.Comment('xxx') - self.assertEqual( - '// xxx\n' - '// ' + 'x' * 100 + '\n' - '// xxx', - c.Render()) + self.assertEqual('// xxx\n' + '// ' + 'x' * 100 + '\n' + '// xxx', c.Render()) c = Code(indent_size=2, comment_length=40) - c.Comment('Pretend this is a Closure Compiler style comment, which should ' - 'both wrap and indent', comment_prefix=' * ', wrap_indent=4) + c.Comment( + 'Pretend this is a Closure Compiler style comment, which should ' + 'both wrap and indent', + comment_prefix=' * ', + wrap_indent=4) self.assertEqual( ' * Pretend this is a Closure Compiler\n' ' * style comment, which should both\n' - ' * wrap and indent', - c.Render()) + ' * wrap and indent', c.Render()) def testCommentWithSpecialCharacters(self): c = Code() @@ -170,8 +163,7 @@ class CodeTest(unittest.TestCase): d.Append('90') d.Concat(c) self.assertEqual('90\n' - '// 20% of 80%s', - d.Render()) + '// 20% of 80%s', d.Render()) def testLinePrefixes(self): c = Code() @@ -192,8 +184,7 @@ class CodeTest(unittest.TestCase): ' * x: y\n' ' * }\n' ' * }}\n' - ' */', - output) + ' */', output) def testSameLineAppendConcatComment(self): c = Code() @@ -206,12 +197,13 @@ class CodeTest(unittest.TestCase): c = Code() c.Append('This is a') c.Comment(' spectacular 80-character line thingy ' + - 'that fits wonderfully everywhere.', + 'that fits wonderfully everywhere.', comment_prefix='', new_line=False) - self.assertEqual('This is a spectacular 80-character line thingy that ' + - 'fits wonderfully everywhere.', - c.Render()) + self.assertEqual( + 'This is a spectacular 80-character line thingy that ' + + 'fits wonderfully everywhere.', c.Render()) + if __name__ == '__main__': unittest.main() diff --git a/tools/json_schema_compiler/compiler.py b/tools/json_schema_compiler/compiler.py index 7bd50051b2264..78b0f1e73bd70 100755 --- a/tools/json_schema_compiler/compiler.py +++ b/tools/json_schema_compiler/compiler.py @@ -101,12 +101,10 @@ def GenerateSchema( # Construct the type generator with all the namespaces in this model. schema_dir = os.path.dirname(os.path.relpath(file_paths[0], root)) - namespace_resolver = NamespaceResolver( - root, schema_dir, include_rules, cpp_namespace_pattern - ) - type_generator = CppTypeGenerator( - api_model, namespace_resolver, default_namespace - ) + namespace_resolver = NamespaceResolver(root, schema_dir, include_rules, + cpp_namespace_pattern) + type_generator = CppTypeGenerator(api_model, namespace_resolver, + default_namespace) if generator_name in ('cpp-bundle-registration', 'cpp-bundle-schema'): cpp_bundle_generator = CppBundleGenerator( root, @@ -187,12 +185,11 @@ if __name__ == '__main__': default='.', help=( 'logical include root directory. Path to schema files from specified' - ' dir will be the include path.' - ), - ) - parser.add_option( - '-d', '--destdir', help='root directory to output generated files.' + ' dir will be the include path.'), ) + parser.add_option('-d', + '--destdir', + help='root directory to output generated files.') parser.add_option( '-n', '--namespace', @@ -203,11 +200,9 @@ if __name__ == '__main__': '-b', '--bundle-name', default='', - help=( - 'A string to prepend to generated bundle class names, so that ' - 'multiple bundle rules can be used without conflicting. ' - 'Only used with one of the cpp-bundle generators.' - ), + help=('A string to prepend to generated bundle class names, so that ' + 'multiple bundle rules can be used without conflicting. ' + 'Only used with one of the cpp-bundle generators.'), ) parser.add_option( '-g', @@ -216,9 +211,7 @@ if __name__ == '__main__': choices=GENERATORS, help=( 'The generator to use to build the output code. Supported values are' - ' %s' - ) - % GENERATORS, + ' %s') % GENERATORS, ) parser.add_option( '-i', @@ -229,11 +222,9 @@ if __name__ == '__main__': parser.add_option( '-I', '--include-rules', - help=( - 'A list of paths to include when searching for referenced objects,' - " with the namespace separated by a ':'. Example: " - '/foo/bar:Foo::Bar::%(namespace)s' - ), + help=('A list of paths to include when searching for referenced objects,' + " with the namespace separated by a ':'. Example: " + '/foo/bar:Foo::Bar::%(namespace)s'), ) (opts, file_paths) = parser.parse_args() @@ -242,28 +233,23 @@ if __name__ == '__main__': sys.exit(0) # This is OK as a no-op # Unless in bundle mode, only one file should be specified. - if ( - opts.generator not in ('cpp-bundle-registration', 'cpp-bundle-schema') - and len(file_paths) > 1 - ): + if (opts.generator not in ('cpp-bundle-registration', 'cpp-bundle-schema') + and len(file_paths) > 1): # TODO(sashab): Could also just use file_paths[0] here and not complain. raise Exception( - 'Unless in bundle mode, only one file can be specified at a time.' - ) + 'Unless in bundle mode, only one file can be specified at a time.') def split_path_and_namespace(path_and_namespace): if ':' not in path_and_namespace: raise ValueError( 'Invalid include rule "%s". Rules must be of the form path:namespace' - % path_and_namespace - ) + % path_and_namespace) return path_and_namespace.split(':', 1) include_rules = [] if opts.include_rules: include_rules = list( - map(split_path_and_namespace, shlex.split(opts.include_rules)) - ) + map(split_path_and_namespace, shlex.split(opts.include_rules))) result = GenerateSchema( opts.generator, diff --git a/tools/json_schema_compiler/cpp_bundle_generator.py b/tools/json_schema_compiler/cpp_bundle_generator.py index 6c7619b64ed91..54b0d3e1f39e3 100644 --- a/tools/json_schema_compiler/cpp_bundle_generator.py +++ b/tools/json_schema_compiler/cpp_bundle_generator.py @@ -26,6 +26,7 @@ def _RemoveKey(node, key, type_restriction): for value in node: _RemoveKey(value, key, type_restriction) + def _RemoveUnneededFields(schema): """Returns a copy of |schema| with fields that aren't necessary at runtime removed. @@ -41,6 +42,7 @@ def _RemoveUnneededFields(schema): _RemoveKey(ret, 'manifest_keys', object) return ret + def _PrefixSchemaWithNamespace(schema): """Modifies |schema| in place to prefix all types and references with a namespace, if they aren't already qualified. That is, in the tabs API, this @@ -49,10 +51,11 @@ def _PrefixSchemaWithNamespace(schema): """ assert isinstance(schema, dict), "Schema is unexpected type" namespace = schema['namespace'] + def prefix(obj, key, mandatory): if not key in obj: - assert not mandatory, ( - 'Required key "%s" is not present in object.' % key) + assert not mandatory, ('Required key "%s" is not present in object.' % + key) return assert type(obj[key]) is str if obj[key].find('.') == -1: @@ -73,6 +76,7 @@ def _PrefixSchemaWithNamespace(schema): prefix(val, '$ref', False) for key, sub_val in val.items(): prefix_refs(sub_val) + prefix_refs(schema) return schema @@ -81,15 +85,8 @@ class CppBundleGenerator(object): """This class contains methods to generate code based on multiple schemas. """ - def __init__(self, - root, - model, - api_defs, - cpp_type_generator, - cpp_namespace_pattern, - bundle_name, - source_file_dir, - impl_dir): + def __init__(self, root, model, api_defs, cpp_type_generator, + cpp_namespace_pattern, bundle_name, source_file_dir, impl_dir): self._root = root self._model = model self._api_defs = api_defs @@ -196,9 +193,9 @@ class CppBundleGenerator(object): if function.nocompile: continue namespace_types_name = JsFunctionNameToClassName( - namespace.name, type_.name) - c.Concat(self._GenerateRegistrationEntry(namespace_types_name, - function)) + namespace.name, type_.name) + c.Concat( + self._GenerateRegistrationEntry(namespace_types_name, function)) if namespace_ifdefs is not None: c.Append("#endif // %s" % namespace_ifdefs, indent_level=0) @@ -218,6 +215,7 @@ class CppBundleGenerator(object): class _APIHGenerator(object): """Generates the header for API registration / declaration""" + def __init__(self, cpp_bundle): self._bundle = cpp_bundle @@ -234,7 +232,7 @@ class _APIHGenerator(object): self._bundle._GenerateBundleClass('GeneratedFunctionRegistry')) c.Sblock(' public:') c.Append('static void RegisterAll(' - 'ExtensionFunctionRegistry* registry);') + 'ExtensionFunctionRegistry* registry);') c.Eblock('};') c.Append() c.Concat(cpp_util.CloseNamespace(self._bundle._cpp_namespace)) @@ -251,9 +249,8 @@ class _APICCGenerator(object): c = code_util.Code() c.Append(cpp_util.CHROMIUM_LICENSE) c.Append() - c.Append('#include "%s"' % ( - cpp_util.ToPosixPath(os.path.join(self._bundle._impl_dir, - 'generated_api_registration.h')))) + c.Append('#include "%s"' % (cpp_util.ToPosixPath( + os.path.join(self._bundle._impl_dir, 'generated_api_registration.h')))) c.Append() c.Append('#include "build/build_config.h"') c.Append('#include "build/chromeos_buildflags.h"') @@ -261,17 +258,15 @@ class _APICCGenerator(object): for namespace in self._bundle._model.namespaces.values(): namespace_name = namespace.unix_name.replace("experimental_", "") implementation_header = namespace.compiler_options.get( - "implemented_in", - "%s/%s/%s_api.h" % (self._bundle._impl_dir, - namespace_name, - namespace_name)) + "implemented_in", "%s/%s/%s_api.h" % + (self._bundle._impl_dir, namespace_name, namespace_name)) if not os.path.exists( os.path.join(self._bundle._root, os.path.normpath(implementation_header))): if "implemented_in" in namespace.compiler_options: raise ValueError('Header file for namespace "%s" specified in ' - 'compiler_options not found: %s' % - (namespace.unix_name, implementation_header)) + 'compiler_options not found: %s' % + (namespace.unix_name, implementation_header)) continue ifdefs = self._bundle._GetPlatformIfdefs(namespace) if ifdefs is not None: @@ -283,7 +278,7 @@ class _APICCGenerator(object): c.Append("#endif // %s" % ifdefs, indent_level=0) c.Append() c.Append('#include ' - '"extensions/browser/extension_function_registry.h"') + '"extensions/browser/extension_function_registry.h"') c.Append() c.Concat(cpp_util.OpenNamespace(self._bundle._cpp_namespace)) c.Append() @@ -296,6 +291,7 @@ class _APICCGenerator(object): class _SchemasHGenerator(object): """Generates a code_util.Code object for the generated schemas .h file""" + def __init__(self, cpp_bundle): self._bundle = cpp_bundle @@ -322,8 +318,7 @@ class _SchemasHGenerator(object): def _FormatNameAsConstant(name): """Formats a name to be a C++ constant of the form kConstantName""" name = '%s%s' % (name[0].upper(), name[1:]) - return 'k%s' % re.sub('_[a-z]', - lambda m: m.group(0)[1].upper(), + return 'k%s' % re.sub('_[a-z]', lambda m: m.group(0)[1].upper(), name.replace('.', '_')) @@ -337,9 +332,8 @@ class _SchemasCCGenerator(object): c = code_util.Code() c.Append(cpp_util.CHROMIUM_LICENSE) c.Append() - c.Append('#include "%s"' % ( - cpp_util.ToPosixPath(os.path.join(self._bundle._source_file_dir, - 'generated_schemas.h')))) + c.Append('#include "%s"' % (cpp_util.ToPosixPath( + os.path.join(self._bundle._source_file_dir, 'generated_schemas.h')))) c.Append() c.Append('#include <algorithm>') c.Append('#include <iterator>') @@ -351,7 +345,7 @@ class _SchemasCCGenerator(object): for api in self._bundle._api_defs: namespace = self._bundle._model.namespaces[api.get('namespace')] json_content = json.dumps(_PrefixSchemaWithNamespace( - _RemoveUnneededFields(api)), + _RemoveUnneededFields(api)), separators=(',', ':')) # This will output a valid JSON C string. Note that some schemas are # too large to compile on windows. Split the JSON up into several @@ -381,8 +375,10 @@ class _SchemasCCGenerator(object): c.Append('static constexpr auto kSchemas = ' 'base::MakeFixedFlatMap<std::string_view, std::string_view>({') c.Sblock() - namespaces = [self._bundle._model.namespaces[api.get('namespace')].name - for api in self._bundle._api_defs] + namespaces = [ + self._bundle._model.namespaces[api.get('namespace')].name + for api in self._bundle._api_defs + ] for namespace in sorted(namespaces): schema_constant_name = _FormatNameAsConstant(namespace) c.Append('{"%s", %s},' % (namespace, schema_constant_name)) diff --git a/tools/json_schema_compiler/cpp_bundle_generator_test.py b/tools/json_schema_compiler/cpp_bundle_generator_test.py index fb4218feaa9e3..6ac3c2c1eca44 100755 --- a/tools/json_schema_compiler/cpp_bundle_generator_test.py +++ b/tools/json_schema_compiler/cpp_bundle_generator_test.py @@ -10,26 +10,29 @@ import json_schema import os import unittest + def _createCppBundleGenerator(file_path): json_object = json_schema.Load(file_path) model = Model() model.AddNamespace(json_object[0], file_path) - cpp_bundle_generator = CppBundleGenerator( - None, model, None, None, 'generated_api_schemas', - None, None, None) + cpp_bundle_generator = CppBundleGenerator(None, model, None, None, + 'generated_api_schemas', None, None, + None) return (cpp_bundle_generator, model) + def _getPlatformIfdefs(cpp_bundle_generator, model): return cpp_bundle_generator._GetPlatformIfdefs( list(list(model.namespaces.values())[0].functions.values())[0]) + class CppBundleGeneratorTest(unittest.TestCase): + def testIfDefsForWinLinux(self): cpp_bundle_generator, model = _createCppBundleGenerator( 'test/function_platform_win_linux.json') - self.assertEqual( - 'BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX)', - _getPlatformIfdefs(cpp_bundle_generator, model)) + self.assertEqual('BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX)', + _getPlatformIfdefs(cpp_bundle_generator, model)) def testIfDefsForAll(self): cpp_bundle_generator, model = _createCppBundleGenerator( @@ -43,7 +46,7 @@ class CppBundleGeneratorTest(unittest.TestCase): cpp_bundle_generator, model = _createCppBundleGenerator( 'test/function_platform_chromeos.json') self.assertEqual('BUILDFLAG(IS_CHROMEOS_ASH)', - _getPlatformIfdefs(cpp_bundle_generator, model)) + _getPlatformIfdefs(cpp_bundle_generator, model)) if __name__ == '__main__': diff --git a/tools/json_schema_compiler/cpp_generator.py b/tools/json_schema_compiler/cpp_generator.py index 18e7b25aef6f0..fec00838cdca4 100644 --- a/tools/json_schema_compiler/cpp_generator.py +++ b/tools/json_schema_compiler/cpp_generator.py @@ -5,7 +5,9 @@ from cc_generator import CCGenerator from h_generator import HGenerator + class CppGenerator(object): + def __init__(self, type_generator): self.h_generator = HGenerator(type_generator) self.cc_generator = CCGenerator(type_generator) diff --git a/tools/json_schema_compiler/cpp_namespace_environment.py b/tools/json_schema_compiler/cpp_namespace_environment.py index f71b8bce4c46b..046131469e4aa 100644 --- a/tools/json_schema_compiler/cpp_namespace_environment.py +++ b/tools/json_schema_compiler/cpp_namespace_environment.py @@ -2,6 +2,8 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. + class CppNamespaceEnvironment(object): + def __init__(self, namespace_pattern): self.namespace_pattern = namespace_pattern diff --git a/tools/json_schema_compiler/cpp_type_generator.py b/tools/json_schema_compiler/cpp_type_generator.py index bd2c8a2e094fe..c5c5afb2acc1a 100644 --- a/tools/json_schema_compiler/cpp_type_generator.py +++ b/tools/json_schema_compiler/cpp_type_generator.py @@ -8,11 +8,13 @@ import cpp_util from json_parse import OrderedDict import schema_util + class _TypeDependency(object): """Contains information about a dependency a namespace has on a type: the type's model, and whether that dependency is "hard" meaning that it cannot be forward declared. """ + def __init__(self, type_, hard=False): self.type_ = type_ self.hard = hard @@ -25,6 +27,7 @@ class CppTypeGenerator(object): """Manages the types of properties and provides utilities for getting the C++ type out of a model.Property """ + def __init__(self, model, namespace_resolver, default_namespace=None): """Creates a cpp_type_generator. The given root_namespace should be of the format extensions::api::sub. The generator will generate code suitable for @@ -40,9 +43,8 @@ class CppTypeGenerator(object): typename in an optional, for the regular case, or uses a base::expected for when it should support string errors. """ - return (('base::expected<{typename}, std::u16string>' - if support_errors else 'std::optional<{typename}>') - .format(typename=typename)) + return (('base::expected<{typename}, std::u16string>' if support_errors else + 'std::optional<{typename}>').format(typename=typename)) def GetEnumNoneValue(self, type_, full_name=True): """Gets the enum value in the given model. Property indicating no value has @@ -91,7 +93,7 @@ class CppTypeGenerator(object): result = '' for char in name: if char in {'_', '-'}: - change_to_upper=True + change_to_upper = True elif change_to_upper: # Numbers must be kept separate, for better readability (e.g. kX86_64). if char.isnumeric() and result and result[-1].isnumeric(): @@ -125,9 +127,9 @@ class CppTypeGenerator(object): prefix = '{classname}::'.format(classname=classname) # We kCamelCase the string, also removing any _ from the name, to allow # SHOUTY_CASE keys to be kCamelCase as well. - return '{prefix}k{name}'.format( - prefix=prefix, - name=self.FormatStringForEnumValue(enum_value.name)) + return '{prefix}k{name}'.format(prefix=prefix, + name=self.FormatStringForEnumValue( + enum_value.name)) def GetCppType(self, type_, is_optional=False): """Translates a model.Property or model.Type into its C++ type. @@ -155,8 +157,7 @@ class CppTypeGenerator(object): cpp_type = 'double' elif type_.property_type == PropertyType.STRING: cpp_type = 'std::string' - elif type_.property_type in (PropertyType.ENUM, - PropertyType.OBJECT, + elif type_.property_type in (PropertyType.ENUM, PropertyType.OBJECT, PropertyType.CHOICES): if self._default_namespace is type_.namespace: cpp_type = cpp_util.Classname(type_.name) @@ -164,8 +165,7 @@ class CppTypeGenerator(object): cpp_namespace = cpp_util.GetCppNamespace( type_.namespace.environment.namespace_pattern, type_.namespace.unix_name) - cpp_type = '%s::%s' % (cpp_namespace, - cpp_util.Classname(type_.name)) + cpp_type = '%s::%s' % (cpp_namespace, cpp_util.Classname(type_.name)) elif type_.property_type == PropertyType.ANY: cpp_type = 'base::Value' elif type_.property_type == PropertyType.FUNCTION: @@ -201,10 +201,9 @@ class CppTypeGenerator(object): return cpp_type def IsCopyable(self, type_): - return not (self.FollowRef(type_).property_type in (PropertyType.ANY, - PropertyType.ARRAY, - PropertyType.OBJECT, - PropertyType.CHOICES)) + return not (self.FollowRef(type_).property_type + in (PropertyType.ANY, PropertyType.ARRAY, PropertyType.OBJECT, + PropertyType.CHOICES)) def GenerateForwardDeclarations(self): """Returns the forward declarations for self._default_namespace. @@ -212,17 +211,16 @@ class CppTypeGenerator(object): c = Code() for namespace, deps in self._NamespaceTypeDependencies().items(): filtered_deps = [ - dep for dep in deps - # Add more ways to forward declare things as necessary. - if (not dep.hard and - dep.type_.property_type in (PropertyType.CHOICES, - PropertyType.OBJECT))] + dep for dep in deps + # Add more ways to forward declare things as necessary. + if (not dep.hard and dep.type_.property_type in (PropertyType.CHOICES, + PropertyType.OBJECT)) + ] if not filtered_deps: continue cpp_namespace = cpp_util.GetCppNamespace( - namespace.environment.namespace_pattern, - namespace.unix_name) + namespace.environment.namespace_pattern, namespace.unix_name) c.Concat(cpp_util.OpenNamespace(cpp_namespace)) for dep in filtered_deps: c.Append('struct %s;' % dep.type_.name) @@ -236,9 +234,9 @@ class CppTypeGenerator(object): # The inclusion of the std::string_view header is dependent on either the # presence of enums, or manifest keys. - include_string_view = (self._default_namespace.manifest_keys or - any(type_.property_type is PropertyType.ENUM for type_ in - self._default_namespace.types.values())) + include_string_view = (self._default_namespace.manifest_keys or any( + type_.property_type is PropertyType.ENUM + for type_ in self._default_namespace.types.values())) if include_string_view: c.Append('#include <string_view>') @@ -246,11 +244,10 @@ class CppTypeGenerator(object): # The header for `base::expected` should be included whenever error messages # are supposed to be returned, which only occurs with object, choices, or # functions. - if (generate_error_messages and ( - len(self._default_namespace.functions.values()) or - any(type_.property_type in - [PropertyType.OBJECT, PropertyType.CHOICES] for type_ in - self._default_namespace.types.values()))): + if (generate_error_messages + and (len(self._default_namespace.functions.values()) or any( + type_.property_type in [PropertyType.OBJECT, PropertyType.CHOICES] + for type_ in self._default_namespace.types.values()))): c.Append('#include "base/types/expected.h"') # Note: It's possible that there are multiple dependencies from the same @@ -361,11 +358,7 @@ class CppTypeGenerator(object): cpp_value = '"%s"' % cpp_value cpp_type = 'char' cpp_name = '%s[]' % cpp_name - c.Append(line % { - "type": cpp_type, - "name": cpp_name, - "value": cpp_value - }) + c.Append(line % {"type": cpp_type, "name": cpp_name, "value": cpp_value}) else: has_child_code = False c.Sblock('namespace %s {' % prop.name) diff --git a/tools/json_schema_compiler/cpp_type_generator_test.py b/tools/json_schema_compiler/cpp_type_generator_test.py index f69b6351ce9b5..59c3f3fab8cb8 100755 --- a/tools/json_schema_compiler/cpp_type_generator_test.py +++ b/tools/json_schema_compiler/cpp_type_generator_test.py @@ -12,7 +12,9 @@ import unittest from collections import defaultdict + class _FakeSchemaLoader(object): + def __init__(self, model): self._model = model @@ -22,13 +24,15 @@ class _FakeSchemaLoader(object): return default if type_name in default.types else None return self._model.namespaces[parts[0]] + class CppTypeGeneratorTest(unittest.TestCase): + def setUp(self): self.models = defaultdict(model.Model) forbidden_json = CachedLoad('test/forbidden.json') - self.models['forbidden'].AddNamespace( - forbidden_json[0], 'path/to/forbidden.json') + self.models['forbidden'].AddNamespace(forbidden_json[0], + 'path/to/forbidden.json') permissions_json = CachedLoad('test/permissions.json') self.permissions = self.models['permissions'].AddNamespace( @@ -59,12 +63,13 @@ class CppTypeGeneratorTest(unittest.TestCase): objects_movable_idl = idl_schema.Load('test/objects_movable.idl') self.objects_movable = self.models['objects_movable'].AddNamespace( - objects_movable_idl[0], 'path/to/objects_movable.idl', + objects_movable_idl[0], + 'path/to/objects_movable.idl', include_compiler_options=True) self.simple_api_json = CachedLoad('test/simple_api.json') - self.models['simple_api'].AddNamespace( - self.simple_api_json[0], 'path/to/simple_api.json') + self.models['simple_api'].AddNamespace(self.simple_api_json[0], + 'path/to/simple_api.json') self.crossref_enums_json = CachedLoad('test/crossref_enums.json') self.crossref_enums = self.models['crossref_enums'].AddNamespace( @@ -73,8 +78,7 @@ class CppTypeGeneratorTest(unittest.TestCase): self.crossref_enums_array_json = CachedLoad( 'test/crossref_enums_array.json') self.models['crossref_enums_array'].AddNamespace( - self.crossref_enums_array_json[0], - 'path/to/crossref_enums_array.json') + self.crossref_enums_array_json[0], 'path/to/crossref_enums_array.json') def testGenerateIncludesAndForwardDeclarations(self): m = model.Model() @@ -88,7 +92,7 @@ class CppTypeGeneratorTest(unittest.TestCase): self.assertEqual('', manager.GenerateIncludes().Render()) self.assertEqual('#include "path/to/tabs.h"', - manager.GenerateIncludes(include_soft=True).Render()) + manager.GenerateIncludes(include_soft=True).Render()) self.assertEqual( 'namespace tabs {\n' 'struct Tab;\n' @@ -96,14 +100,14 @@ class CppTypeGeneratorTest(unittest.TestCase): manager.GenerateForwardDeclarations().Render()) m = model.Model() - m.AddNamespace(self.windows_json[0], - 'path/to/windows.json', - environment=CppNamespaceEnvironment( - 'foo::bar::%(namespace)s')) - m.AddNamespace(self.tabs_json[0], - 'path/to/tabs.json', - environment=CppNamespaceEnvironment( - 'foo::bar::%(namespace)s')) + m.AddNamespace( + self.windows_json[0], + 'path/to/windows.json', + environment=CppNamespaceEnvironment('foo::bar::%(namespace)s')) + m.AddNamespace( + self.tabs_json[0], + 'path/to/tabs.json', + environment=CppNamespaceEnvironment('foo::bar::%(namespace)s')) manager = CppTypeGenerator(m, _FakeSchemaLoader(m)) self.assertEqual( 'namespace foo {\n' @@ -134,9 +138,10 @@ class CppTypeGeneratorTest(unittest.TestCase): manager = CppTypeGenerator(m, _FakeSchemaLoader(m), default_namespace=dependency_tester) - self.assertEqual('#include "path/to/browser_action.h"\n' - '#include "path/to/font_settings.h"', - manager.GenerateIncludes().Render()) + self.assertEqual( + '#include "path/to/browser_action.h"\n' + '#include "path/to/font_settings.h"', + manager.GenerateIncludes().Render()) self.assertEqual('', manager.GenerateForwardDeclarations().Render()) def testGetCppTypeSimple(self): @@ -167,7 +172,7 @@ class CppTypeGeneratorTest(unittest.TestCase): def testGetCppTypeArray(self): manager = CppTypeGenerator(self.models.get('windows'), - _FakeSchemaLoader(None)) + _FakeSchemaLoader(None)) self.assertEqual( 'std::vector<Window>', manager.GetCppType( @@ -183,9 +188,8 @@ class CppTypeGeneratorTest(unittest.TestCase): _FakeSchemaLoader(None)) self.assertEqual( 'std::vector<MovablePod>', - manager.GetCppType( - self.objects_movable.types['MovableParent']. - properties['pods'].type_)) + manager.GetCppType(self.objects_movable.types['MovableParent']. + properties['pods'].type_)) def testGetCppTypeLocalRef(self): manager = CppTypeGenerator(self.models.get('tabs'), _FakeSchemaLoader(None)) @@ -211,7 +215,8 @@ class CppTypeGeneratorTest(unittest.TestCase): def testGetCppTypeWithPadForGeneric(self): manager = CppTypeGenerator(self.models.get('permissions'), _FakeSchemaLoader(None)) - self.assertEqual('std::vector<std::string>', + self.assertEqual( + 'std::vector<std::string>', manager.GetCppType( self.permissions.types['Permissions'].properties['origins'].type_)) self.assertEqual( @@ -235,7 +240,7 @@ class CppTypeGeneratorTest(unittest.TestCase): _FakeSchemaLoader(m)) self.assertEqual('#include "path/to/simple_api.h"', - manager.GenerateIncludes().Render()) + manager.GenerateIncludes().Render()) def testHardIncludesForEnumArrays(self): """Tests that enums in arrays generate hard includes. Note that it's @@ -253,7 +258,7 @@ class CppTypeGeneratorTest(unittest.TestCase): _FakeSchemaLoader(m)) self.assertEqual('#include "path/to/simple_api.h"', - manager.GenerateIncludes().Render()) + manager.GenerateIncludes().Render()) def testCrossNamespaceGetEnumDefaultValue(self): m = model.Model() @@ -272,9 +277,9 @@ class CppTypeGeneratorTest(unittest.TestCase): self.assertEqual( 'namespace1::api::simple_api::TestEnum()', manager.GetEnumDefaultValue( - self.crossref_enums.types['CrossrefType'] - .properties['testEnumOptional'].type_, - self.crossref_enums)) + self.crossref_enums.types['CrossrefType']. + properties['testEnumOptional'].type_, self.crossref_enums)) + if __name__ == '__main__': unittest.main() diff --git a/tools/json_schema_compiler/cpp_util.py b/tools/json_schema_compiler/cpp_util.py index 6f5437b2cba6f..b34e438ca1c1d 100644 --- a/tools/json_schema_compiler/cpp_util.py +++ b/tools/json_schema_compiler/cpp_util.py @@ -1,7 +1,6 @@ # Copyright 2012 The Chromium Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. - """Utilies and constants specific to Chromium C++ code. """ @@ -12,11 +11,9 @@ import os import posixpath import re -CHROMIUM_LICENSE = ( -"""// Copyright %d The Chromium Authors +CHROMIUM_LICENSE = ("""// Copyright %d The Chromium Authors // Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file.""" % datetime.now().year -) +// found in the LICENSE file.""" % datetime.now().year) GENERATED_FILE_MESSAGE = """// GENERATED FROM THE API DEFINITION IN // %s // by tools/json_schema_compiler. @@ -33,6 +30,7 @@ GENERATED_FEATURE_MESSAGE = """// GENERATED FROM THE FEATURE DEFINITIONS IN // DO NOT EDIT. """ + def Classname(s): """Translates a namespace name or function name into something more suited to C++. @@ -56,6 +54,7 @@ def Classname(s): result = '_' + result return result + def GetAsFundamentalValue(type_, src): """Returns the C++ code for retrieving a fundamental type from a Value into a variable. @@ -68,9 +67,9 @@ def GetAsFundamentalValue(type_, src): s = '%s.GetIfDouble()' elif type_.property_type == PropertyType.INTEGER: s = '%s.GetIfInt()' - elif (type_.property_type == PropertyType.STRING or - (type_.property_type == PropertyType.FUNCTION and - type_.is_serializable_function)): + elif (type_.property_type == PropertyType.STRING + or (type_.property_type == PropertyType.FUNCTION + and type_.is_serializable_function)): s = '%s.GetIfString()' else: raise ValueError('Type %s is not a fundamental value' % type_.name) @@ -104,43 +103,49 @@ def GetValueType(type_): raise ValueError('Invalid type: %s' % type_.name) + def ShouldUseStdOptional(type_): """Called to validate whether or not an optional value should be represented with std::optional. This function is a temporary utility, while optional fields are gradually migrated away from using std::unique_ptr. """ - if type_.property_type in (PropertyType.ANY, - PropertyType.ARRAY, - PropertyType.BINARY, - PropertyType.BOOLEAN, - PropertyType.CHOICES, - PropertyType.DOUBLE, - PropertyType.FUNCTION, - PropertyType.INTEGER, - PropertyType.OBJECT, - PropertyType.STRING): + if type_.property_type in ( + PropertyType.ANY, + PropertyType.ARRAY, + PropertyType.BINARY, + PropertyType.BOOLEAN, + PropertyType.CHOICES, + PropertyType.DOUBLE, + PropertyType.FUNCTION, + PropertyType.INTEGER, + PropertyType.OBJECT, + PropertyType.STRING, + ): return True return False + def GetParameterDeclaration(param, type_): """Gets a parameter declaration of a given model.Property and its C++ type. """ - if param.type_.property_type in (PropertyType.ANY, - PropertyType.ARRAY, - PropertyType.BINARY, - PropertyType.CHOICES, - PropertyType.OBJECT, - PropertyType.REF, - PropertyType.STRING): + if param.type_.property_type in ( + PropertyType.ANY, + PropertyType.ARRAY, + PropertyType.BINARY, + PropertyType.CHOICES, + PropertyType.OBJECT, + PropertyType.REF, + PropertyType.STRING, + ): arg = 'const %(type)s& %(name)s' else: arg = '%(type)s %(name)s' return arg % { - 'type': type_, - 'name': param.unix_name, + 'type': type_, + 'name': param.unix_name, } @@ -150,10 +155,10 @@ def GenerateIfndefName(file_path): e.g chrome/extensions/gen/file.h becomes CHROME_EXTENSIONS_GEN_FILE_H__. """ - return (('%s__' % file_path).upper() - .replace('\\', '_') - .replace('/', '_') - .replace('-', '_') + return (('%s__' % file_path).upper() \ + .replace('\\', '_') \ + .replace('/', '_') \ + .replace('-', '_') \ .replace('.', '_')) @@ -180,7 +185,7 @@ def FeatureNameToConstantName(feature_name): """Returns a kName for a feature's name. """ return ('k' + ''.join(word[0].upper() + word[1:] - for word in feature_name.replace('.', ' ').split())) + for word in feature_name.replace('.', ' ').split())) def UnixNameToConstantName(unix_name): @@ -189,6 +194,7 @@ def UnixNameToConstantName(unix_name): """ return ('k' + ''.join(word.capitalize() for word in unix_name.split('_'))) + def IsUnixName(s): # type (str) -> bool """Returns true if |s| is of the type unix_name i.e. only has lower cased @@ -196,6 +202,7 @@ def IsUnixName(s): """ return all(x.islower() or x == '_' for x in s) and '_' in s + def ToPosixPath(path): """Returns |path| with separator converted to POSIX style. @@ -220,7 +227,7 @@ def GetCppNamespace(pattern, namespace): # For some reason Windows builds escape the % characters, so unescape them. # This means that %% can never appear legitimately within a pattern, but # that's ok. It should never happen. - cpp_namespace = pattern.replace('%%', '%') % { 'namespace': namespace } + cpp_namespace = pattern.replace('%%', '%') % {'namespace': namespace} assert '%' not in cpp_namespace, \ ('Did not manage to fully substitute namespace "%s" into pattern "%s"' % (namespace, pattern)) diff --git a/tools/json_schema_compiler/cpp_util_test.py b/tools/json_schema_compiler/cpp_util_test.py index cdf8a297a5436..63743680dcfca 100755 --- a/tools/json_schema_compiler/cpp_util_test.py +++ b/tools/json_schema_compiler/cpp_util_test.py @@ -5,35 +5,27 @@ import unittest -from cpp_util import ( - Classname, - CloseNamespace, - GetCppNamespace, - GenerateIfndefName, - OpenNamespace -) +from cpp_util import (Classname, CloseNamespace, GetCppNamespace, + GenerateIfndefName, OpenNamespace) + class CppUtilTest(unittest.TestCase): + def testClassname(self): self.assertEqual('Permissions', Classname('permissions')) - self.assertEqual('UpdateAllTheThings', - Classname('updateAllTheThings')) + self.assertEqual('UpdateAllTheThings', Classname('updateAllTheThings')) self.assertEqual('Aa_Bb_Cc', Classname('aa.bb.cc')) def testNamespaceDeclaration(self): - self.assertEqual('namespace foo {', - OpenNamespace('foo').Render()) - self.assertEqual('} // namespace foo', - CloseNamespace('foo').Render()) + self.assertEqual('namespace foo {', OpenNamespace('foo').Render()) + self.assertEqual('} // namespace foo', CloseNamespace('foo').Render()) - self.assertEqual( - 'namespace extensions {\n' - 'namespace foo {', - OpenNamespace('extensions::foo').Render()) - self.assertEqual( - '} // namespace foo\n' - '} // namespace extensions', - CloseNamespace('extensions::foo').Render()) + self.assertEqual('namespace extensions {\n' + 'namespace foo {', + OpenNamespace('extensions::foo').Render()) + self.assertEqual('} // namespace foo\n' + '} // namespace extensions', + CloseNamespace('extensions::foo').Render()) self.assertEqual( 'namespace extensions {\n' diff --git a/tools/json_schema_compiler/feature_compiler.py b/tools/json_schema_compiler/feature_compiler.py index b59a98411b874..bef297602ba0e 100644 --- a/tools/json_schema_compiler/feature_compiler.py +++ b/tools/json_schema_compiler/feature_compiler.py @@ -72,6 +72,7 @@ CC_FILE_END = """ } // namespace extensions """ + def ToPosixPath(path): """Returns |path| with separator converted to POSIX style. @@ -79,11 +80,13 @@ def ToPosixPath(path): """ return path.replace(os.path.sep, posixpath.sep) + # Returns true if the list 'l' only contains strings that are a hex-encoded SHA1 # hashes. def ListContainsOnlySha1Hashes(l): return len(list(filter(lambda s: not re.match("^[A-F0-9]{40}$", s), l))) == 0 + # A "grammar" for what is and isn't allowed in the features.json files. This # grammar has to list all possible keys and the requirements for each. The # format of each entry is: @@ -170,19 +173,25 @@ FEATURE_GRAMMAR = ({ list: { 'enum_map': { 'privileged_extension': - 'mojom::ContextType::kPrivilegedExtension', - 'privileged_web_page': 'mojom::ContextType::kPrivilegedWebPage', - 'content_script': 'mojom::ContextType::kContentScript', + 'mojom::ContextType::kPrivilegedExtension', + 'privileged_web_page': + 'mojom::ContextType::kPrivilegedWebPage', + 'content_script': + 'mojom::ContextType::kContentScript', 'lock_screen_extension': - 'mojom::ContextType::kLockscreenExtension', + 'mojom::ContextType::kLockscreenExtension', 'offscreen_extension': - 'mojom::ContextType::kOffscreenExtension', - 'user_script': 'mojom::ContextType::kUserScript', - 'web_page': 'mojom::ContextType::kWebPage', - 'webui': 'mojom::ContextType::kWebUi', - 'webui_untrusted': 'mojom::ContextType::kUntrustedWebUi', + 'mojom::ContextType::kOffscreenExtension', + 'user_script': + 'mojom::ContextType::kUserScript', + 'web_page': + 'mojom::ContextType::kWebPage', + 'webui': + 'mojom::ContextType::kWebUi', + 'webui_untrusted': + 'mojom::ContextType::kUntrustedWebUi', 'unprivileged_extension': - 'mojom::ContextType::kUnprivilegedExtension', + 'mojom::ContextType::kUnprivilegedExtension', }, 'allow_all': True, 'allow_empty': True @@ -210,12 +219,18 @@ FEATURE_GRAMMAR = ({ 'extension_types': { list: { 'enum_map': { - 'extension': 'Manifest::TYPE_EXTENSION', - 'hosted_app': 'Manifest::TYPE_HOSTED_APP', - 'legacy_packaged_app': 'Manifest::TYPE_LEGACY_PACKAGED_APP', - 'platform_app': 'Manifest::TYPE_PLATFORM_APP', - 'shared_module': 'Manifest::TYPE_SHARED_MODULE', - 'theme': 'Manifest::TYPE_THEME', + 'extension': + 'Manifest::TYPE_EXTENSION', + 'hosted_app': + 'Manifest::TYPE_HOSTED_APP', + 'legacy_packaged_app': + 'Manifest::TYPE_LEGACY_PACKAGED_APP', + 'platform_app': + 'Manifest::TYPE_PLATFORM_APP', + 'shared_module': + 'Manifest::TYPE_SHARED_MODULE', + 'theme': + 'Manifest::TYPE_THEME', 'login_screen_extension': 'Manifest::TYPE_LOGIN_SCREEN_EXTENSION', 'chromeos_system_extension': @@ -288,10 +303,12 @@ FEATURE_GRAMMAR = ({ 'session_types': { list: { 'enum_map': { - 'regular': 'mojom::FeatureSessionType::kRegular', - 'kiosk': 'mojom::FeatureSessionType::kKiosk', + 'regular': + 'mojom::FeatureSessionType::kRegular', + 'kiosk': + 'mojom::FeatureSessionType::kKiosk', 'kiosk.autolaunched': - 'mojom::FeatureSessionType::kAutolaunchedKiosk', + 'mojom::FeatureSessionType::kAutolaunchedKiosk', } } }, @@ -301,21 +318,27 @@ FEATURE_GRAMMAR = ({ }, }) -FEATURE_TYPES = ['APIFeature', 'BehaviorFeature', - 'ManifestFeature', 'PermissionFeature'] +FEATURE_TYPES = [ + 'APIFeature', 'BehaviorFeature', 'ManifestFeature', 'PermissionFeature' +] + def HasProperty(property_name, value): return property_name in value + def HasAtLeastOneProperty(property_names, value): return any([HasProperty(name, value) for name in property_names]) + def DoesNotHaveAllProperties(property_names, value): return not all([HasProperty(name, value) for name in property_names]) + def DoesNotHaveProperty(property_name, value): return property_name not in value + def DoesNotHavePropertyInComplexFeature(property_name, feature, all_features): if type(feature) is ComplexFeature: for child_feature in feature.feature_list: @@ -323,6 +346,7 @@ def DoesNotHavePropertyInComplexFeature(property_name, feature, all_features): return False return True + def IsEmptyContextsAllowed(feature, all_features): # An alias feature wouldn't have the 'contexts' feature value. if feature.GetValue('source'): @@ -338,12 +362,13 @@ def IsEmptyContextsAllowed(feature, all_features): assert contexts, 'contexts must have been specified for the APIFeature' allowlisted_empty_context_namespaces = [ - 'manifestTypes', - 'extensionsManifestTypes', - 'empty_contexts' # Only added for testing. + 'manifestTypes', + 'extensionsManifestTypes', + 'empty_contexts' # Only added for testing. ] - return (contexts != '{}' or - feature.name in allowlisted_empty_context_namespaces) + return (contexts != '{}' + or feature.name in allowlisted_empty_context_namespaces) + def IsFeatureCrossReference(property_name, reverse_property_name, feature, all_features): @@ -374,6 +399,7 @@ def IsFeatureCrossReference(property_name, reverse_property_name, feature, return True return reverse_reference_value == ('"%s"' % feature.name) + # Verifies that a feature with an allowlist is not available to hosted apps, # returning true on success. def DoesNotHaveAllowlistForHostedApps(value): @@ -428,92 +454,90 @@ def DoesNotHaveAllowlistForHostedApps(value): SIMPLE_FEATURE_CPP_CLASSES = ({ - 'APIFeature': 'SimpleFeature', - 'ManifestFeature': 'ManifestFeature', - 'PermissionFeature': 'PermissionFeature', - 'BehaviorFeature': 'SimpleFeature', + 'APIFeature': 'SimpleFeature', + 'ManifestFeature': 'ManifestFeature', + 'PermissionFeature': 'PermissionFeature', + 'BehaviorFeature': 'SimpleFeature', }) VALIDATION = ({ - 'all': [ - (partial(HasAtLeastOneProperty, ['channel', 'dependencies']), - 'Features must specify either a channel or dependencies'), - (DoesNotHaveAllowlistForHostedApps, - 'Hosted apps are not allowed to use restricted features'), - ], - 'APIFeature': [ - (partial(HasProperty, 'contexts'), - 'APIFeatures must specify the contexts property'), - (partial(DoesNotHaveAllProperties, ['alias', 'source']), - 'Features cannot specify both alias and source.') - ], - 'ManifestFeature': [ - (partial(HasProperty, 'extension_types'), - 'ManifestFeatures must specify at least one extension type'), - (partial(DoesNotHaveProperty, 'contexts'), - 'ManifestFeatures do not support contexts.'), - (partial(DoesNotHaveProperty, 'alias'), - 'ManifestFeatures do not support alias.'), - (partial(DoesNotHaveProperty, 'source'), - 'ManifestFeatures do not support source.'), - # The `required_buildflags` field is intended to be used to toggle the - # availability of certain APIs; if we support this for feature types other - # than APIFeature, we may emit warnings that are visible to developers which - # is not desirable. - (partial(DoesNotHaveProperty, 'required_buildflags'), - 'ManifestFeatures do not support required_buildflags.'), - ], - 'BehaviorFeature': [ - (partial(DoesNotHaveProperty, 'alias'), - 'BehaviorFeatures do not support alias.'), - (partial(DoesNotHaveProperty, 'source'), - 'BehaviorFeatures do not support source.'), - (partial(DoesNotHaveProperty, 'required_buildflags'), - # The `required_buildflags` field is intended to be used to toggle the - # availability of certain APIs; if we support this for feature types other - # than APIFeature, we may emit warnings that are visible to developers which - # is not desirable. - 'BehaviorFeatures do not support required_buildflags.'), - ], - 'PermissionFeature': [ - (partial(HasProperty, 'extension_types'), - 'PermissionFeatures must specify at least one extension type'), - (partial(DoesNotHaveProperty, 'contexts'), - 'PermissionFeatures do not support contexts.'), - (partial(DoesNotHaveProperty, 'alias'), - 'PermissionFeatures do not support alias.'), - (partial(DoesNotHaveProperty, 'source'), - 'PermissionFeatures do not support source.'), - (partial(DoesNotHaveProperty, 'required_buildflags'), - # The `required_buildflags` field is intended to be used to toggle the - # availability of certain APIs; if we support this for feature types other - # than APIFeature, we may emit warnings that are visible to developers which - # is not desirable. - 'PermissionFeatures do not support required_buildflags.'), - ], + 'all': [ + (partial(HasAtLeastOneProperty, ['channel', 'dependencies']), + 'Features must specify either a channel or dependencies'), + (DoesNotHaveAllowlistForHostedApps, + 'Hosted apps are not allowed to use restricted features'), + ], + 'APIFeature': + [(partial(HasProperty, + 'contexts'), 'APIFeatures must specify the contexts property'), + (partial(DoesNotHaveAllProperties, ['alias', 'source']), + 'Features cannot specify both alias and source.')], + 'ManifestFeature': [ + (partial(HasProperty, 'extension_types'), + 'ManifestFeatures must specify at least one extension type'), + (partial(DoesNotHaveProperty, + 'contexts'), 'ManifestFeatures do not support contexts.'), + (partial(DoesNotHaveProperty, + 'alias'), 'ManifestFeatures do not support alias.'), + (partial(DoesNotHaveProperty, + 'source'), 'ManifestFeatures do not support source.'), + # The `required_buildflags` field is intended to be used to toggle the + # availability of certain APIs; if we support this for feature types + # other than APIFeature, we may emit warnings that are visible to + # developers which is not desirable. + (partial(DoesNotHaveProperty, 'required_buildflags'), + 'ManifestFeatures do not support required_buildflags.'), + ], + 'BehaviorFeature': [ + (partial(DoesNotHaveProperty, + 'alias'), 'BehaviorFeatures do not support alias.'), + (partial(DoesNotHaveProperty, + 'source'), 'BehaviorFeatures do not support source.'), + # The `required_buildflags` field is intended to be used to toggle the + # availability of certain APIs; if we support this for feature types + # other than APIFeature, we may emit warnings that are visible to + # developers which is not desirable. + (partial(DoesNotHaveProperty, 'required_buildflags'), + 'BehaviorFeatures do not support required_buildflags.'), + ], + 'PermissionFeature': [ + (partial(HasProperty, 'extension_types'), + 'PermissionFeatures must specify at least one extension type'), + (partial(DoesNotHaveProperty, + 'contexts'), 'PermissionFeatures do not support contexts.'), + (partial(DoesNotHaveProperty, + 'alias'), 'PermissionFeatures do not support alias.'), + (partial(DoesNotHaveProperty, + 'source'), 'PermissionFeatures do not support source.'), + # The `required_buildflags` field is intended to be used to toggle the + # availability of certain APIs; if we support this for feature types + # other than APIFeature, we may emit warnings that are visible to + # developers which is not desirable. + (partial(DoesNotHaveProperty, 'required_buildflags'), + 'PermissionFeatures do not support required_buildflags.'), + ], }) FINAL_VALIDATION = ({ - 'all': [ - # A complex feature requires at least one child entry at all times; with - # `required_buildflags` it becomes harder to guarantee that this holds for - # every potential combination of the provided flags. - (partial(DoesNotHavePropertyInComplexFeature, 'required_buildflags'), - 'required_buildflags cannot be nested in a ComplexFeature'), - ], - 'APIFeature': [ - (partial(IsFeatureCrossReference, 'alias', 'source'), - 'A feature alias property should reference a feature whose source ' - 'property references it back.'), - (partial(IsFeatureCrossReference, 'source', 'alias'), - 'A feature source property should reference a feature whose alias ' - 'property references it back.'), - (IsEmptyContextsAllowed, - 'An empty contexts list is not allowed for this feature.') - ], - 'ManifestFeature': [], - 'BehaviorFeature': [], - 'PermissionFeature': [] + 'all': [ + # A complex feature requires at least one child entry at all times; with + # `required_buildflags` it becomes harder to guarantee that this holds + # for every potential combination of the provided flags. + (partial(DoesNotHavePropertyInComplexFeature, 'required_buildflags'), + 'required_buildflags cannot be nested in a ComplexFeature'), + ], + 'APIFeature': + [(partial(IsFeatureCrossReference, 'alias', 'source'), + 'A feature alias property should reference a feature whose source ' + 'property references it back.'), + (partial(IsFeatureCrossReference, 'source', 'alias'), + 'A feature source property should reference a feature whose alias ' + 'property references it back.'), + (IsEmptyContextsAllowed, + 'An empty contexts list is not allowed for this feature.')], + 'ManifestFeature': [], + 'BehaviorFeature': [], + 'PermissionFeature': [] }) # These keys can not be set on a feature and are hence ignored. @@ -523,6 +547,7 @@ IGNORED_KEYS = ['default_parent', 'required_buildflags'] # can be disabled for testing. ENABLE_ASSERTIONS = True + def GetCodeForFeatureValues(feature_values): """ Gets the Code object for setting feature values for this object. """ c = Code() @@ -533,10 +558,12 @@ def GetCodeForFeatureValues(feature_values): c.Append('feature->set_%s(%s);' % (key, feature_values[key])) return c + class Feature(object): """A representation of a single simple feature that can handle all parsing, validation, and code generation. """ + def __init__(self, name): self.name = name self.has_parent = False @@ -557,10 +584,10 @@ class Feature(object): """Adds an error relating to a particular key in the feature. """ self.AddError('Error parsing feature "%s" at key "%s": %s' % - (self.name, key, error)) + (self.name, key, error)) - def _GetCheckedValue(self, key, expected_type, expected_values, - enum_map, value): + def _GetCheckedValue(self, key, expected_type, expected_values, enum_map, + value): """Returns a string to be used in the generated C++ code for a given key's python value, or None if the value is invalid. For example, if the python value is True, this returns 'true', for a string foo, this returns "foo", @@ -737,13 +764,15 @@ class Feature(object): return values def GetErrors(self): - return self.errors; + return self.errors + class ComplexFeature(Feature): """ Complex feature - feature that is comprised of list of features. Overall complex feature is available if any of contained feature is available. """ + def __init__(self, name): Feature.__init__(self, name) self.feature_list = [] @@ -779,11 +808,13 @@ class ComplexFeature(Feature): errors.extend(feature.GetErrors()) return errors + class FeatureCompiler(object): """A compiler to load, parse, and generate C++ code for a number of features.json files.""" - def __init__(self, chrome_root, source_files, feature_type, - method_name, out_root, gen_dir_relpath, out_base_filename): + + def __init__(self, chrome_root, source_files, feature_type, method_name, + out_root, gen_dir_relpath, out_base_filename): # See __main__'s ArgumentParser for documentation on these properties. self._chrome_root = chrome_root self._source_files = source_files @@ -808,7 +839,7 @@ class FeatureCompiler(object): f_json = json_parse.Parse(f.read()) except: print('FAILED: Exception encountered while loading "%s"' % - abs_source_file) + abs_source_file) raise dupes = set(f_json) & set(self._json) assert not dupes, 'Duplicate keys found: %s' % list(dupes) @@ -822,8 +853,8 @@ class FeatureCompiler(object): no_parent_values = ['noparent' in v for v in feature_value] no_parent = all(no_parent_values) assert no_parent or not any(no_parent_values), ( - '"%s:" All child features must contain the same noparent value' % - feature_name) + '"%s:" All child features must contain the same noparent value' % + feature_name) else: no_parent = 'noparent' in feature_value sep = feature_name.rfind('.') @@ -889,8 +920,9 @@ class FeatureCompiler(object): parse_and_validate(feature_name, v, parent, shared_values)) self._features[feature_name] = feature else: - self._features[feature_name] = parse_and_validate( - feature_name, feature_value, parent, shared_values) + self._features[feature_name] = parse_and_validate(feature_name, + feature_value, parent, + shared_values) # Apply parent shared values at the end to enable child features to # override parent shared value - if parent shared values are added to @@ -926,7 +958,8 @@ class FeatureCompiler(object): required_buildflags = feature.GetValue('required_buildflags') if required_buildflags: formatted_buildflags = [ - 'BUILDFLAG(%s)' % format(flag.upper()) for flag in required_buildflags + 'BUILDFLAG(%s)' % format(flag.upper()) + for flag in required_buildflags ] c.Append('#if %s' % format(' && '.join(formatted_buildflags))) c.Concat(feature.GetCode(self._feature_type)) @@ -942,16 +975,20 @@ class FeatureCompiler(object): header_file = self._out_base_filename + '.h' cc_file = self._out_base_filename + '.cc' - include_file_root = self._out_root[len(self._gen_dir_relpath)+1:] + include_file_root = self._out_root[len(self._gen_dir_relpath) + 1:] header_file_path = '%s/%s' % (include_file_root, header_file) cc_file_path = '%s/%s' % (include_file_root, cc_file) substitutions = ({ - 'header_file_path': header_file_path, - 'header_guard': (header_file_path.replace('/', '_'). - replace('.', '_').upper()), - 'method_name': self._method_name, - 'source_files': str([ToPosixPath(f) for f in self._source_files]), - 'year': str(datetime.now().year) + 'header_file_path': + header_file_path, + 'header_guard': + (header_file_path.replace('/', '_').replace('.', '_').upper()), + 'method_name': + self._method_name, + 'source_files': + str([ToPosixPath(f) for f in self._source_files]), + 'year': + str(datetime.now().year) }) if not os.path.exists(self._out_root): os.makedirs(self._out_root) @@ -973,25 +1010,36 @@ class FeatureCompiler(object): cc_file.Concat(cc_end) f.write(cc_file.Render().strip()) + if __name__ == '__main__': parser = argparse.ArgumentParser(description='Compile json feature files') - parser.add_argument('chrome_root', type=str, + parser.add_argument('chrome_root', + type=str, help='The root directory of the chrome checkout') parser.add_argument( - 'feature_type', type=str, + 'feature_type', + type=str, help='The name of the class to use in feature generation ' + - '(e.g. APIFeature, PermissionFeature)') - parser.add_argument('method_name', type=str, + '(e.g. APIFeature, PermissionFeature)') + parser.add_argument('method_name', + type=str, help='The name of the method to populate the provider') - parser.add_argument('out_root', type=str, + parser.add_argument('out_root', + type=str, help='The root directory to generate the C++ files into') - parser.add_argument('gen_dir_relpath', default='gen', help='Path of the ' + parser.add_argument( + 'gen_dir_relpath', + default='gen', + help='Path of the ' 'gen directory relative to the out/. If running in the default ' 'toolchain, the path is gen, otherwise $toolchain_name/gen') parser.add_argument( - 'out_base_filename', type=str, + 'out_base_filename', + type=str, help='The base filename for the C++ files (.h and .cc will be appended)') - parser.add_argument('source_files', type=str, nargs='+', + parser.add_argument('source_files', + type=str, + nargs='+', help='The source features.json files') args = parser.parse_args() if args.feature_type not in FEATURE_TYPES: diff --git a/tools/json_schema_compiler/feature_compiler_test.py b/tools/json_schema_compiler/feature_compiler_test.py index 019f107535e77..c86e7fc39eb3f 100755 --- a/tools/json_schema_compiler/feature_compiler_test.py +++ b/tools/json_schema_compiler/feature_compiler_test.py @@ -7,6 +7,7 @@ import copy import feature_compiler import unittest + class FeatureCompilerTest(unittest.TestCase): """Test the FeatureCompiler. Note that we test that the expected features are generated more thoroughly in features_generation_unittest.cc. And, of course, @@ -14,6 +15,7 @@ class FeatureCompilerTest(unittest.TestCase): feature fails to parse, the compile fails). These tests primarily focus on catching errors during parsing. """ + def _parseFeature(self, value): """Parses a feature from the given value and returns the result.""" f = feature_compiler.Feature('alpha') @@ -22,7 +24,8 @@ class FeatureCompilerTest(unittest.TestCase): def _createTestFeatureCompiler(self, feature_class): return feature_compiler.FeatureCompiler('chrome_root', [], feature_class, - 'provider_class', 'out_root', 'gen', 'out_base_filename') + 'provider_class', 'out_root', 'gen', + 'out_base_filename') def _hasError(self, f, error): """Asserts that |error| is present somewhere in the given feature's @@ -37,60 +40,69 @@ class FeatureCompilerTest(unittest.TestCase): def testFeature(self): # Test some basic feature parsing for a sanity check. f = self._parseFeature({ - 'blocklist': [ - 'ABCDEF0123456789ABCDEF0123456789ABCDEF01', - '10FEDCBA9876543210FEDCBA9876543210FEDCBA' - ], - 'channel': 'stable', - 'command_line_switch': 'switch', - 'component_extensions_auto_granted': False, - 'contexts': [ - 'privileged_extension', - 'privileged_web_page', - 'lock_screen_extension' - ], - 'default_parent': True, - 'dependencies': ['dependency1', 'dependency2'], - 'developer_mode_only': True, - 'disallow_for_service_workers': True, - 'extension_types': ['extension'], - 'location': 'component', - 'internal': True, - 'matches': ['*://*/*'], - 'max_manifest_version': 1, - 'requires_delegated_availability_check': True, - 'noparent': True, - 'platforms': ['mac', 'win'], - 'session_types': ['kiosk', 'regular'], - 'allowlist': [ - '0123456789ABCDEF0123456789ABCDEF01234567', - '76543210FEDCBA9876543210FEDCBA9876543210' - ], - 'required_buildflags': [ - 'use_cups' - ] + 'blocklist': [ + 'ABCDEF0123456789ABCDEF0123456789ABCDEF01', + '10FEDCBA9876543210FEDCBA9876543210FEDCBA' + ], + 'channel': + 'stable', + 'command_line_switch': + 'switch', + 'component_extensions_auto_granted': + False, + 'contexts': [ + 'privileged_extension', 'privileged_web_page', + 'lock_screen_extension' + ], + 'default_parent': + True, + 'dependencies': ['dependency1', 'dependency2'], + 'developer_mode_only': + True, + 'disallow_for_service_workers': + True, + 'extension_types': ['extension'], + 'location': + 'component', + 'internal': + True, + 'matches': ['*://*/*'], + 'max_manifest_version': + 1, + 'requires_delegated_availability_check': + True, + 'noparent': + True, + 'platforms': ['mac', 'win'], + 'session_types': ['kiosk', 'regular'], + 'allowlist': [ + '0123456789ABCDEF0123456789ABCDEF01234567', + '76543210FEDCBA9876543210FEDCBA9876543210' + ], + 'required_buildflags': ['use_cups'] }) self.assertFalse(f.GetErrors()) def testInvalidAll(self): f = self._parseFeature({ - 'channel': 'stable', - 'dependencies': 'all', + 'channel': 'stable', + 'dependencies': 'all', }) self._hasError(f, 'Illegal value: "all"') def testUnknownKeyError(self): f = self._parseFeature({ - 'contexts': ['privileged_extension'], - 'channel': 'stable', - 'unknownkey': 'unknownvalue' + 'contexts': ['privileged_extension'], + 'channel': 'stable', + 'unknownkey': 'unknownvalue' }) self._hasError(f, 'Unrecognized key') def testUnknownEnumValue(self): f = self._parseFeature({ - 'contexts': ['privileged_extension', 'unknown_context'], - 'channel': 'stable' + 'contexts': ['privileged_extension', 'unknown_context'], + 'channel': + 'stable' }) self._hasError(f, 'Illegal value: "unknown_context"') @@ -116,15 +128,19 @@ class FeatureCompilerTest(unittest.TestCase): self.assertFalse(f.GetErrors()) def testApiFeaturesNeedContexts(self): - f = self._parseFeature({'extension_types': ['extension'], - 'channel': 'trunk'}) + f = self._parseFeature({ + 'extension_types': ['extension'], + 'channel': 'trunk' + }) f.Validate('APIFeature', {}) self._hasError(f, 'APIFeatures must specify the contexts property') def testAPIFeaturesCanSpecifyEmptyContexts(self): - f = self._parseFeature({'extension_types': ['extension'], - 'channel': 'trunk', - 'contexts': []}) + f = self._parseFeature({ + 'extension_types': ['extension'], + 'channel': 'trunk', + 'contexts': [] + }) f.Validate('APIFeature', {}) self.assertFalse(f.GetErrors()) @@ -135,10 +151,12 @@ class FeatureCompilerTest(unittest.TestCase): 'ManifestFeatures must specify at least one extension type') def testManifestFeaturesCantHaveContexts(self): - f = self._parseFeature({'dependencies': 'alpha', - 'channel': 'beta', - 'extension_types': ['extension'], - 'contexts': ['privileged_extension']}) + f = self._parseFeature({ + 'dependencies': 'alpha', + 'channel': 'beta', + 'extension_types': ['extension'], + 'contexts': ['privileged_extension'] + }) f.Validate('ManifestFeature', {}) self._hasError(f, 'ManifestFeatures do not support contexts') @@ -149,18 +167,20 @@ class FeatureCompilerTest(unittest.TestCase): f, 'PermissionFeatures must specify at least one extension type') def testPermissionFeaturesCantHaveContexts(self): - f = self._parseFeature({'dependencies': 'alpha', - 'channel': 'beta', - 'extension_types': ['extension'], - 'contexts': ['privileged_extension']}) + f = self._parseFeature({ + 'dependencies': 'alpha', + 'channel': 'beta', + 'extension_types': ['extension'], + 'contexts': ['privileged_extension'] + }) f.Validate('PermissionFeature', {}) self._hasError(f, 'PermissionFeatures do not support contexts') def testAllPermissionsNeedChannelOrDependencies(self): api_feature = self._parseFeature({'contexts': ['privileged_extension']}) api_feature.Validate('APIFeature', {}) - self._hasError( - api_feature, 'Features must specify either a channel or dependencies') + self._hasError(api_feature, + 'Features must specify either a channel or dependencies') permission_feature = self._parseFeature({'extension_types': ['extension']}) permission_feature.Validate('PermissionFeature', {}) self._hasError(permission_feature, @@ -169,25 +189,28 @@ class FeatureCompilerTest(unittest.TestCase): manifest_feature.Validate('ManifestFeature', {}) self._hasError(manifest_feature, 'Features must specify either a channel or dependencies') - channel_feature = self._parseFeature({'contexts': ['privileged_extension'], - 'channel': 'trunk'}) + channel_feature = self._parseFeature({ + 'contexts': ['privileged_extension'], + 'channel': 'trunk' + }) channel_feature.Validate('APIFeature', {}) self.assertFalse(channel_feature.GetErrors()) - dependency_feature = self._parseFeature( - {'contexts': ['privileged_extension'], - 'dependencies': ['alpha']}) + dependency_feature = self._parseFeature({ + 'contexts': ['privileged_extension'], + 'dependencies': ['alpha'] + }) dependency_feature.Validate('APIFeature', {}) self.assertFalse(dependency_feature.GetErrors()) def testBothAliasAndSource(self): compiler = self._createTestFeatureCompiler('APIFeature') compiler._json = { - 'feature_alpha': { - 'channel': 'beta', - 'contexts': ['privileged_extension'], - 'alias': 'feature_alpha', - 'source': 'feature_alpha' - } + 'feature_alpha': { + 'channel': 'beta', + 'contexts': ['privileged_extension'], + 'alias': 'feature_alpha', + 'source': 'feature_alpha' + } } compiler.Compile() @@ -198,20 +221,20 @@ class FeatureCompilerTest(unittest.TestCase): def testAliasOnNonApiFeature(self): compiler = self._createTestFeatureCompiler('PermissionFeature') compiler._json = { - 'feature_alpha': { - 'channel': 'beta', - 'contexts': ['privileged_extension'], - 'alias': 'feature_beta' - }, - 'feature_beta': [{ - 'channel': 'beta', - 'contexts': ['privileged_extension'], - 'source': 'feature_alpha' - },{ - 'channel': 'dev', - 'context': ['privileged_extension'] - }] - }; + 'feature_alpha': { + 'channel': 'beta', + 'contexts': ['privileged_extension'], + 'alias': 'feature_beta' + }, + 'feature_beta': [{ + 'channel': 'beta', + 'contexts': ['privileged_extension'], + 'source': 'feature_alpha' + }, { + 'channel': 'dev', + 'context': ['privileged_extension'] + }] + } compiler.Compile() feature = compiler._features.get('feature_alpha') @@ -225,17 +248,17 @@ class FeatureCompilerTest(unittest.TestCase): def testAliasFeature(self): compiler = self._createTestFeatureCompiler('APIFeature') compiler._json = { - 'feature_alpha': { - 'channel': 'beta', - 'contexts': ['privileged_extension'], - 'alias': 'feature_beta' - }, - 'feature_beta': { - 'channel': 'beta', - 'contexts': ['privileged_extension'], - 'source': 'feature_alpha' - } - }; + 'feature_alpha': { + 'channel': 'beta', + 'contexts': ['privileged_extension'], + 'alias': 'feature_beta' + }, + 'feature_beta': { + 'channel': 'beta', + 'contexts': ['privileged_extension'], + 'source': 'feature_alpha' + } + } compiler.Compile() feature = compiler._features.get('feature_alpha') @@ -249,40 +272,41 @@ class FeatureCompilerTest(unittest.TestCase): def testMultipleAliasesInComplexFeature(self): compiler = self._createTestFeatureCompiler('APIFeature') compiler._json = { - 'feature_alpha': [{ - 'channel': 'beta', - 'contexts': ['privileged_extension'], - 'alias': 'feature_beta' - }, { - 'contexts': ['privileged_extension'], - 'channel': 'beta', - 'alias': 'feature_beta' - }] - }; + 'feature_alpha': [{ + 'channel': 'beta', + 'contexts': ['privileged_extension'], + 'alias': 'feature_beta' + }, { + 'contexts': ['privileged_extension'], + 'channel': 'beta', + 'alias': 'feature_beta' + }] + } compiler.Compile() feature = compiler._features.get('feature_alpha') self.assertTrue(feature) - self._hasError(feature, 'Error parsing feature "feature_alpha" at key ' + - '"alias": Key can be set at most once per feature.') + self._hasError( + feature, 'Error parsing feature "feature_alpha" at key ' + + '"alias": Key can be set at most once per feature.') def testAliasReferenceInComplexFeature(self): compiler = self._createTestFeatureCompiler('APIFeature') compiler._json = { - 'feature_alpha': [{ - 'channel': 'beta', - 'contexts': ['privileged_extension'], - 'alias': 'feature_beta' - }, { - 'contexts': ['privileged_extension'], - 'channel': 'beta', - }], - 'feature_beta': { - 'channel': 'beta', - 'contexts': ['privileged_extension'], - 'source': 'feature_alpha' - } - }; + 'feature_alpha': [{ + 'channel': 'beta', + 'contexts': ['privileged_extension'], + 'alias': 'feature_beta' + }, { + 'contexts': ['privileged_extension'], + 'channel': 'beta', + }], + 'feature_beta': { + 'channel': 'beta', + 'contexts': ['privileged_extension'], + 'source': 'feature_alpha' + } + } compiler.Compile() feature = compiler._features.get('feature_alpha') @@ -296,57 +320,58 @@ class FeatureCompilerTest(unittest.TestCase): def testSourceMissingReference(self): compiler = self._createTestFeatureCompiler('APIFeature') compiler._json = { - 'feature_alpha': { - 'channel': 'beta', - 'contexts': ['privileged_extension'], - 'alias': 'feature_beta' - }, - 'feature_beta': { - 'contexts': ['privileged_extension'], - 'channel': 'beta', - 'source': 'does_not_exist' - } - }; + 'feature_alpha': { + 'channel': 'beta', + 'contexts': ['privileged_extension'], + 'alias': 'feature_beta' + }, + 'feature_beta': { + 'contexts': ['privileged_extension'], + 'channel': 'beta', + 'source': 'does_not_exist' + } + } compiler.Compile() feature = compiler._features.get('feature_beta') self.assertTrue(feature) - self._hasError(feature, 'A feature source property should reference a ' + - 'feature whose alias property references it back.') - + self._hasError( + feature, 'A feature source property should reference a ' + + 'feature whose alias property references it back.') def testAliasMissingReferenceInComplexFeature(self): compiler = self._createTestFeatureCompiler('APIFeature') compiler._json = { - 'feature_alpha': [{ - 'channel': 'beta', - 'contexts': ['privileged_extension'], - 'alias': 'feature_beta' - }, { - 'contexts': ['privileged_extension'], - 'channel': 'beta' - }] - }; + 'feature_alpha': [{ + 'channel': 'beta', + 'contexts': ['privileged_extension'], + 'alias': 'feature_beta' + }, { + 'contexts': ['privileged_extension'], + 'channel': 'beta' + }] + } compiler.Compile() feature = compiler._features.get('feature_alpha') self.assertTrue(feature) - self._hasError(feature, 'A feature alias property should reference a ' + - 'feature whose source property references it back.') + self._hasError( + feature, 'A feature alias property should reference a ' + + 'feature whose source property references it back.') def testAliasReferenceMissingSourceInComplexFeature(self): compiler = self._createTestFeatureCompiler('APIFeature') compiler._json = { - 'feature_alpha': { - 'contexts': ['privileged_extension'], - 'channel': 'beta', - }, - 'feature_beta': { - 'channel': 'beta', - 'contexts': ['privileged_extension'], - 'alias': 'feature_alpha' - } - }; + 'feature_alpha': { + 'contexts': ['privileged_extension'], + 'channel': 'beta', + }, + 'feature_beta': { + 'channel': 'beta', + 'contexts': ['privileged_extension'], + 'alias': 'feature_alpha' + } + } compiler.Compile() feature = compiler._features.get('feature_alpha') @@ -355,23 +380,23 @@ class FeatureCompilerTest(unittest.TestCase): feature = compiler._features.get('feature_beta') self.assertTrue(feature) - self._hasError(feature, 'A feature alias property should reference a ' + - 'feature whose source property references it back.') + self._hasError( + feature, 'A feature alias property should reference a ' + + 'feature whose source property references it back.') def testComplexParentWithoutDefaultParent(self): - c = feature_compiler.FeatureCompiler( - None, None, 'APIFeature', None, None, None, None) - c._CompileFeature('bookmarks', - [{ - 'contexts': ['privileged_extension'], - }, { - 'channel': 'stable', - 'contexts': ['webui'], - }]) + c = feature_compiler.FeatureCompiler(None, None, 'APIFeature', None, None, + None, None) + c._CompileFeature('bookmarks', [{ + 'contexts': ['privileged_extension'], + }, { + 'channel': 'stable', + 'contexts': ['webui'], + }]) with self.assertRaisesRegex(AssertionError, 'No default parent found for bookmarks'): - c._CompileFeature('bookmarks.export', { "allowlist": ["asdf"] }) + c._CompileFeature('bookmarks.export', {"allowlist": ["asdf"]}) def testComplexFeatureWithSinglePropertyBlock(self): compiler = self._createTestFeatureCompiler('APIFeature') @@ -383,16 +408,18 @@ class FeatureCompilerTest(unittest.TestCase): 'feature key instead of a list.') with self.assertRaisesRegex(AssertionError, error): compiler._CompileFeature('feature_alpha', - [{ - 'contexts': ['privileged_extension'], - 'channel': 'stable', - }]) + [{ + 'contexts': ['privileged_extension'], + 'channel': 'stable', + }]) def testRealIdsDisallowedInAllowlist(self): - fake_id = 'a' * 32; - f = self._parseFeature({'allowlist': [fake_id], - 'extension_types': ['extension'], - 'channel': 'beta'}) + fake_id = 'a' * 32 + f = self._parseFeature({ + 'allowlist': [fake_id], + 'extension_types': ['extension'], + 'channel': 'beta' + }) f.Validate('PermissionFeature', {}) self._hasError( f, 'list should only have hex-encoded SHA1 hashes of extension ids') @@ -401,31 +428,34 @@ class FeatureCompilerTest(unittest.TestCase): f = self._parseFeature({ 'extension_types': ['extension', 'hosted_app'], 'allowlist': ['0123456789ABCDEF0123456789ABCDEF01234567'], - 'channel': 'beta', + 'channel': + 'beta', }) f.Validate('PermissionFeature', {}) self._hasError(f, 'Hosted apps are not allowed to use restricted features') def testHostedAppsCantUseAllowlistedFeatures_ComplexFeature(self): - c = feature_compiler.FeatureCompiler( - None, None, 'PermissionFeature', None, None, None, None) - c._CompileFeature('invalid_feature', + c = feature_compiler.FeatureCompiler(None, None, 'PermissionFeature', None, + None, None, None) + c._CompileFeature( + 'invalid_feature', [{ - 'extension_types': ['extension'], - 'channel': 'beta', + 'extension_types': ['extension'], + 'channel': 'beta', }, { - 'channel': 'beta', - 'extension_types': ['hosted_app'], - 'allowlist': ['0123456789ABCDEF0123456789ABCDEF01234567'], + 'channel': 'beta', + 'extension_types': ['hosted_app'], + 'allowlist': ['0123456789ABCDEF0123456789ABCDEF01234567'], }]) - c._CompileFeature('valid_feature', + c._CompileFeature( + 'valid_feature', [{ - 'extension_types': ['extension'], - 'channel': 'beta', - 'allowlist': ['0123456789ABCDEF0123456789ABCDEF01234567'], + 'extension_types': ['extension'], + 'channel': 'beta', + 'allowlist': ['0123456789ABCDEF0123456789ABCDEF01234567'], }, { - 'channel': 'beta', - 'extension_types': ['hosted_app'], + 'channel': 'beta', + 'extension_types': ['hosted_app'], }]) valid_feature = c._features.get('valid_feature') @@ -437,20 +467,17 @@ class FeatureCompilerTest(unittest.TestCase): self._hasError(invalid_feature, 'Hosted apps are not allowed to use restricted features') - def testHostedAppsCantUseAllowlistedFeatures_ChildFeature(self): - c = feature_compiler.FeatureCompiler( - None, None, 'PermissionFeature', None, None, None, None) - c._CompileFeature('parent', - { - 'extension_types': ['hosted_app'], - 'channel': 'beta', - }) + c = feature_compiler.FeatureCompiler(None, None, 'PermissionFeature', None, + None, None, None) + c._CompileFeature('parent', { + 'extension_types': ['hosted_app'], + 'channel': 'beta', + }) - c._CompileFeature('parent.child', - { - 'allowlist': ['0123456789ABCDEF0123456789ABCDEF01234567'] - }) + c._CompileFeature( + 'parent.child', + {'allowlist': ['0123456789ABCDEF0123456789ABCDEF01234567']}) feature = c._features.get('parent.child') self.assertTrue(feature) self._hasError(feature, @@ -459,27 +486,27 @@ class FeatureCompilerTest(unittest.TestCase): def testEmptyContextsDisallowed(self): compiler = self._createTestFeatureCompiler('APIFeature') compiler._json = { - 'feature_alpha': { - 'channel': 'beta', - 'contexts': [], - 'extension_types': ['extension'] - } + 'feature_alpha': { + 'channel': 'beta', + 'contexts': [], + 'extension_types': ['extension'] + } } compiler.Compile() feature = compiler._features.get('feature_alpha') self.assertTrue(feature) self._hasError(feature, - 'An empty contexts list is not allowed for this feature.') + 'An empty contexts list is not allowed for this feature.') def testEmptyContextsAllowed(self): compiler = self._createTestFeatureCompiler('APIFeature') compiler._json = { - 'empty_contexts': { - 'channel': 'beta', - 'contexts': [], - 'extension_types': ['extension'] - } + 'empty_contexts': { + 'channel': 'beta', + 'contexts': [], + 'extension_types': ['extension'] + } } compiler.Compile() @@ -491,18 +518,19 @@ class FeatureCompilerTest(unittest.TestCase): compiler = self._createTestFeatureCompiler('APIFeature') compiler._json = { - 'feature_cups': { - 'channel': 'beta', - 'contexts': ['privileged_extension'], - 'extension_types': ['extension'], - 'required_buildflags': ['use_cups'] - } + 'feature_cups': { + 'channel': 'beta', + 'contexts': ['privileged_extension'], + 'extension_types': ['extension'], + 'required_buildflags': ['use_cups'] + } } compiler.Compile() cc_code = compiler.Render() # The code below is formatted correctly! - self.assertEqual(cc_code.Render(), ''' { + self.assertEqual( + cc_code.Render(), ''' { #if BUILDFLAG(USE_CUPS) SimpleFeature* feature = new SimpleFeature(); feature->set_name("feature_cups"); @@ -513,5 +541,6 @@ class FeatureCompilerTest(unittest.TestCase): #endif }''') + if __name__ == '__main__': unittest.main() diff --git a/tools/json_schema_compiler/features_cc_generator.py b/tools/json_schema_compiler/features_cc_generator.py index f3e6a657a1ba7..5934a7d6a50ee 100644 --- a/tools/json_schema_compiler/features_cc_generator.py +++ b/tools/json_schema_compiler/features_cc_generator.py @@ -9,6 +9,7 @@ import cpp_util class CCGenerator(object): + def Generate(self, feature_defs, source_file, namespace): return _Generator(feature_defs, source_file, namespace).Generate() @@ -16,6 +17,7 @@ class CCGenerator(object): class _Generator(object): """A .cc generator for features. """ + def __init__(self, feature_defs, source_file, namespace): self._feature_defs = feature_defs self._source_file = source_file @@ -27,54 +29,53 @@ class _Generator(object): """Generates a Code object for features. """ c = Code() - (c.Append(cpp_util.CHROMIUM_LICENSE) - .Append() + (c.Append(cpp_util.CHROMIUM_LICENSE) \ + .Append() \ .Append(cpp_util.GENERATED_FEATURE_MESSAGE % - cpp_util.ToPosixPath(self._source_file)) - .Append() - .Append('#include <string>') - .Append() + cpp_util.ToPosixPath(self._source_file)) \ + .Append() \ + .Append('#include <string>') \ + .Append() \ .Append('#include "%s.h"' % - cpp_util.ToPosixPath(self._source_file_filename)) - .Append() - .Append('#include "base/notreached.h"') - .Append() - .Concat(cpp_util.OpenNamespace(self._namespace)) + cpp_util.ToPosixPath(self._source_file_filename)) \ + .Append() \ + .Append('#include "base/notreached.h"') \ + .Append() \ + .Concat(cpp_util.OpenNamespace(self._namespace)) \ .Append() ) # Generate the constructor. - (c.Append('%s::%s() {' % (self._class_name, self._class_name)) + (c.Append('%s::%s() {' % (self._class_name, self._class_name)) \ .Sblock() ) for feature in self._feature_defs: - c.Append('features_["%s"] = %s;' - % (feature.name, - cpp_util.FeatureNameToConstantName(feature.name))) - (c.Eblock() - .Append('}') + c.Append('features_["%s"] = %s;' % + (feature.name, cpp_util.FeatureNameToConstantName(feature.name))) + (c.Eblock() \ + .Append('}') \ .Append() ) # Generate the ToString function. (c.Append('const char* %s::ToString(' - '%s::ID id) const {' % (self._class_name, self._class_name)) - .Sblock() - .Append('switch (id) {') + '%s::ID id) const {' % (self._class_name, self._class_name)) \ + .Sblock() \ + .Append('switch (id) {') \ .Sblock() ) for feature in self._feature_defs: c.Append('case %s: return "%s";' % - (cpp_util.FeatureNameToConstantName(feature.name), feature.name)) - (c.Append('case kUnknown: break;') - .Append('case kEnumBoundary: break;') - .Eblock() - .Append('}') - .Append('NOTREACHED_IN_MIGRATION();') + (cpp_util.FeatureNameToConstantName(feature.name), feature.name)) + (c.Append('case kUnknown: break;') \ + .Append('case kEnumBoundary: break;') \ + .Eblock() \ + .Append('}') \ + .Append('NOTREACHED_IN_MIGRATION();') \ .Append('return "";') ) - (c.Eblock() - .Append('}') + (c.Eblock() \ + .Append('}') \ .Append() ) @@ -82,13 +83,13 @@ class _Generator(object): (c.Append('%s::ID %s::FromString(' 'const std::string& id) const {' - % (self._class_name, self._class_name)) - .Sblock() - .Append('const auto& it = features_.find(id);' % self._class_name) - .Append('return (it == features_.end()) ? kUnknown : it->second;') - .Eblock() - .Append('}') - .Append() + % (self._class_name, self._class_name)) \ + .Sblock() \ + .Append('const auto& it = features_.find(id);' % self._class_name) \ + .Append('return (it == features_.end()) ? kUnknown : it->second;') \ + .Eblock() \ + .Append('}') \ + .Append() \ .Cblock(cpp_util.CloseNamespace(self._namespace)) ) diff --git a/tools/json_schema_compiler/features_compiler.py b/tools/json_schema_compiler/features_compiler.py index 7441a387180d9..611adbcc8f70c 100755 --- a/tools/json_schema_compiler/features_compiler.py +++ b/tools/json_schema_compiler/features_compiler.py @@ -23,9 +23,7 @@ def _GenerateSchema(filename, root, destdir, namespace): # Load in the feature permissions from the JSON file. schema = os.path.normpath(filename) schema_loader = SchemaLoader(os.path.dirname(os.path.relpath(schema, root)), - os.path.dirname(schema), - [], - None) + os.path.dirname(schema), [], None) schema_filename = os.path.splitext(schema)[0] feature_defs = schema_loader.LoadSchema(schema) @@ -38,10 +36,8 @@ def _GenerateSchema(filename, root, destdir, namespace): relpath = os.path.relpath(os.path.normpath(source_file_dir), root) full_path = os.path.join(relpath, schema) - generators = [ - ('%s.cc' % schema_filename, CCGenerator()), - ('%s.h' % schema_filename, HGenerator()) - ] + generators = [('%s.cc' % schema_filename, CCGenerator()), + ('%s.h' % schema_filename, HGenerator())] # Generate and output the code for all features. output_code = [] @@ -59,12 +55,19 @@ if __name__ == '__main__': parser = optparse.OptionParser( description='Generates a C++ features model from JSON schema', usage='usage: %prog [option]... schema') - parser.add_option('-r', '--root', default='.', + parser.add_option( + '-r', + '--root', + default='.', help='logical include root directory. Path to schema files from ' - 'specified dir will be the include path.') - parser.add_option('-d', '--destdir', - help='root directory to output generated files.') - parser.add_option('-n', '--namespace', default='generated_features', + 'specified dir will be the include path.') + parser.add_option('-d', + '--destdir', + help='root directory to output generated files.') + parser.add_option( + '-n', + '--namespace', + default='generated_features', help='C++ namespace for generated files. e.g extensions::api.') (opts, filenames) = parser.parse_args() diff --git a/tools/json_schema_compiler/features_h_generator.py b/tools/json_schema_compiler/features_h_generator.py index 250c878600333..e5ad95cbddddc 100644 --- a/tools/json_schema_compiler/features_h_generator.py +++ b/tools/json_schema_compiler/features_h_generator.py @@ -9,6 +9,7 @@ import cpp_util class HGenerator(object): + def Generate(self, features, source_file, namespace): return _Generator(features, source_file, namespace).Generate() @@ -16,6 +17,7 @@ class HGenerator(object): class _Generator(object): """A .cc generator for features. """ + def __init__(self, features, source_file, namespace): self._feature_defs = features self._source_file = source_file @@ -27,10 +29,10 @@ class _Generator(object): """Generates a Code object for features. """ c = Code() - (c.Append(cpp_util.CHROMIUM_LICENSE) - .Append() + (c.Append(cpp_util.CHROMIUM_LICENSE) \ + .Append() \ .Append(cpp_util.GENERATED_FEATURE_MESSAGE % - cpp_util.ToPosixPath(self._source_file)) + cpp_util.ToPosixPath(self._source_file)) \ .Append() ) @@ -39,31 +41,31 @@ class _Generator(object): output_file = os.path.splitext(self._namespace.source_file)[0] + '.h' ifndef_name = cpp_util.GenerateIfndefName(output_file) - (c.Append('#ifndef %s' % ifndef_name) - .Append('#define %s' % ifndef_name) + (c.Append('#ifndef %s' % ifndef_name) \ + .Append('#define %s' % ifndef_name) \ .Append() ) - (c.Append('#include <map>') - .Append('#include <string>') - .Append() - .Concat(cpp_util.OpenNamespace(self._namespace)) + (c.Append('#include <map>') \ + .Append('#include <string>') \ + .Append() \ + .Concat(cpp_util.OpenNamespace(self._namespace)) \ .Append() ) - (c.Append('class %s {' % self._class_name) - .Append(' public:') - .Sblock() - .Concat(self._GeneratePublicBody()) - .Eblock() - .Append(' private:') - .Sblock() - .Concat(self._GeneratePrivateBody()) - .Eblock('};') - .Append() + (c.Append('class %s {' % self._class_name) \ + .Append(' public:') \ + .Sblock() \ + .Concat(self._GeneratePublicBody()) \ + .Eblock() \ + .Append(' private:') \ + .Sblock() \ + .Concat(self._GeneratePrivateBody()) \ + .Eblock('};') \ + .Append() \ .Cblock(cpp_util.CloseNamespace(self._namespace)) ) - (c.Append('#endif // %s' % ifndef_name) + (c.Append('#endif // %s' % ifndef_name) \ .Append() ) return c @@ -71,14 +73,14 @@ class _Generator(object): def _GeneratePublicBody(self): c = Code() - (c.Append('%s();' % self._class_name) - .Append() - .Append('enum ID {') - .Concat(self._GenerateEnumConstants()) - .Eblock('};') - .Append() - .Append('const char* ToString(ID id) const;') - .Append('ID FromString(const std::string& id) const;') + (c.Append('%s();' % self._class_name) \ + .Append() \ + .Append('enum ID {') \ + .Concat(self._GenerateEnumConstants()) \ + .Eblock('};') \ + .Append() \ + .Append('const char* ToString(ID id) const;') \ + .Append('ID FromString(const std::string& id) const;') \ .Append() ) return c @@ -90,7 +92,7 @@ class _Generator(object): def _GenerateEnumConstants(self): c = Code() - (c.Sblock() + (c.Sblock() \ .Append('kUnknown,') ) for feature in self._feature_defs: diff --git a/tools/json_schema_compiler/generate_all_externs.py b/tools/json_schema_compiler/generate_all_externs.py index a910f9eeceb21..ed97e99182654 100755 --- a/tools/json_schema_compiler/generate_all_externs.py +++ b/tools/json_schema_compiler/generate_all_externs.py @@ -2,7 +2,6 @@ # Copyright 2022 The Chromium Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. - """Helper for quickly generating all known JS externs.""" import argparse @@ -31,6 +30,7 @@ REPO_ROOT = os.path.dirname(os.path.dirname(DIR)) # Import the helper module. sys.path.insert(0, os.path.join(REPO_ROOT, 'extensions', 'common', 'api')) from externs_checker import ExternsChecker + sys.path.pop(0) @@ -72,6 +72,7 @@ class FakeOutputApi: """ class PresubmitResult: + def __init__(self, msg, long_text=None): self.msg = msg self.long_text = long_text @@ -112,7 +113,7 @@ def Generate(input_api, output_api, force=False, dryrun=False): externs_relpath = input_api.os_path.relpath(externs, src_root) print('\r' + ' ' * msg_len, end='\r') - msg = 'Checking %s ...' % (source_relpath,) + msg = 'Checking %s ...' % (source_relpath, ) msg_len = len(msg) print(msg, end='') sys.stdout.flush() @@ -123,9 +124,9 @@ def Generate(input_api, output_api, force=False, dryrun=False): if not dryrun: print('\n%s: %s' % (source_relpath, e)) ret.append( - output_api.PresubmitResult( - '%s: unable to generate' % (source_relpath,), - long_text=str(e))) + output_api.PresubmitResult('%s: unable to generate' % + (source_relpath, ), + long_text=str(e))) continue # Ignore the first line (copyright) to avoid yearly thrashing. @@ -148,7 +149,7 @@ def Generate(input_api, output_api, force=False, dryrun=False): if not dryrun: print('\r' + ' ' * msg_len, end='\r') msg_len = 0 - print('Updating %s' % (externs_relpath,)) + print('Updating %s' % (externs_relpath, )) with open(externs, 'w', encoding='utf-8') as fp: fp.write(copyright + '\n') fp.write(new_data) @@ -161,11 +162,16 @@ def Generate(input_api, output_api, force=False, dryrun=False): def get_parser(): """Get CLI parser.""" parser = argparse.ArgumentParser(description=__doc__) - parser.add_argument('-n', '--dry-run', dest='dryrun', action='store_true', + parser.add_argument('-n', + '--dry-run', + dest='dryrun', + action='store_true', help="Don't make changes; only show changed files") - parser.add_argument('-f', '--force', action='store_true', + parser.add_argument('-f', + '--force', + action='store_true', help='Regenerate files even if they have a TODO ' - 'disabling generation') + 'disabling generation') return parser @@ -174,7 +180,9 @@ def main(argv): parser = get_parser() opts = parser.parse_args(argv) - results = Generate(FakeInputApi(), FakeOutputApi(), force=opts.force, + results = Generate(FakeInputApi(), + FakeOutputApi(), + force=opts.force, dryrun=opts.dryrun) if opts.dryrun and results: for result in results: diff --git a/tools/json_schema_compiler/h_generator.py b/tools/json_schema_compiler/h_generator.py index a427b4f702c39..e101d8a3ff532 100644 --- a/tools/json_schema_compiler/h_generator.py +++ b/tools/json_schema_compiler/h_generator.py @@ -9,7 +9,9 @@ from model import PropertyType, Type, Property import cpp_util import schema_util + class HGenerator(object): + def __init__(self, type_generator): self._type_generator = type_generator @@ -20,6 +22,7 @@ class HGenerator(object): class _Generator(object): """A .h generator for a namespace. """ + def __init__(self, namespace, cpp_type_generator): self._namespace = namespace self._type_helper = cpp_type_generator @@ -30,10 +33,10 @@ class _Generator(object): """Generates a Code object with the .h for a single namespace. """ c = Code() - (c.Append(cpp_util.CHROMIUM_LICENSE) - .Append() + (c.Append(cpp_util.CHROMIUM_LICENSE) \ + .Append() \ .Append(cpp_util.GENERATED_FILE_MESSAGE % - cpp_util.ToPosixPath(self._namespace.source_file)) + cpp_util.ToPosixPath(self._namespace.source_file)) \ .Append() ) @@ -48,21 +51,21 @@ class _Generator(object): # non-optional types from other namespaces. include_soft = self._namespace.name not in ('tabs', 'windows') - (c.Append('#ifndef %s' % ifndef_name) - .Append('#define %s' % ifndef_name) - .Append() - .Append('#include <stdint.h>') - .Append() - .Append('#include <map>') - .Append('#include <memory>') - .Append('#include <optional>') - .Append('#include <string>') - .Append('#include <vector>') - .Append() - .Append('#include "base/values.h"') + (c.Append('#ifndef %s' % ifndef_name) \ + .Append('#define %s' % ifndef_name) \ + .Append() \ + .Append('#include <stdint.h>') \ + .Append() \ + .Append('#include <map>') \ + .Append('#include <memory>') \ + .Append('#include <optional>') \ + .Append('#include <string>') \ + .Append('#include <vector>') \ + .Append() \ + .Append('#include "base/values.h"') \ .Cblock(self._type_helper.GenerateIncludes( include_soft=include_soft, - generate_error_messages=self._generate_error_messages)) + generate_error_messages=self._generate_error_messages)) \ .Append() ) @@ -77,22 +80,21 @@ class _Generator(object): c.Concat(cpp_util.OpenNamespace(cpp_namespace)) c.Append() if self._namespace.properties: - (c.Append('//') - .Append('// Properties') - .Append('//') + (c.Append('//') \ + .Append('// Properties') \ + .Append('//') \ .Append() ) for prop in self._namespace.properties.values(): property_code = self._type_helper.GeneratePropertyValues( - prop, - 'extern const %(type)s %(name)s;') + prop, 'extern const %(type)s %(name)s;') if property_code: c.Cblock(property_code) if self._namespace.types: - (c.Append('//') - .Append('// Types') - .Append('//') - .Append() + (c.Append('//') \ + .Append('// Types') \ + .Append('//') \ + .Append() \ .Cblock(self._GenerateTypes(self._FieldDependencyOrder(), is_toplevel=True, generate_typedefs=True)) @@ -104,24 +106,24 @@ class _Generator(object): c.Append() c.Cblock(self._GenerateManifestKeys()) if self._namespace.functions: - (c.Append('//') - .Append('// Functions') - .Append('//') + (c.Append('//') \ + .Append('// Functions') \ + .Append('//') \ .Append() ) for function in self._namespace.functions.values(): c.Cblock(self._GenerateFunction(function)) if self._namespace.events: - (c.Append('//') - .Append('// Events') - .Append('//') + (c.Append('//') \ + .Append('// Events') \ + .Append('//') \ .Append() ) for event in self._namespace.events.values(): c.Cblock(self._GenerateEvent(event)) - (c.Concat(cpp_util.CloseNamespace(cpp_namespace)) - .Append() - .Append('#endif // %s' % ifndef_name) + (c.Concat(cpp_util.CloseNamespace(cpp_namespace)) \ + .Append() \ + .Append('#endif // %s' % ifndef_name) \ .Append() ) return c @@ -137,8 +139,8 @@ class _Generator(object): raise ValueError("Illegal circular dependency via cycle " + ", ".join(map(lambda x: x.name, path + [type_]))) for prop in type_.properties.values(): - if (prop.type_ == PropertyType.REF and - schema_util.GetNamespace(prop.ref_type) == self._namespace.name): + if (prop.type_ == PropertyType.REF and schema_util.GetNamespace( + prop.ref_type) == self._namespace.name): ExpandType(path + [type_], self._namespace.types[prop.ref_type]) if not type_ in dependency_order: dependency_order.append(type_) @@ -151,23 +153,23 @@ class _Generator(object): """Generate a code object with the declaration of a C++ enum. """ c = Code() - c.Sblock('enum class {name} {{'.format( - name=enum_name)) + c.Sblock('enum class {name} {{'.format(name=enum_name)) # Explicitly initialize kNone to 0, since we rely on default initialization # for enum members. Otherwise, default initialization will always set a # value to 0, even if it's not a valid enum entry. c.Append( - self._type_helper.GetEnumNoneValue(type_, full_name=False) + ' = 0,') + self._type_helper.GetEnumNoneValue(type_, full_name=False) + ' = 0,') for value in type_.enum_values: - current_enum_string = ( - self._type_helper.GetEnumValue(type_, value, full_name=False)) + current_enum_string = (self._type_helper.GetEnumValue(type_, + value, + full_name=False)) c.Append(current_enum_string + ',') # Adding kMaxValue, which is friendly to enumaration histogram macros. c.Append('kMaxValue = {last_key_value},'.format( - last_key_value=current_enum_string)) + last_key_value=current_enum_string)) c.Eblock('};') return c @@ -183,10 +185,8 @@ class _Generator(object): needs_blank_line = True if prop.description: c.Comment(prop.description) - (c.Append('%s %s;' % ( - self._type_helper.GetCppType(prop.type_, is_optional=prop.optional), - prop.unix_name)) - ) + (c.Append('%s %s;' % (self._type_helper.GetCppType( + prop.type_, is_optional=prop.optional), prop.unix_name))) return c def _GenerateType(self, type_, is_toplevel=False, generate_typedefs=False): @@ -204,7 +204,7 @@ class _Generator(object): if type_.functions: # Wrap functions within types in the type's namespace. - (c.Append('namespace %s {' % classname) + (c.Append('namespace %s {' % classname) \ .Append() ) for function in type_.functions.values(): @@ -217,10 +217,8 @@ class _Generator(object): if generate_typedefs: item_cpp_type = self._type_helper.GetCppType(type_.item_type) if item_cpp_type != 'base::Value': - (c.Append('using %s = std::vector<%s >;' % ( - classname, - item_cpp_type)) - ) + (c.Append('using %s = std::vector<%s >;' % + (classname, item_cpp_type))) else: c.Append('using %s = base::Value::List;' % classname) elif type_.property_type == PropertyType.STRING: @@ -235,26 +233,25 @@ class _Generator(object): # Top level enums are in a namespace scope so the methods shouldn't be # static. On the other hand, those declared inline (e.g. in an object) do. maybe_static = '' if is_toplevel else 'static ' - (c.Append() + (c.Append() \ .Append('%sconst char* ToString(%s as_enum);' % - (maybe_static, classname)) + (maybe_static, classname)) \ .Append('%s%s Parse%s(std::string_view as_string);' % - (maybe_static, classname, classname)) + (maybe_static, classname, classname)) \ .Append( '%sstd::u16string Get%sParseError(std::string_view as_string);' % (maybe_static, classname)) ) - elif type_.property_type in (PropertyType.CHOICES, - PropertyType.OBJECT): + elif type_.property_type in (PropertyType.CHOICES, PropertyType.OBJECT): if type_.description: c.Comment(type_.description) - (c.Sblock('struct %(classname)s {') - .Append('%(classname)s();') - .Append('~%(classname)s();') - .Append('%(classname)s(const %(classname)s&) = delete;') - .Append('%(classname)s& operator=(const %(classname)s&) = delete;') - .Append('%(classname)s(%(classname)s&& rhs) noexcept;') + (c.Sblock('struct %(classname)s {') \ + .Append('%(classname)s();') \ + .Append('~%(classname)s();') \ + .Append('%(classname)s(const %(classname)s&) = delete;') \ + .Append('%(classname)s& operator=(const %(classname)s&) = delete;') \ + .Append('%(classname)s(%(classname)s&& rhs) noexcept;') \ .Append('%(classname)s& operator=(%(classname)s&& rhs) noexcept;') ) @@ -263,28 +260,27 @@ class _Generator(object): c.Comment('Manifest key constants.') c.Concat(self._GenerateManifestKeyConstants(type_.properties.values())) - value_type = ('base::Value' - if type_.property_type is PropertyType.CHOICES else - 'base::Value::Dict') + value_type = ('base::Value' if type_.property_type is PropertyType.CHOICES + else 'base::Value::Dict') - if (type_.origin.from_json or - (type_.origin.from_manifest_keys and - type_.property_type is PropertyType.CHOICES)): - (c.Append() + if (type_.origin.from_json + or (type_.origin.from_manifest_keys + and type_.property_type is PropertyType.CHOICES)): + (c.Append() \ .Comment('Populates a %s object from a base::Value& instance. Returns' - ' whether |out| was successfully populated.' % classname) + ' whether |out| was successfully populated.' % classname) \ .Append('static bool Populate(%s);' % self._GenerateParams( ('const base::Value& value', '%s& out' % classname))) ) if type_.property_type is not PropertyType.CHOICES: - (c.Append() + (c.Append() \ .Comment('Populates a %s object from a Dict& instance. Returns' - ' whether |out| was successfully populated.' % classname) + ' whether |out| was successfully populated.' % classname) \ .Append('static bool Populate(%s);' % self._GenerateParams( ('const base::Value::Dict& value', '%s& out' % classname))) ) - (c.Append() - .Comment('Creates a deep copy of %s.' % classname) + (c.Append() \ + .Comment('Creates a deep copy of %s.' % classname) \ .Append('%s Clone() const;' % classname) ) @@ -292,32 +288,32 @@ class _Generator(object): classname, support_errors=self._generate_error_messages) if type_.property_type is not PropertyType.CHOICES: - (c.Append() + (c.Append() \ .Comment('Creates a {classname} object from a base::Value::Dict,' ' or {failure} on failure.'.format( classname=classname, failure=('unexpected' - if self._generate_error_messages else 'nullopt'))) + if self._generate_error_messages else 'nullopt'))) \ .Append('static {return_type} ' 'FromValue(const base::Value::Dict& value);'.format( return_type=return_type)) ) - (c.Append() + (c.Append() \ .Comment('Creates a {classname} object from a base::Value,' ' or {failure} on failure.'.format( classname=classname, failure=('unexpected' - if self._generate_error_messages else 'nullopt'))) + if self._generate_error_messages else 'nullopt'))) \ .Append('static {return_type} ' 'FromValue(const base::Value& value);'.format( return_type=return_type)) ) if type_.origin.from_client: - (c.Append() + (c.Append() \ .Comment('Returns a new %s representing the serialized form of this' - '%s object.' % (value_type, classname)) + '%s object.' % (value_type, classname)) \ .Append('%s ToValue() const;' % value_type) ) @@ -330,13 +326,12 @@ class _Generator(object): c.Cblock(self._GenerateTypes(type_.choices)) c.Append('// Choices:') for choice_type in type_.choices: - c.Append('%s as_%s;' % ( - self._type_helper.GetCppType(choice_type, is_optional=True), - choice_type.unix_name)) + c.Append('%s as_%s;' % (self._type_helper.GetCppType( + choice_type, is_optional=True), choice_type.unix_name)) else: properties = type_.properties.values() - (c.Append() - .Cblock(self._GenerateTypes(p.type_ for p in properties)) + (c.Append() \ + .Cblock(self._GenerateTypes(p.type_ for p in properties)) \ .Cblock(self._GenerateFields(properties))) if type_.additional_properties is not None: # Most additionalProperties actually have type "any", which is better @@ -344,7 +339,7 @@ class _Generator(object): if type_.additional_properties.property_type == PropertyType.ANY: c.Append('base::Value::Dict additional_properties;') else: - (c.Cblock(self._GenerateType(type_.additional_properties)) + (c.Cblock(self._GenerateType(type_.additional_properties)) \ .Append('std::map<std::string, %s> additional_properties;' % self._type_helper.GetCppType(type_.additional_properties)) ) @@ -357,10 +352,10 @@ class _Generator(object): c = Code() # TODO(kalman): use event.unix_name not Classname. event_namespace = cpp_util.Classname(event.name) - (c.Append('namespace %s {' % event_namespace) - .Append() - .Concat(self._GenerateEventNameConstant(event)) - .Concat(self._GenerateAsyncResponseArguments(event.params)) + (c.Append('namespace %s {' % event_namespace) \ + .Append() \ + .Concat(self._GenerateEventNameConstant(event)) \ + .Concat(self._GenerateAsyncResponseArguments(event.params)) \ .Append('} // namespace %s' % event_namespace) ) return c @@ -375,8 +370,8 @@ class _Generator(object): # to not use the name. if function_namespace == 'SendMessage': function_namespace = 'PassMessage' - (c.Append('namespace %s {' % function_namespace) - .Append() + (c.Append('namespace %s {' % function_namespace) \ + .Append() \ .Cblock(self._GenerateFunctionParams(function)) ) if function.returns_async: @@ -394,26 +389,26 @@ class _Generator(object): (c.Sblock('struct Params {')) if self._generate_error_messages: (c.Append('static base::expected<Params, std::u16string> ' - 'Create(const base::Value::List& args);') + 'Create(const base::Value::List& args);') \ .Comment('DEPRECATED: prefer the variant of this function ' 'returning errors with `base::expected`.') ) (c.Append('static std::optional<Params> Create(%s);' % self._GenerateParams( - ('const base::Value::List& args',))) - .Append('Params(const Params&) = delete;') - .Append('Params& operator=(const Params&) = delete;') - .Append('Params(Params&& rhs) noexcept;') - .Append('Params& operator=(Params&& rhs) noexcept;') - .Append('~Params();') - .Append() - .Cblock(self._GenerateTypes(p.type_ for p in function.params)) - .Cblock(self._GenerateFields(function.params)) - .Eblock() - .Append() - .Sblock(' private:') - .Append('Params();') + ('const base::Value::List& args',))) \ + .Append('Params(const Params&) = delete;') \ + .Append('Params& operator=(const Params&) = delete;') \ + .Append('Params(Params&& rhs) noexcept;') \ + .Append('Params& operator=(Params&& rhs) noexcept;') \ + .Append('~Params();') \ + .Append() \ + .Cblock(self._GenerateTypes(p.type_ for p in function.params)) \ + .Cblock(self._GenerateFields(function.params)) \ + .Eblock() \ + .Append() \ + .Sblock(' private:') \ + .Append('Params();') \ .Eblock('};') ) return c @@ -424,9 +419,10 @@ class _Generator(object): """ c = Code() for type_ in types: - c.Cblock(self._GenerateType(type_, - is_toplevel=is_toplevel, - generate_typedefs=generate_typedefs)) + c.Cblock( + self._GenerateType(type_, + is_toplevel=is_toplevel, + generate_typedefs=generate_typedefs)) return c def _GenerateManifestKeys(self): @@ -446,26 +442,23 @@ class _Generator(object): # manifest types. if type_.IsRootManifestKeyType(): params = [ - 'const base::Value::Dict& root_dict', - '%s& out' % classname, - 'std::u16string& error' + 'const base::Value::Dict& root_dict', + '%s& out' % classname, 'std::u16string& error' ] comment = ( - 'Parses manifest keys for this namespace. Any keys not available to the' - ' manifest will be ignored. On a parsing error, false is returned and ' - '|error| is populated.') + 'Parses manifest keys for this namespace. Any keys not available to' + ' the manifest will be ignored. On a parsing error, false is returned' + ' and |error| is populated.') else: params = [ - 'const base::Value::Dict& root_dict', - 'std::string_view key', - '%s& out' % classname, - 'std::u16string& error', - 'std::vector<std::string_view>& error_path_reversed' + 'const base::Value::Dict& root_dict', 'std::string_view key', + '%s& out' % classname, 'std::u16string& error', + 'std::vector<std::string_view>& error_path_reversed' ] comment = ( - 'Parses the given |key| from |root_dict|. Any keys not available to the' - ' manifest will be ignored. On a parsing error, false is returned and ' - '|error| and |error_path_reversed| are populated.') + 'Parses the given |key| from |root_dict|. Any keys not available to' + ' the manifest will be ignored. On a parsing error, false is returned' + ' and |error| and |error_path_reversed| are populated.') c = Code() c.Append().Comment(comment) @@ -499,18 +492,18 @@ class _Generator(object): for param in params: if param.description: c.Comment(param.description) - declaration_list.append(cpp_util.GetParameterDeclaration( - param, self._type_helper.GetCppType(param.type_))) - c.Append('base::Value::List Create(%s);' % - ', '.join(declaration_list)) + declaration_list.append( + cpp_util.GetParameterDeclaration( + param, self._type_helper.GetCppType(param.type_))) + c.Append('base::Value::List Create(%s);' % ', '.join(declaration_list)) return c def _GenerateEventNameConstant(self, event): """Generates a constant string array for the event name. """ c = Code() - c.Append('extern const char kEventName[]; // "%s.%s"' % ( - self._namespace.name, event.name)) + c.Append('extern const char kEventName[]; // "%s.%s"' % + (self._namespace.name, event.name)) c.Append() return c @@ -518,15 +511,14 @@ class _Generator(object): """Generates namespace for passing a function's result back. """ c = Code() - (c.Append('namespace Results {') - .Append() - .Concat(self._GenerateAsyncResponseArguments(returns_async.params)) + (c.Append('namespace Results {') \ + .Append() \ + .Concat(self._GenerateAsyncResponseArguments(returns_async.params)) \ .Append('} // namespace Results') ) return c - def _GenerateParams( - self, params, generate_error_messages=None): + def _GenerateParams(self, params, generate_error_messages=None): """Builds the parameter list for a function, given an array of parameters. If |generate_error_messages| is specified, it overrides |self._generate_error_messages|. @@ -539,5 +531,5 @@ class _Generator(object): if generate_error_messages is None: generate_error_messages = self._generate_error_messages if generate_error_messages: - params += ('std::u16string& error',) + params += ('std::u16string& error', ) return ', '.join(str(p) for p in params) diff --git a/tools/json_schema_compiler/idl_schema.py b/tools/json_schema_compiler/idl_schema.py index 8760683cb9e11..ef07f516e6e79 100755 --- a/tools/json_schema_compiler/idl_schema.py +++ b/tools/json_schema_compiler/idl_schema.py @@ -32,6 +32,7 @@ else: finally: sys.path.pop(0) + def ProcessComment(comment): ''' Convert a comment into a parent comment and a list of parameter comments. @@ -57,6 +58,7 @@ def ProcessComment(comment): } ) ''' + def add_paragraphs(content): paragraphs = content.split('\n\n') if len(paragraphs) < 2: @@ -69,8 +71,8 @@ def ProcessComment(comment): # Get the parent comment (everything before the first parameter comment. first_parameter_location = (parameter_starts[0].start() if parameter_starts else len(comment)) - parent_comment = (add_paragraphs(comment[:first_parameter_location].strip()) - .replace('\n', '')) + parent_comment = (add_paragraphs( + comment[:first_parameter_location].strip()).replace('\n', '')) params = OrderedDict() for (cur_param, next_param) in itertools.zip_longest(parameter_starts, @@ -81,9 +83,9 @@ def ProcessComment(comment): # beginning of the next parameter's introduction. param_comment_start = cur_param.end() param_comment_end = next_param.start() if next_param else len(comment) - params[param_name] = ( - add_paragraphs(comment[param_comment_start:param_comment_end].strip()) - .replace('\n', '')) + params[param_name] = (add_paragraphs( + comment[param_comment_start:param_comment_end].strip()).replace( + '\n', '')) return (parent_comment, params) @@ -94,6 +96,7 @@ class Callspec(object): a tuple: (name, list of function parameters, return type, async return) ''' + def __init__(self, callspec_node, comment): self.node = callspec_node self.comment = comment @@ -103,9 +106,10 @@ class Callspec(object): return_type = None returns_async = None if self.node.GetProperty('TYPEREF') not in ('void', None): - return_type = Typeref(self.node.GetProperty('TYPEREF'), - self.node.parent, - {'name': self.node.GetName()}).process(callbacks) + return_type = Typeref(self.node.GetProperty('TYPEREF'), self.node.parent, + { + 'name': self.node.GetName() + }).process(callbacks) # The IDL parser doesn't allow specifying return types as optional. # Instead we infer any object return values to be optional. # TODO(asargent): fix the IDL parser to support optional return types. @@ -127,32 +131,25 @@ class Callspec(object): # trailingCallbackIsFunctionParameter extended attribute). # TODO(tjudkins): Once IDL definitions are changed to describe returning # promises, we can condition on that instead. - if ( - use_returns_async + if (use_returns_async and not self.node.GetProperty('trailingCallbackIsFunctionParameter') - and len(parameters) > 0 - and parameters[-1].get('type') == 'function' - ): + and len(parameters) > 0 and parameters[-1].get('type') == 'function'): returns_async = parameters.pop() # The returns_async field is inherently a function, so doesn't need type # specified on it. returns_async.pop('type') does_not_support_promises = self.node.GetProperty( - 'doesNotSupportPromises' - ) + 'doesNotSupportPromises') if does_not_support_promises is not None: returns_async['does_not_support_promises'] = does_not_support_promises else: assert return_type is None, ( 'Function "%s" cannot support promises and also have a ' - 'return value.' % self.node.GetName() - ) + 'return value.' % self.node.GetName()) else: assert not self.node.GetProperty('doesNotSupportPromises'), ( 'Callspec "%s" does not need to specify [doesNotSupportPromises] if ' - 'it does not have a trailing callback' - % self.node.GetName() - ) + 'it does not have a trailing callback' % self.node.GetName()) return (self.node.GetName(), parameters, return_type, returns_async) @@ -162,13 +159,14 @@ class Param(object): Given a Param node representing a function parameter, converts into a Python dictionary that the JSON schema compiler expects to see. ''' + def __init__(self, param_node): self.node = param_node def process(self, callbacks): - return Typeref(self.node.GetProperty('TYPEREF'), - self.node, - {'name': self.node.GetName()}).process(callbacks) + return Typeref(self.node.GetProperty('TYPEREF'), self.node, { + 'name': self.node.GetName() + }).process(callbacks) class Dictionary(object): @@ -176,6 +174,7 @@ class Dictionary(object): Given an IDL Dictionary node, converts into a Python dictionary that the JSON schema compiler expects to see. ''' + def __init__(self, dictionary_node): self.node = dictionary_node @@ -185,9 +184,11 @@ class Dictionary(object): if node.cls == 'Member': k, v = Member(node).process(callbacks) properties[k] = v - result = {'id': self.node.GetName(), - 'properties': properties, - 'type': 'object'} + result = { + 'id': self.node.GetName(), + 'properties': properties, + 'type': 'object' + } # If this has the `ignoreAdditionalProperties` extended attribute, copy it # into the resulting object with a value of True. if self.node.GetProperty('ignoreAdditionalProperties'): @@ -208,19 +209,22 @@ class Member(object): where the value is a Python dictionary that the JSON schema compiler expects to see. ''' + def __init__(self, member_node): self.node = member_node - def process( - self, callbacks, functions_are_properties=False, use_returns_async=False - ): + def process(self, + callbacks, + functions_are_properties=False, + use_returns_async=False): properties = OrderedDict() name = self.node.GetName() if self.node.GetProperty('deprecated'): properties['deprecated'] = self.node.GetProperty('deprecated') - for property_name in ['nodoc', 'nocompile', 'nodart', - 'serializableFunction']: + for property_name in [ + 'nodoc', 'nocompile', 'nodart', 'serializableFunction' + ]: if self.node.GetProperty(property_name): properties[property_name] = True @@ -230,27 +234,24 @@ class Member(object): if self.node.GetProperty('platforms'): properties['platforms'] = list(self.node.GetProperty('platforms')) - for option_name, sanitizer in [ - ('maxListeners', int), - ('supportsFilters', lambda s: s == 'true'), - ('supportsListeners', lambda s: s == 'true'), - ('supportsRules', lambda s: s == 'true')]: + for option_name, sanitizer in [('maxListeners', int), + ('supportsFilters', lambda s: s == 'true'), + ('supportsListeners', lambda s: s == 'true'), + ('supportsRules', lambda s: s == 'true')]: if self.node.GetProperty(option_name): if 'options' not in properties: properties['options'] = {} - properties['options'][option_name] = sanitizer(self.node.GetProperty( - option_name)) + properties['options'][option_name] = sanitizer( + self.node.GetProperty(option_name)) type_override = None parameter_comments = OrderedDict() for node in self.node.GetChildren(): if node.cls == 'Comment': - (parent_comment, parameter_comments) = ProcessComment( - node.GetName()) + (parent_comment, parameter_comments) = ProcessComment(node.GetName()) properties['description'] = parent_comment elif node.cls == 'Callspec': name, parameters, return_type, returns_async = Callspec( - node, parameter_comments - ).process(use_returns_async, callbacks) + node, parameter_comments).process(use_returns_async, callbacks) if functions_are_properties: # If functions are treated as properties (which will happen if the # interface is named Properties) then this isn't a function, it's a @@ -258,14 +259,14 @@ class Member(object): # property type is the return type. This is an egregious hack in lieu # of the IDL parser supporting 'const'. assert parameters == [], ( - 'Property "%s" must be no-argument functions ' - 'with a non-void return type' % name) + 'Property "%s" must be no-argument functions ' + 'with a non-void return type' % name) assert return_type is not None, ( - 'Property "%s" must be no-argument functions ' - 'with a non-void return type' % name) + 'Property "%s" must be no-argument functions ' + 'with a non-void return type' % name) assert 'type' in return_type, ( - 'Property return type "%s" from "%s" must specify a ' - 'fundamental IDL type.' % (pprint.pformat(return_type), name)) + 'Property return type "%s" from "%s" must specify a ' + 'fundamental IDL type.' % (pprint.pformat(return_type), name)) type_override = return_type['type'] else: type_override = 'function' @@ -279,8 +280,8 @@ class Member(object): if type_override is not None: properties['type'] = type_override else: - properties = Typeref(self.node.GetProperty('TYPEREF'), - self.node, properties).process(callbacks) + properties = Typeref(self.node.GetProperty('TYPEREF'), self.node, + properties).process(callbacks) value = self.node.GetProperty('value') if value is not None: # IDL always returns values as strings, so cast to their real type. @@ -298,9 +299,9 @@ class Member(object): return float(string_value) # Add more as necessary. assert json_type == 'string', ( - 'No rule exists to cast JSON Schema type "%s" to its equivalent ' - 'Python type for value "%s". You must add a new rule here.' % - (json_type, string_value)) + 'No rule exists to cast JSON Schema type "%s" to its equivalent ' + 'Python type for value "%s". You must add a new rule here.' % + (json_type, string_value)) return string_value @@ -310,6 +311,7 @@ class Typeref(object): function parameter, converts into a Python dictionary that the JSON schema compiler expects to see. ''' + def __init__(self, typeref, parent, additional_properties): self.typeref = typeref self.parent = parent @@ -363,11 +365,11 @@ class Typeref(object): properties['additionalProperties'] = OrderedDict() properties['additionalProperties']['type'] = 'any' elif self.parent.GetPropertyLocal('Union'): - properties['choices'] = [Typeref(node.GetProperty('TYPEREF'), - node, - OrderedDict()).process(callbacks) - for node in self.parent.GetChildren() - if node.cls == 'Option'] + properties['choices'] = [ + Typeref(node.GetProperty('TYPEREF'), node, + OrderedDict()).process(callbacks) + for node in self.parent.GetChildren() if node.cls == 'Option' + ] elif self.typeref is None: properties['type'] = 'function' else: @@ -390,6 +392,7 @@ class Enum(object): Given an IDL Enum node, converts into a Python dictionary that the JSON schema compiler expects to see. ''' + def __init__(self, enum_node): self.node = enum_node self.description = '' @@ -411,12 +414,15 @@ class Enum(object): self.description = ProcessComment(node.GetName())[0] else: sys.exit('Did not process %s %s' % (node.cls, node)) - result = {'id' : self.node.GetName(), - 'description': self.description, - 'type': 'string', - 'enum': enum} - for property_name in ['cpp_enum_prefix_override', 'inline_doc', - 'noinline_doc', 'nodoc']: + result = { + 'id': self.node.GetName(), + 'description': self.description, + 'type': 'string', + 'enum': enum + } + for property_name in [ + 'cpp_enum_prefix_override', 'inline_doc', 'noinline_doc', 'nodoc' + ]: if self.node.GetProperty(property_name): result[property_name] = self.node.GetProperty(property_name) if self.node.GetProperty('deprecated'): @@ -485,19 +491,19 @@ class Namespace(object): compiler_options = self.compiler_options or {} documentation_options = self.documentation_options or {} return { - 'namespace': self.namespace.GetName(), - 'description': self.description, - 'nodoc': self.nodoc, - 'types': self.types, - 'functions': self.functions, - 'properties': self.properties, - 'manifest_keys': self.manifest_keys, - 'internal': self.internal, - 'events': self.events, - 'platforms': self.platforms, - 'compiler_options': compiler_options, - 'deprecated': self.deprecated, - 'documentation_options': documentation_options + 'namespace': self.namespace.GetName(), + 'description': self.description, + 'nodoc': self.nodoc, + 'types': self.types, + 'functions': self.functions, + 'properties': self.properties, + 'manifest_keys': self.manifest_keys, + 'internal': self.internal, + 'events': self.events, + 'platforms': self.platforms, + 'compiler_options': compiler_options, + 'deprecated': self.deprecated, + 'documentation_options': documentation_options } def process_interface(self, node, functions_are_properties=False): @@ -542,9 +548,12 @@ class IDLSchema(object): if not description: # TODO(kalman): Go back to throwing an error here. print('%s must have a namespace-level comment. This will ' - 'appear on the API summary page.' % node.GetName()) + 'appear on the API summary page.' % node.GetName()) description = '' - namespace = Namespace(node, description, nodoc, internal, + namespace = Namespace(node, + description, + nodoc, + internal, platforms=platforms, compiler_options=compiler_options or None, deprecated=deprecated, @@ -621,8 +630,9 @@ def Main(): contents = sys.stdin.read() for i, char in enumerate(contents): if not char.isascii(): - raise Exception('Non-ascii character "%s" (ord %d) found at offset %d.' - % (char, ord(char), i)) + raise Exception( + 'Non-ascii character "%s" (ord %d) found at offset %d.' % + (char, ord(char), i)) idl = idl_parser.IDLParser().ParseData(contents, '<stdin>') schema = IDLSchema(idl).process() print(json.dumps(schema, indent=2)) diff --git a/tools/json_schema_compiler/idl_schema_test.py b/tools/json_schema_compiler/idl_schema_test.py index c93333d07d686..27d470485954f 100755 --- a/tools/json_schema_compiler/idl_schema_test.py +++ b/tools/json_schema_compiler/idl_schema_test.py @@ -8,6 +8,7 @@ import unittest from json_parse import OrderedDict + def getFunction(schema, name): for item in schema['functions']: if item['name'] == name: @@ -19,10 +20,12 @@ def getParams(schema, name): function = getFunction(schema, name) return function['parameters'] + def getReturnsAsync(schema, name): function = getFunction(schema, name) return function.get('returns_async', False) + def getReturns(schema, name): function = getFunction(schema, name) return function['returns'] @@ -35,6 +38,7 @@ def getType(schema, id): class IdlSchemaTest(unittest.TestCase): + def setUp(self): loaded = idl_schema.Load('test/idl_basics.idl') self.assertEqual(1, len(loaded)) @@ -44,66 +48,123 @@ class IdlSchemaTest(unittest.TestCase): def testSimpleCallbacks(self): schema = self.idl_basics - expected = {'name': 'cb', 'parameters':[]} + expected = {'name': 'cb', 'parameters': []} self.assertEqual(expected, getReturnsAsync(schema, 'function4')) - expected = {'name': 'cb', - 'parameters':[{'name': 'x', 'type': 'integer'}]} + expected = {'name': 'cb', 'parameters': [{'name': 'x', 'type': 'integer'}]} self.assertEqual(expected, getReturnsAsync(schema, 'function5')) - expected = {'name': 'cb', - 'parameters':[{'name': 'arg', '$ref': 'MyType1'}]} + expected = { + 'name': 'cb', + 'parameters': [{ + 'name': 'arg', + '$ref': 'MyType1' + }] + } self.assertEqual(expected, getReturnsAsync(schema, 'function6')) def testCallbackWithArrayArgument(self): schema = self.idl_basics - expected = {'name': 'cb', - 'parameters':[{'name': 'arg', 'type': 'array', - 'items':{'$ref': 'MyType2'}}]} + expected = { + 'name': + 'cb', + 'parameters': [{ + 'name': 'arg', + 'type': 'array', + 'items': { + '$ref': 'MyType2' + } + }] + } self.assertEqual(expected, getReturnsAsync(schema, 'function12')) def testArrayOfCallbacks(self): schema = idl_schema.Load('test/idl_function_types.idl')[0] - expected = [{'type': 'array', 'name': 'callbacks', - 'items':{'type': 'function', 'name': 'MyCallback', - 'parameters':[{'type': 'integer', 'name': 'x'}]}}] + expected = [{ + 'type': 'array', + 'name': 'callbacks', + 'items': { + 'type': 'function', + 'name': 'MyCallback', + 'parameters': [{ + 'type': 'integer', + 'name': 'x' + }] + } + }] self.assertEqual(expected, getParams(schema, 'whatever')) def testProperties(self): - self.assertEqual({ - 'x': {'name': 'x', 'type': 'integer', - 'description': 'This comment tests "double-quotes".'}, - 'y': {'name': 'y', 'type': 'string'}, - 'z': {'name': 'z', 'type': 'string'}, - 'a': {'name': 'a', 'type': 'string'}, - 'b': {'name': 'b', 'type': 'string'}, - 'c': {'name': 'c', 'type': 'string'}}, - getType(self.idl_basics, 'MyType1')['properties']) + self.assertEqual( + { + 'x': { + 'name': 'x', + 'type': 'integer', + 'description': 'This comment tests "double-quotes".' + }, + 'y': { + 'name': 'y', + 'type': 'string' + }, + 'z': { + 'name': 'z', + 'type': 'string' + }, + 'a': { + 'name': 'a', + 'type': 'string' + }, + 'b': { + 'name': 'b', + 'type': 'string' + }, + 'c': { + 'name': 'c', + 'type': 'string' + } + }, + getType(self.idl_basics, 'MyType1')['properties']) def testMemberOrdering(self): - self.assertEqual( - ['x', 'y', 'z', 'a', 'b', 'c'], - list(getType(self.idl_basics, 'MyType1')['properties'].keys())) + self.assertEqual(['x', 'y', 'z', 'a', 'b', 'c'], + list( + getType(self.idl_basics, + 'MyType1')['properties'].keys())) def testEnum(self): schema = self.idl_basics - expected = {'enum': [{'name': 'name1', 'description': 'comment1'}, - {'name': 'name2'}], - 'description': 'Enum description', - 'type': 'string', 'id': 'EnumType'} + expected = { + 'enum': [{ + 'name': 'name1', + 'description': 'comment1' + }, { + 'name': 'name2' + }], + 'description': 'Enum description', + 'type': 'string', + 'id': 'EnumType' + } self.assertEqual(expected, getType(schema, expected['id'])) expected_params = [{'name': 'type', '$ref': 'EnumType'}] expected_returns_async = { 'name': 'cb', - 'parameters':[{'name': 'type', '$ref': 'EnumType'}]} + 'parameters': [{ + 'name': 'type', + '$ref': 'EnumType' + }] + } self.assertEqual(expected_params, getParams(schema, 'function13')) - self.assertEqual( - expected_returns_async, getReturnsAsync(schema, 'function13') - ) + self.assertEqual(expected_returns_async, + getReturnsAsync(schema, 'function13')) - expected = [{'items': {'$ref': 'EnumType'}, 'name': 'types', - 'type': 'array'}] + expected = [{ + 'items': { + '$ref': 'EnumType' + }, + 'name': 'types', + 'type': 'array' + }] self.assertEqual(expected, getParams(schema, 'function14')) def testScopedArguments(self): @@ -111,19 +172,29 @@ class IdlSchemaTest(unittest.TestCase): expected = [{'name': 'value', '$ref': 'idl_other_namespace.SomeType'}] self.assertEqual(expected, getParams(schema, 'function20')) - expected = [{'items': {'$ref': 'idl_other_namespace.SomeType'}, - 'name': 'values', - 'type': 'array'}] + expected = [{ + 'items': { + '$ref': 'idl_other_namespace.SomeType' + }, + 'name': 'values', + 'type': 'array' + }] self.assertEqual(expected, getParams(schema, 'function21')) - expected = [{'name': 'value', - '$ref': 'idl_other_namespace.sub_namespace.AnotherType'}] + expected = [{ + 'name': 'value', + '$ref': 'idl_other_namespace.sub_namespace.AnotherType' + }] self.assertEqual(expected, getParams(schema, 'function22')) - expected = [{'items': {'$ref': 'idl_other_namespace.sub_namespace.' - 'AnotherType'}, - 'name': 'values', - 'type': 'array'}] + expected = [{ + 'items': { + '$ref': 'idl_other_namespace.sub_namespace.' + 'AnotherType' + }, + 'name': 'values', + 'type': 'array' + }] self.assertEqual(expected, getParams(schema, 'function23')) def testNoCompile(self): @@ -151,45 +222,73 @@ class IdlSchemaTest(unittest.TestCase): 'name': 'name3', 'description': 'comment3' }], - 'type': 'string', - 'id': 'EnumTypeWithNoDocValue', - 'description': '' + 'type': + 'string', + 'id': + 'EnumTypeWithNoDocValue', + 'description': + '' } self.assertEqual(expected, getType(schema, expected['id'])) def testInternalNamespace(self): - idl_basics = self.idl_basics + idl_basics = self.idl_basics self.assertEqual('idl_basics', idl_basics['namespace']) self.assertTrue(idl_basics['internal']) self.assertFalse(idl_basics['nodoc']) def testReturnTypes(self): schema = self.idl_basics - self.assertEqual({'name': 'function24', 'type': 'integer'}, - getReturns(schema, 'function24')) - self.assertEqual({'name': 'function25', '$ref': 'MyType1', - 'optional': True}, - getReturns(schema, 'function25')) - self.assertEqual({'name': 'function26', 'type': 'array', - 'items': {'$ref': 'MyType1'}}, - getReturns(schema, 'function26')) - self.assertEqual({'name': 'function27', '$ref': 'EnumType', - 'optional': True}, - getReturns(schema, 'function27')) - self.assertEqual({'name': 'function28', 'type': 'array', - 'items': {'$ref': 'EnumType'}}, - getReturns(schema, 'function28')) - self.assertEqual({'name': 'function29', '$ref': - 'idl_other_namespace.SomeType', - 'optional': True}, - getReturns(schema, 'function29')) - self.assertEqual({'name': 'function30', 'type': 'array', - 'items': {'$ref': 'idl_other_namespace.SomeType'}}, - getReturns(schema, 'function30')) + self.assertEqual({ + 'name': 'function24', + 'type': 'integer' + }, getReturns(schema, 'function24')) + self.assertEqual({ + 'name': 'function25', + '$ref': 'MyType1', + 'optional': True + }, getReturns(schema, 'function25')) + self.assertEqual( + { + 'name': 'function26', + 'type': 'array', + 'items': { + '$ref': 'MyType1' + } + }, getReturns(schema, 'function26')) + self.assertEqual( + { + 'name': 'function27', + '$ref': 'EnumType', + 'optional': True + }, getReturns(schema, 'function27')) + self.assertEqual( + { + 'name': 'function28', + 'type': 'array', + 'items': { + '$ref': 'EnumType' + } + }, getReturns(schema, 'function28')) + self.assertEqual( + { + 'name': 'function29', + '$ref': 'idl_other_namespace.SomeType', + 'optional': True + }, getReturns(schema, 'function29')) + self.assertEqual( + { + 'name': 'function30', + 'type': 'array', + 'items': { + '$ref': 'idl_other_namespace.SomeType' + } + }, getReturns(schema, 'function30')) def testIgnoresAdditionalPropertiesOnType(self): - self.assertTrue(getType(self.idl_basics, 'IgnoreAdditionalPropertiesType') - ['ignoreAdditionalProperties']) + self.assertTrue( + getType(self.idl_basics, + 'IgnoreAdditionalPropertiesType')['ignoreAdditionalProperties']) def testChromeOSPlatformsNamespace(self): schema = idl_schema.Load('test/idl_namespace_chromeos.idl')[0] @@ -206,7 +305,7 @@ class IdlSchemaTest(unittest.TestCase): def testNonSpecificPlatformsNamespace(self): schema = idl_schema.Load('test/idl_namespace_non_specific_platforms.idl')[0] self.assertEqual('idl_namespace_non_specific_platforms', - schema['namespace']) + schema['namespace']) expected = None self.assertEqual(expected, schema['platforms']) @@ -214,17 +313,16 @@ class IdlSchemaTest(unittest.TestCase): schema = idl_schema.Load('test/idl_generate_error_messages.idl')[0] self.assertEqual('idl_generate_error_messages', schema['namespace']) self.assertTrue(schema['compiler_options'].get('generate_error_messages', - False)) + False)) schema = idl_schema.Load('test/idl_basics.idl')[0] self.assertEqual('idl_basics', schema['namespace']) self.assertFalse(schema['compiler_options'].get('generate_error_messages', - False)) + False)) def testSpecificImplementNamespace(self): schema = idl_schema.Load('test/idl_namespace_specific_implement.idl')[0] - self.assertEqual('idl_namespace_specific_implement', - schema['namespace']) + self.assertEqual('idl_namespace_specific_implement', schema['namespace']) expected = 'idl_namespace_specific_implement.idl' self.assertEqual(expected, schema['compiler_options']['implemented_in']) @@ -232,39 +330,39 @@ class IdlSchemaTest(unittest.TestCase): schema = idl_schema.Load( 'test/idl_namespace_specific_implement_chromeos.idl')[0] self.assertEqual('idl_namespace_specific_implement_chromeos', - schema['namespace']) + schema['namespace']) expected_implemented_path = 'idl_namespace_specific_implement_chromeos.idl' expected_platform = ['chromeos'] self.assertEqual(expected_implemented_path, - schema['compiler_options']['implemented_in']) + schema['compiler_options']['implemented_in']) self.assertEqual(expected_platform, schema['platforms']) def testCallbackComment(self): schema = self.idl_basics self.assertEqual('A comment on a callback.', - getReturnsAsync(schema, 'function16')['description']) + getReturnsAsync(schema, 'function16')['description']) self.assertEqual( 'A parameter.', getReturnsAsync(schema, 'function16')['parameters'][0]['description']) self.assertEqual( 'Just a parameter comment, with no comment on the callback.', getReturnsAsync(schema, 'function17')['parameters'][0]['description']) - self.assertEqual( - 'Override callback comment.', - getReturnsAsync(schema, 'function18')['description']) + self.assertEqual('Override callback comment.', + getReturnsAsync(schema, 'function18')['description']) def testFunctionComment(self): schema = self.idl_basics func = getFunction(schema, 'function3') self.assertEqual(('This comment should appear in the documentation, ' - 'despite occupying multiple lines.'), - func['description']) - self.assertEqual( - [{'description': ('So should this comment about the argument. ' - '<em>HTML</em> is fine too.'), - 'name': 'arg', - '$ref': 'MyType1'}], - func['parameters']) + 'despite occupying multiple lines.'), func['description']) + self.assertEqual([{ + 'description': ('So should this comment about the argument. ' + '<em>HTML</em> is fine too.'), + 'name': + 'arg', + '$ref': + 'MyType1' + }], func['parameters']) func = getFunction(schema, 'function4') self.assertEqual( '<p>This tests if "double-quotes" are escaped correctly.</p>' @@ -275,12 +373,18 @@ class IdlSchemaTest(unittest.TestCase): schema = idl_schema.Load('test/idl_reserved_words.idl')[0] foo_type = getType(schema, 'Foo') - self.assertEqual([{'name': 'float'}, {'name': 'DOMString'}], - foo_type['enum']) + self.assertEqual([{ + 'name': 'float' + }, { + 'name': 'DOMString' + }], foo_type['enum']) enum_type = getType(schema, 'enum') - self.assertEqual([{'name': 'callback'}, {'name': 'namespace'}], - enum_type['enum']) + self.assertEqual([{ + 'name': 'callback' + }, { + 'name': 'namespace' + }], enum_type['enum']) dictionary = getType(schema, 'dictionary') self.assertEqual('integer', dictionary['properties']['long']['type']) @@ -300,12 +404,10 @@ class IdlSchemaTest(unittest.TestCase): self.assertEqual('integer', foo_type['properties']['x']['type']) self.assertEqual('object', foo_type['properties']['y']['type']) self.assertEqual( - 'any', - foo_type['properties']['y']['additionalProperties']['type']) + 'any', foo_type['properties']['y']['additionalProperties']['type']) self.assertEqual('object', foo_type['properties']['z']['type']) self.assertEqual( - 'any', - foo_type['properties']['z']['additionalProperties']['type']) + 'any', foo_type['properties']['z']['additionalProperties']['type']) self.assertEqual('Window', foo_type['properties']['z']['isInstanceOf']) bar_type = getType(schema, 'BarType') @@ -337,35 +439,48 @@ class IdlSchemaTest(unittest.TestCase): union_type = getType(schema, 'UnionType') expected = { - 'type': 'object', - 'id': 'UnionType', - 'properties': { - 'x': { - 'name': 'x', - 'optional': True, - 'choices': [ - {'type': 'integer'}, - {'$ref': 'FooType'}, - ] - }, - 'y': { - 'name': 'y', - 'choices': [ - {'type': 'string'}, - {'type': 'object', - 'additionalProperties': {'type': 'any'}} - ] - }, - 'z': { - 'name': 'z', - 'choices': [ - {'type': 'object', 'isInstanceOf': 'ImageData', - 'additionalProperties': {'type': 'any'}}, - {'type': 'integer'} - ] - } - }, - } + 'type': 'object', + 'id': 'UnionType', + 'properties': { + 'x': { + 'name': 'x', + 'optional': True, + 'choices': [ + { + 'type': 'integer' + }, + { + '$ref': 'FooType' + }, + ] + }, + 'y': { + 'name': + 'y', + 'choices': [{ + 'type': 'string' + }, { + 'type': 'object', + 'additionalProperties': { + 'type': 'any' + } + }] + }, + 'z': { + 'name': + 'z', + 'choices': [{ + 'type': 'object', + 'isInstanceOf': 'ImageData', + 'additionalProperties': { + 'type': 'any' + } + }, { + 'type': 'integer' + }] + } + }, + } self.assertEqual(expected, union_type) @@ -374,19 +489,20 @@ class IdlSchemaTest(unittest.TestCase): union_type = getType(schema, 'ModifiedUnionType') expected = { - 'type': 'object', - 'id': 'ModifiedUnionType', - 'properties': { - 'x': { - 'name': 'x', - 'nodoc': True, - 'choices': [ - {'type': 'integer'}, - {'type': 'string'} - ] - } - } - } + 'type': 'object', + 'id': 'ModifiedUnionType', + 'properties': { + 'x': { + 'name': 'x', + 'nodoc': True, + 'choices': [{ + 'type': 'integer' + }, { + 'type': 'string' + }] + } + } + } self.assertEqual(expected, union_type) @@ -394,17 +510,17 @@ class IdlSchemaTest(unittest.TestCase): schema = idl_schema.Load('test/idl_object_types.idl')[0] object_type = getType(schema, 'SerializableFunctionObject') expected = { - 'type': 'object', - 'id': 'SerializableFunctionObject', - 'properties': { - 'func': { - 'name': 'func', - 'serializableFunction': True, - 'type': 'function', - 'parameters': [] - } - } - } + 'type': 'object', + 'id': 'SerializableFunctionObject', + 'properties': { + 'func': { + 'name': 'func', + 'serializableFunction': True, + 'type': 'function', + 'parameters': [] + } + } + } self.assertEqual(expected, object_type) def testUnionsWithFunctions(self): @@ -412,12 +528,13 @@ class IdlSchemaTest(unittest.TestCase): union_params = getParams(schema, 'union_params') expected = [{ - 'name': 'x', - 'choices': [ - {'type': 'integer'}, - {'type': 'string'} - ] - }] + 'name': 'x', + 'choices': [{ + 'type': 'integer' + }, { + 'type': 'string' + }] + }] self.assertEqual(expected, union_params) @@ -426,24 +543,32 @@ class IdlSchemaTest(unittest.TestCase): blah_params = getReturnsAsync(schema, 'blah') expected = { - 'name': 'callback', 'parameters': [{ - 'name': 'x', - 'choices': [ - {'type': 'integer'}, - {'type': 'string'} - ]} - ] - } + 'name': + 'callback', + 'parameters': [{ + 'name': 'x', + 'choices': [{ + 'type': 'integer' + }, { + 'type': 'string' + }] + }] + } badabish_params = getReturnsAsync(schema, 'badabish') expected = { - 'name': 'callback', 'parameters': [{ - 'name': 'x', 'optional': True, 'choices': [ - {'type': 'integer'}, - {'type': 'string'} - ] - }] - } + 'name': + 'callback', + 'parameters': [{ + 'name': 'x', + 'optional': True, + 'choices': [{ + 'type': 'integer' + }, { + 'type': 'string' + }] + }] + } self.assertEqual(expected, badabish_params) @@ -453,7 +578,10 @@ class IdlSchemaTest(unittest.TestCase): expected_params = [] expected_returns_async = { 'name': 'callback', - 'parameters': [{'name': 'x', 'type': 'integer'}], + 'parameters': [{ + 'name': 'x', + 'type': 'integer' + }], 'does_not_support_promises': 'Test' } params = getParams(schema, 'non_promise_supporting') @@ -465,46 +593,60 @@ class IdlSchemaTest(unittest.TestCase): def testFunctionWithoutPromiseSupportAndParams(self): schema = idl_schema.Load('test/idl_function_types.idl')[0] - expected_params = [ - {'name': 'z', 'type': 'integer'}, - {'name': 'y', 'choices': [{'type': 'integer'}, {'type': 'string'}]} - ] + expected_params = [{ + 'name': 'z', + 'type': 'integer' + }, { + 'name': 'y', + 'choices': [{ + 'type': 'integer' + }, { + 'type': 'string' + }] + }] expected_returns_async = { 'name': 'callback', - 'parameters': [{'name': 'x', 'type': 'integer'}], + 'parameters': [{ + 'name': 'x', + 'type': 'integer' + }], 'does_not_support_promises': 'Test' } params = getParams(schema, 'non_promise_supporting_with_params') - returns_async = getReturnsAsync( - schema, 'non_promise_supporting_with_params' - ) + returns_async = getReturnsAsync(schema, + 'non_promise_supporting_with_params') self.assertEqual(expected_params, params) self.assertEqual(expected_returns_async, returns_async) def testProperties(self): schema = idl_schema.Load('test/idl_properties.idl')[0] - self.assertEqual(OrderedDict([ - ('first', OrderedDict([ - ('description', 'Integer property.'), - ('type', 'integer'), - ('value', 42), - ])), - ('second', OrderedDict([ - ('description', 'Double property.'), - ('type', 'number'), - ('value', 42.1), - ])), - ('third', OrderedDict([ - ('description', 'String property.'), - ('type', 'string'), - ('value', 'hello world'), - ])), - ('fourth', OrderedDict([ - ('description', 'Unvalued property.'), - ('type', 'integer'), - ])), - ]), schema.get('properties')) + self.assertEqual( + OrderedDict([ + ('first', + OrderedDict([ + ('description', 'Integer property.'), + ('type', 'integer'), + ('value', 42), + ])), + ('second', + OrderedDict([ + ('description', 'Double property.'), + ('type', 'number'), + ('value', 42.1), + ])), + ('third', + OrderedDict([ + ('description', 'String property.'), + ('type', 'string'), + ('value', 'hello world'), + ])), + ('fourth', + OrderedDict([ + ('description', 'Unvalued property.'), + ('type', 'integer'), + ])), + ]), schema.get('properties')) def testManifestKeys(self): schema = self.idl_basics @@ -512,19 +654,20 @@ class IdlSchemaTest(unittest.TestCase): # exhaustive so we don't have to update it each time we add a new key in the # test file. manifest_keys = schema.get('manifest_keys') - self.assertEqual(manifest_keys['key_str'], - OrderedDict([('description', 'String manifest key.'), - ('name', 'key_str'), - ('type', 'string')])) + self.assertEqual( + manifest_keys['key_str'], + OrderedDict([('description', 'String manifest key.'), + ('name', 'key_str'), ('type', 'string')])) self.assertEqual(manifest_keys['key_ref'], - OrderedDict([('name', 'key_ref'), - ('$ref', 'MyType2')])), - self.assertEqual(manifest_keys['choice_with_arrays'], - OrderedDict([('name', 'choice_with_arrays'), - ('$ref', 'ChoiceWithArraysType')])), - self.assertEqual(manifest_keys['choice_with_optional'], - OrderedDict([('name', 'choice_with_optional'), - ('$ref', 'ChoiceWithOptionalType')])) + OrderedDict([('name', 'key_ref'), ('$ref', 'MyType2')])), + self.assertEqual( + manifest_keys['choice_with_arrays'], + OrderedDict([('name', 'choice_with_arrays'), + ('$ref', 'ChoiceWithArraysType')])), + self.assertEqual( + manifest_keys['choice_with_optional'], + OrderedDict([('name', 'choice_with_optional'), + ('$ref', 'ChoiceWithOptionalType')])) def testNoManifestKeys(self): schema = idl_schema.Load('test/idl_properties.idl')[0] diff --git a/tools/json_schema_compiler/js_externs_generator.py b/tools/json_schema_compiler/js_externs_generator.py index 3672f975bf0e6..4f1dc9bd8a2dc 100644 --- a/tools/json_schema_compiler/js_externs_generator.py +++ b/tools/json_schema_compiler/js_externs_generator.py @@ -23,11 +23,15 @@ NOTE = """// NOTE: The format of types has changed. 'FooType' is now // See https://chromium.googlesource.com/chromium/src/+/main/docs/closure_compilation.md """ + class JsExternsGenerator(object): + def Generate(self, namespace): return _Generator(namespace).Generate() + class _Generator(object): + def __init__(self, namespace): self._namespace = namespace self._class_name = None @@ -45,7 +49,7 @@ class _Generator(object): src_to_script = os.path.relpath(script_dir, src_root) # tools/json_schema_compiler/compiler.py compiler_path = os.path.join(src_to_script, 'compiler.py') - (c.Append(self._GetHeader(compiler_path, self._namespace.name)) + (c.Append(self._GetHeader(compiler_path, self._namespace.name)) \ .Append()) self._AppendNamespaceObject(c) @@ -69,13 +73,10 @@ class _Generator(object): def _GetHeader(self, tool, namespace): """Returns the file header text. """ - return (self._js_util.GetLicense() + '\n' + - self._js_util.GetInfo(tool) + (NOTE % namespace) + '\n' + - '/**\n' + + return (self._js_util.GetLicense() + '\n' + self._js_util.GetInfo(tool) + + (NOTE % namespace) + '\n' + '/**\n' + (' * @fileoverview Externs generated from namespace: %s\n' % - namespace) + - ' * @externs\n' + - ' */') + namespace) + ' * @externs\n' + ' */') def _AppendType(self, c, js_type): """Given a Type object, generates the Code for this type's definition. @@ -95,9 +96,10 @@ class _Generator(object): js_type.simple_name) c.Eblock(' */') c.Append('%s.%s = {' % (self._GetNamespace(), js_type.name)) - c.Append('\n'.join( - [" %s: '%s'," % (self._js_util.GetPropertyName(v.name), v.name) - for v in js_type.enum_values])) + c.Append('\n'.join([ + " %s: '%s'," % (self._js_util.GetPropertyName(v.name), v.name) + for v in js_type.enum_values + ])) c.Append('};') def _IsTypeConstructor(self, js_type): @@ -120,8 +122,8 @@ class _Generator(object): if js_type.property_type is not PropertyType.OBJECT: self._js_util.AppendTypeJsDoc(c, self._namespace.name, js_type, optional) elif is_constructor: - c.Comment('@constructor', comment_prefix = '', wrap_indent=4) - c.Comment('@private', comment_prefix = '', wrap_indent=4) + c.Comment('@constructor', comment_prefix='', wrap_indent=4) + c.Comment('@private', comment_prefix='', wrap_indent=4) else: self._AppendTypedef(c, js_type.properties) @@ -151,8 +153,10 @@ class _Generator(object): c.Append('@typedef {') if properties: - self._js_util.AppendObjectDefinition( - c, self._namespace.name, properties, new_line=False) + self._js_util.AppendObjectDefinition(c, + self._namespace.name, + properties, + new_line=False) else: c.Append('Object', new_line=False) c.Append('}', new_line=False) @@ -178,8 +182,8 @@ class _Generator(object): """ self._js_util.AppendFunctionJsDoc(c, self._namespace.name, function) params = self._GetFunctionParams(function) - c.Append('%s.%s = function(%s) {};' % (self._GetNamespace(), - function.name, params)) + c.Append('%s.%s = function(%s) {};' % + (self._GetNamespace(), function.name, params)) c.Append() def _AppendEvent(self, c, event): diff --git a/tools/json_schema_compiler/js_externs_generator_test.py b/tools/json_schema_compiler/js_externs_generator_test.py index 560c5c8dcf642..924f4aa3c3a9c 100755 --- a/tools/json_schema_compiler/js_externs_generator_test.py +++ b/tools/json_schema_compiler/js_externs_generator_test.py @@ -496,15 +496,16 @@ chrome.fakeJson.funcWithReturnsAsync = function(someNumber, callback) {};""" % ( class JsExternGeneratorTest(unittest.TestCase): + def _GetNamespace(self, fake_content, filename, is_idl): """Returns a namespace object for the given content""" - api_def = (idl_schema.Process(fake_content, filename) if is_idl - else json_parse.Parse(fake_content)) + api_def = (idl_schema.Process(fake_content, filename) + if is_idl else json_parse.Parse(fake_content)) m = model.Model() return m.AddNamespace(api_def[0], filename) def setUp(self): - self.maxDiff = None # Lets us see the full diff when inequal. + self.maxDiff = None # Lets us see the full diff when inequal. def testBasic(self): namespace = self._GetNamespace(fake_idl, 'fake_api.idl', True) diff --git a/tools/json_schema_compiler/js_interface_generator.py b/tools/json_schema_compiler/js_interface_generator.py index 9ef1699c9d555..4ab0f8eb2e626 100644 --- a/tools/json_schema_compiler/js_interface_generator.py +++ b/tools/json_schema_compiler/js_interface_generator.py @@ -15,11 +15,15 @@ import os import sys import re + class JsInterfaceGenerator(object): + def Generate(self, namespace): return _Generator(namespace).Generate() + class _Generator(object): + def __init__(self, namespace): self._namespace = namespace first = namespace.name[0].upper() @@ -31,7 +35,7 @@ class _Generator(object): """Generates a Code object with the schema for the entire namespace. """ c = Code() - (c.Append(self._GetHeader(sys.argv[0], self._namespace.name)) + (c.Append(self._GetHeader(sys.argv[0], self._namespace.name)) \ .Append()) self._AppendInterfaceObject(c) @@ -56,10 +60,10 @@ class _Generator(object): def _GetHeader(self, tool, namespace): """Returns the file header text. """ - return ( - self._js_util.GetLicense() + '\n' + self._js_util.GetInfo(tool) + '\n' + - ('/** @fileoverview Interface for %s that can be overriden. */' % - namespace)) + return (self._js_util.GetLicense() + '\n' + self._js_util.GetInfo(tool) + + '\n' + + ('/** @fileoverview Interface for %s that can be overriden. */' % + namespace)) def _AppendInterfaceObject(self, c): """Appends the code creating the interface object. @@ -67,7 +71,7 @@ class _Generator(object): /** @interface */ function SettingsPrivate() {} """ - (c.Append('/** @interface */') + (c.Append('/** @interface */') \ .Append('function %s() {}' % self._interface)) def _AppendFunction(self, c, function): @@ -88,8 +92,8 @@ class _Generator(object): self._js_util.AppendFunctionJsDoc(c, self._namespace.name, function) - c.Append('%s: function(%s) {},' % (function.name, ', '.join( - getParamNames(function)))) + c.Append('%s: function(%s) {},' % + (function.name, ', '.join(getParamNames(function)))) c.Append() def _AppendEvent(self, c, event): diff --git a/tools/json_schema_compiler/js_interface_generator_test.py b/tools/json_schema_compiler/js_interface_generator_test.py index 84416ba1a9761..18cb3e34bf1cf 100755 --- a/tools/json_schema_compiler/js_interface_generator_test.py +++ b/tools/json_schema_compiler/js_interface_generator_test.py @@ -110,10 +110,12 @@ FakeApi.prototype = { * @type {!ChromeEvent} * @see https://developer.chrome.com/extensions/fakeApi#event-onTrapDetected */ -FakeApi.prototype.onTrapDetected;""" % (datetime.now().year, - sys.argv[0].replace('\\', '/'))) +FakeApi.prototype.onTrapDetected;""" % + (datetime.now().year, sys.argv[0].replace('\\', '/'))) + class JsExternGeneratorTest(unittest.TestCase): + def _GetNamespace(self, fake_content, filename): """Returns a namespace object for the given content""" api_def = idl_schema.Process(fake_content, filename) @@ -121,7 +123,7 @@ class JsExternGeneratorTest(unittest.TestCase): return m.AddNamespace(api_def[0], filename) def setUp(self): - self.maxDiff = None # Lets us see the full diff when inequal. + self.maxDiff = None # Lets us see the full diff when inequal. def testBasic(self): namespace = self._GetNamespace(fake_idl, 'fake_api.idl') diff --git a/tools/json_schema_compiler/js_util.py b/tools/json_schema_compiler/js_util.py index 259e539eaa47e..089cad7a9b63c 100644 --- a/tools/json_schema_compiler/js_util.py +++ b/tools/json_schema_compiler/js_util.py @@ -21,6 +21,7 @@ INFO = """// This file was generated by: class JsUtil(object): """A helper class for generating JS Code. """ + def GetLicense(self): """Returns the license text for JS extern and interface files. """ @@ -31,7 +32,7 @@ class JsUtil(object): """ return (INFO % tool.replace('\\', '/')) - def GetPropertyName(self,e): + def GetPropertyName(self, e): # Enum properties are normified to be in ALL_CAPS_STYLE. # Assume enum '1ring-rulesThemAll'. # Transform to '1ring-rules_Them_All'. @@ -43,8 +44,11 @@ class JsUtil(object): # Transform to '_1RING_RULES_THEM_ALL'. return e.upper() - def AppendObjectDefinition(self, c, namespace_name, properties, - new_line=True): + def AppendObjectDefinition(self, + c, + namespace_name, + properties, + new_line=True): """Given an OrderedDict of properties, returns a Code containing the description of an object. """ @@ -62,8 +66,8 @@ class JsUtil(object): first = False js_type = self._TypeToJsType(namespace_name, prop.type_) if prop.optional: - js_type = (Code().Append('(') - .Concat(js_type, new_line=False) + js_type = (Code().Append('(') \ + .Concat(js_type, new_line=False) \ .Append('|undefined)', new_line=False)) c.Append('%s: ' % field, strip_right=False) c.Concat(js_type, new_line=False) @@ -88,16 +92,18 @@ class JsUtil(object): c.Append('}', new_line=False) c.Comment(' %s' % name, comment_prefix='', wrap_indent=4, new_line=False) if description: - c.Comment(' %s' % description, comment_prefix='', - wrap_indent=4, new_line=False) + c.Comment(' %s' % description, + comment_prefix='', + wrap_indent=4, + new_line=False) for i, param in enumerate(function.params): # Mark the parameter as optional, *only if* all following parameters are # also optional, to avoid JSC_OPTIONAL_ARG_AT_END errors thrown by Closure # Compiler. - optional = ( - all(p.optional for p in function.params[i:]) and - (function.returns_async is None or function.returns_async.optional)) + optional = (all(p.optional for p in function.params[i:]) + and (function.returns_async is None + or function.returns_async.optional)) js_type = self._TypeToJsType(namespace_name, param.type_) # If the parameter was originally optional, but was followed by @@ -123,8 +129,8 @@ class JsUtil(object): if function.returns: append_field(c, 'return', - self._TypeToJsType(namespace_name, function.returns), - '', False, function.returns.description) + self._TypeToJsType(namespace_name, function.returns), '', + False, function.returns.description) if function.deprecated: c.Append('@deprecated %s' % function.deprecated) @@ -149,9 +155,8 @@ class JsUtil(object): """Converts a model.Function to a JS type (i.e., function([params])...)""" c = Code() c.Append('function(') - c.Concat( - self._FunctionParamsToJsParams(namespace_name, function.params), - new_line=False) + c.Concat(self._FunctionParamsToJsParams(namespace_name, function.params), + new_line=False) c.Append('): ', new_line=False, strip_right=False) if function.returns: @@ -169,9 +174,9 @@ class JsUtil(object): # appended to the params as a callback. c = Code() c.Append('function(') - c.Concat( - self._FunctionParamsToJsParams(namespace_name, returns_async.params), - new_line=False) + c.Concat(self._FunctionParamsToJsParams(namespace_name, + returns_async.params), + new_line=False) c.Append('): ', new_line=False, strip_right=False) c.Append('void', new_line=False) @@ -211,10 +216,9 @@ class JsUtil(object): return Code().Append(js_type.instance_of) return Code().Append('Object') if js_type.property_type is PropertyType.ARRAY: - return (Code().Append('!Array<'). - Concat(self._TypeToJsType(namespace_name, js_type.item_type), - new_line=False). - Append('>', new_line=False)) + return (Code().Append('!Array<').Concat( + self._TypeToJsType(namespace_name, js_type.item_type), + new_line=False).Append('>', new_line=False)) if js_type.property_type is PropertyType.REF: ref_type = '!chrome.%s.%s' % (namespace_name, js_type.ref_type) return Code().Append(ref_type) @@ -235,7 +239,7 @@ class JsUtil(object): return Code().Append('*') if js_type.property_type.is_fundamental: return Code().Append(js_type.property_type.name) - return Code().Append('?') # TODO(tbreisacher): Make this more specific. + return Code().Append('?') # TODO(tbreisacher): Make this more specific. def AppendSeeLink(self, c, namespace_name, object_type, object_name): """Appends a @see link for a given API 'object' (type, method, or event). diff --git a/tools/json_schema_compiler/json_parse.py b/tools/json_schema_compiler/json_parse.py index fcbdb7d54a1b0..541990cee3f98 100644 --- a/tools/json_schema_compiler/json_parse.py +++ b/tools/json_schema_compiler/json_parse.py @@ -9,8 +9,8 @@ import sys _FILE_PATH = os.path.dirname(os.path.realpath(__file__)) _SYS_PATH = sys.path[:] try: - _COMMENT_EATER_PATH = os.path.join( - _FILE_PATH, os.pardir, 'json_comment_eater') + _COMMENT_EATER_PATH = os.path.join(_FILE_PATH, os.pardir, + 'json_comment_eater') sys.path.insert(0, _COMMENT_EATER_PATH) import json_comment_eater finally: @@ -36,17 +36,14 @@ except ImportError: _SYS_PATH = sys.path[:] try: - _SIMPLE_JSON_PATH = os.path.join(_FILE_PATH, - os.pardir, - os.pardir, + _SIMPLE_JSON_PATH = os.path.join(_FILE_PATH, os.pardir, os.pardir, 'third_party') sys.path.insert(0, _SIMPLE_JSON_PATH) # Add this path in case this is being used in the docs server. - sys.path.insert(0, os.path.join(_FILE_PATH, - os.pardir, - os.pardir, - 'third_party', - 'json_schema_compiler')) + sys.path.insert( + 0, + os.path.join(_FILE_PATH, os.pardir, os.pardir, 'third_party', + 'json_schema_compiler')) import simplejson from simplejson import OrderedDict finally: diff --git a/tools/json_schema_compiler/json_schema.py b/tools/json_schema_compiler/json_schema.py index e623a6966c0f3..8a8366d7d72a5 100644 --- a/tools/json_schema_compiler/json_schema.py +++ b/tools/json_schema_compiler/json_schema.py @@ -17,8 +17,8 @@ def DeleteNodes(item, delete_key=None, matcher=None): def ShouldDelete(thing): return json_parse.IsDict(thing) and ( - delete_key is not None and delete_key in thing or - matcher is not None and matcher(thing)) + delete_key is not None and delete_key in thing + or matcher is not None and matcher(thing)) if json_parse.IsDict(item): toDelete = [] @@ -30,8 +30,10 @@ def DeleteNodes(item, delete_key=None, matcher=None): for key in toDelete: del item[key] elif type(item) == list: - item[:] = [DeleteNodes(thing, delete_key, matcher) - for thing in item if not ShouldDelete(thing)] + item[:] = [ + DeleteNodes(thing, delete_key, matcher) for thing in item + if not ShouldDelete(thing) + ] return item diff --git a/tools/json_schema_compiler/json_schema_test.py b/tools/json_schema_compiler/json_schema_test.py index a059c22cf2bd4..6b8f42d7300c2 100755 --- a/tools/json_schema_compiler/json_schema_test.py +++ b/tools/json_schema_compiler/json_schema_test.py @@ -6,93 +6,85 @@ import json_schema import unittest + class JsonSchemaUnittest(unittest.TestCase): + def testNocompile(self): - compiled = [ - { + compiled = [{ "namespace": "compile", "description": "The compile API.", "functions": [], - "types": {} - }, - - { + "types": {} + }, { "namespace": "functions", "description": "The functions API.", - "functions": [ - { + "functions": [{ "id": "two" - }, - { + }, { "id": "four" - } - ], - + }], "types": { - "one": { "key": "value" } + "one": { + "key": "value" + } } - }, - - { + }, { "namespace": "types", "description": "The types API.", - "functions": [ - { "id": "one" } - ], + "functions": [{ + "id": "one" + }], "types": { - "two": { - "key": "value" - }, - "four": { - "key": "value" - } + "two": { + "key": "value" + }, + "four": { + "key": "value" + } } - }, - - { + }, { "namespace": "nested", "description": "The nested API.", "properties": { - "sync": { - "functions": [ - { - "id": "two" - }, - { - "id": "four" - } - ], - "types": { - "two": { - "key": "value" - }, - "four": { - "key": "value" - } + "sync": { + "functions": [{ + "id": "two" + }, { + "id": "four" + }], + "types": { + "two": { + "key": "value" + }, + "four": { + "key": "value" + } + } } - } } - } - ] + }] schema = json_schema.CachedLoad('test/json_schema_test.json') self.assertEqual(compiled, json_schema.DeleteNodes(schema, 'nocompile')) def should_delete(value): return isinstance(value, dict) and not value.get('valid', True) - expected = [ - {'one': {'test': 'test'}}, - {'valid': True}, - {} - ] - given = [ - {'one': {'test': 'test'}, 'two': {'valid': False}}, - {'valid': True}, - {}, - {'valid': False} - ] - self.assertEqual( - expected, json_schema.DeleteNodes(given, matcher=should_delete)) + + expected = [{'one': {'test': 'test'}}, {'valid': True}, {}] + given = [{ + 'one': { + 'test': 'test' + }, + 'two': { + 'valid': False + } + }, { + 'valid': True + }, {}, { + 'valid': False + }] + self.assertEqual(expected, + json_schema.DeleteNodes(given, matcher=should_delete)) if __name__ == '__main__': diff --git a/tools/json_schema_compiler/memoize.py b/tools/json_schema_compiler/memoize.py index 37a3ddd20b8f3..95e84074906c8 100644 --- a/tools/json_schema_compiler/memoize.py +++ b/tools/json_schema_compiler/memoize.py @@ -2,13 +2,16 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. + def memoize(fn): '''Decorates |fn| to memoize. ''' memory = {} + def impl(*args, **optargs): full_args = args + tuple(optargs.items()) if full_args not in memory: memory[full_args] = fn(*args, **optargs) return memory[full_args] + return impl diff --git a/tools/json_schema_compiler/model.py b/tools/json_schema_compiler/model.py index fa3589f66dffe..06155e18c937a 100644 --- a/tools/json_schema_compiler/model.py +++ b/tools/json_schema_compiler/model.py @@ -17,14 +17,16 @@ def _IsTypeFromManifestKeys(namespace, typename, fallback): return fallback + class ParseException(Exception): """Thrown when data in the model is invalid. """ + def __init__(self, parent, message): hierarchy = _GetModelHierarchy(parent) hierarchy.append(message) - Exception.__init__( - self, 'Model parse exception at:\n' + '\n'.join(hierarchy)) + Exception.__init__(self, + 'Model parse exception at:\n' + '\n'.join(hierarchy)) class Model(object): @@ -33,6 +35,7 @@ class Model(object): Properties: - |namespaces| a map of a namespace name to its model.Namespace """ + def __init__(self, allow_inline_enums=True): self._allow_inline_enums = allow_inline_enums self.namespaces = {} @@ -67,11 +70,13 @@ class ComplexFeature(object): - |unix_name| the unix_name of the feature - |feature_list| a list of simple features which make up the feature """ + def __init__(self, feature_name, features): self.name = feature_name self.unix_name = UnixName(self.name) self.feature_list = features + class SimpleFeature(object): """A simple feature, which can make up a complex feature, as specified in files such as chrome/common/extensions/api/_permission_features.json. @@ -83,6 +88,7 @@ class SimpleFeature(object): - |extension_types| the types which can use the feature - |allowlist| a list of extensions allowed to use the feature """ + def __init__(self, feature_name, feature_def): self.name = feature_name self.unix_name = UnixName(self.name) @@ -112,6 +118,7 @@ class Namespace(object): |include_compiler_options| is True - |manifest_keys| is a Type representing the manifest keys for this namespace. """ + def __init__(self, json, source_file, @@ -122,7 +129,7 @@ class Namespace(object): if 'description' not in json: # TODO(kalman): Go back to throwing an error here. print('%s must have a "description" field. This will appear ' - 'on the API summary page.' % self.name) + 'on the API summary page.' % self.name) json['description'] = '' self.description = json['description'] self.nodoc = json.get('nodoc', False) @@ -198,12 +205,8 @@ class Type(object): - |additional_properties| the type of the additional properties, if any is specified """ - def __init__(self, - parent, - name, - json, - namespace, - input_origin): + + def __init__(self, parent, name, json, namespace, input_origin): self.name = name # The typename "ManifestKeys" is reserved. @@ -221,8 +224,9 @@ class Type(object): # We need to do this to ensure types reference by manifest types have the # correct value for |origin.from_manifest_keys|. self.origin = Origin( - input_origin.from_client, input_origin.from_json, - _IsTypeFromManifestKeys(namespace, name, input_origin.from_manifest_keys)) + input_origin.from_client, input_origin.from_json, + _IsTypeFromManifestKeys(namespace, name, + input_origin.from_manifest_keys)) self.parent = parent self.instance_of = json.get('isInstanceOf', None) @@ -252,7 +256,7 @@ class Type(object): raise ParseException( self, 'Inline enum "%s" found in namespace "%s". These are not allowed. ' - 'See crbug.com/472279' % (name, namespace.name)) + 'See crbug.com/472279' % (name, namespace.name)) self.property_type = PropertyType.ENUM self.enum_values = [EnumValue(value, namespace) for value in json['enum']] self.cpp_enum_prefix_override = json.get('cpp_enum_prefix_override', None) @@ -264,13 +268,13 @@ class Type(object): self.property_type = PropertyType.BOOLEAN elif json_type == 'integer': self.property_type = PropertyType.INTEGER - elif (json_type == 'double' or - json_type == 'number'): + elif (json_type == 'double' or json_type == 'number'): self.property_type = PropertyType.DOUBLE elif json_type == 'string': self.property_type = PropertyType.STRING elif 'choices' in json: self.property_type = PropertyType.CHOICES + def generate_type_name(type_json): if 'items' in type_json: return '%ss' % generate_type_name(type_json['items']) @@ -279,28 +283,22 @@ class Type(object): if 'type' in type_json: return type_json['type'] return None + self.choices = [ Type(self, - generate_type_name(choice) or 'choice%s' % i, - choice, - namespace, - self.origin) - for i, choice in enumerate(json['choices'])] + generate_type_name(choice) or 'choice%s' % i, choice, namespace, + self.origin) for i, choice in enumerate(json['choices']) + ] elif json_type == 'object': - if not ( - 'isInstanceOf' in json or - 'properties' in json or - 'additionalProperties' in json or - 'functions' in json or - 'events' in json): + if not ('isInstanceOf' in json or 'properties' in json + or 'additionalProperties' in json or 'functions' in json + or 'events' in json): raise ParseException(self, name + " has no properties or functions") self.property_type = PropertyType.OBJECT additional_properties_json = json.get('additionalProperties', None) if additional_properties_json is not None: - self.additional_properties = Type(self, - 'additionalProperties', - additional_properties_json, - namespace, + self.additional_properties = Type(self, 'additionalProperties', + additional_properties_json, namespace, self.origin) else: self.additional_properties = None @@ -309,8 +307,8 @@ class Type(object): # Sometimes we might have an unnamed function, e.g. if it's a property # of an object. Use the name of the property in that case. function_name = json.get('name', name) - self.function = Function( - self, function_name, json, namespace, self.origin) + self.function = Function(self, function_name, json, namespace, + self.origin) else: raise ParseException(self, 'Unsupported JSON type %s' % json_type) @@ -321,6 +319,7 @@ class Type(object): ''' return self.name == 'ManifestKeys' + class Function(object): """A Function defined in the API. @@ -340,12 +339,8 @@ class Function(object): - |returns| the return type of the function; None if the function does not return a value """ - def __init__(self, - parent, - name, - json, - namespace, - origin): + + def __init__(self, parent, name, json, namespace, origin): self.name = name self.simple_name = _StripNamespace(self.name, namespace) self.platforms = _GetPlatforms(json) @@ -367,8 +362,10 @@ class Function(object): def GeneratePropertyFromParam(p): return Property(self, p['name'], p, namespace, origin) - self.filters = [GeneratePropertyFromParam(filter_instance) - for filter_instance in json.get('filters', [])] + self.filters = [ + GeneratePropertyFromParam(filter_instance) + for filter_instance in json.get('filters', []) + ] # Any asynchronous return should be defined using the returns_async field. returns_async = json.get('returns_async', None) @@ -383,14 +380,11 @@ class Function(object): # incompatible with returning a promise. There are APIs that specify this, # though, so we make sure they have specified does_not_support_promises if # they do. - if ( - json.get('returns') is not None - and self.returns_async.can_return_promise - ): + if (json.get('returns') is not None + and self.returns_async.can_return_promise): raise ValueError( 'Cannot specify both returns and returns_async on a function ' - 'which supports promies: %s.%s' % (namespace.name, name) - ) + 'which supports promies: %s.%s' % (namespace.name, name)) params = json.get('parameters', []) for i, param in enumerate(params): @@ -398,11 +392,8 @@ class Function(object): self.returns = None if 'returns' in json: - self.returns = Type(self, - '%sReturnType' % name, - json['returns'], - namespace, - origin) + self.returns = Type(self, '%sReturnType' % name, json['returns'], + namespace, origin) class ReturnsAsync(object): @@ -423,6 +414,7 @@ class ReturnsAsync(object): callback. Currently only consumed for documentation purposes """ + def __init__(self, parent, json, namespace, origin): self.name = json.get('name') self.simple_name = _StripNamespace(self.name, namespace) @@ -434,25 +426,21 @@ class ReturnsAsync(object): if json.get('returns') is not None: raise ValueError( - 'Cannot return a value from an asynchronous return: %s.%s in %s' - % (namespace.name, parent.name, namespace.source_file) - ) + 'Cannot return a value from an asynchronous return: %s.%s in %s' % + (namespace.name, parent.name, namespace.source_file)) if json.get('deprecated') is not None: raise ValueError( - 'Cannot specify deprecated on an asynchronous return: %s.%s in %s' - % (namespace.name, parent.name, namespace.source_file) - ) + 'Cannot specify deprecated on an asynchronous return: %s.%s in %s' % + (namespace.name, parent.name, namespace.source_file)) if json.get('parameters') is None: raise ValueError( - 'parameters key not specified on returns_async: %s.%s in %s' - % (namespace.name, parent.name, namespace.source_file) - ) + 'parameters key not specified on returns_async: %s.%s in %s' % + (namespace.name, parent.name, namespace.source_file)) if len(json.get('parameters')) > 1 and self.can_return_promise: raise ValueError( 'Only a single parameter can be specific on a returns_async which' - ' supports promises: %s.%s in %s' - % (namespace.name, parent.name, namespace.source_file) - ) + ' supports promises: %s.%s in %s' % + (namespace.name, parent.name, namespace.source_file)) def GeneratePropertyFromParam(p): return Property(self, p['name'], p, namespace, origin) @@ -475,6 +463,7 @@ class Property(object): - |simple_name| the name of this Property without a namespace - |deprecated| a reason and possible alternative for a deprecated property """ + def __init__(self, parent, name, json, namespace, origin): """Creates a Property from JSON. """ @@ -491,11 +480,10 @@ class Property(object): self.nodoc = json.get('nodoc', False) # HACK: only support very specific value types. - is_allowed_value = ( - '$ref' not in json and - ('type' not in json or json['type'] == 'integer' - or json['type'] == 'number' - or json['type'] == 'string')) + is_allowed_value = ('$ref' not in json + and ('type' not in json or json['type'] == 'integer' + or json['type'] == 'number' + or json['type'] == 'string')) self.value = None if 'value' in json and is_allowed_value: @@ -532,20 +520,21 @@ class Property(object): if unix_name == self._unix_name: return if self._unix_name_used: - raise AttributeError( - 'Cannot set the unix_name on %s; ' - 'it is already used elsewhere as %s' % - (self.name, self._unix_name)) + raise AttributeError('Cannot set the unix_name on %s; ' + 'it is already used elsewhere as %s' % + (self.name, self._unix_name)) self._unix_name = unix_name unix_name = property(GetUnixName, SetUnixName) + class EnumValue(object): """A single value from an enum. Properties: - |name| name of the property as in the json. - |description| a description of the property (if provided) """ + def __init__(self, json, namespace): if isinstance(json, dict): self.name = json['name'] @@ -556,17 +545,18 @@ class EnumValue(object): # Using empty string values as enum key is only allowed in a few namespaces, # as an exception to the rule, and we should not add more. - if (not self.name and - namespace.name not in ['enums', 'webstorePrivate']): + if (not self.name and namespace.name not in ['enums', 'webstorePrivate']): raise ValueError('Enum value cannot be an empty string') def CamelName(self): return CamelName(self.name) + class _Enum(object): """Superclass for enum types with a "name" field, setting up repr/eq/ne. Enums need to do this so that equality/non-equality work over pickling. """ + @staticmethod def GetAll(cls): """Yields all _Enum objects declared in |cls|. @@ -581,6 +571,7 @@ class _Enum(object): def __eq__(self, other): return type(other) == type(self) and other.name == self.name + def __ne__(self, other): return not (self == other) @@ -595,6 +586,7 @@ class _Enum(object): class _PropertyTypeInfo(_Enum): + def __init__(self, is_fundamental, name): _Enum.__init__(self, name) self.is_fundamental = is_fundamental @@ -602,6 +594,7 @@ class _PropertyTypeInfo(_Enum): def __repr__(self): return self.name + class PropertyType(object): """Enum of different types of properties/parameters. """ @@ -619,111 +612,113 @@ class PropertyType(object): REF = _PropertyTypeInfo(False, "ref") STRING = _PropertyTypeInfo(True, "string") + def IsCPlusPlusKeyword(name): """Returns true if `name` is a C++ reserved keyword. """ # Obtained from https://en.cppreference.com/w/cpp/keyword. keywords = { - "alignas", - "alignof", - "and", - "and_eq", - "asm", - "atomic_cancel", - "atomic_commit", - "atomic_noexcept", - "auto", - "bitand", - "bitor", - "bool", - "break", - "case", - "catch", - "char", - "char8_t", - "char16_t", - "char32_t", - "class", - "compl", - "concept", - "const", - "consteval", - "constexpr", - "constinit", - "const_cast", - "continue", - "co_await", - "co_return", - "co_yield", - "decltype", - "default", - "delete", - "do", - "double", - "dynamic_cast", - "else", - "enum", - "explicit", - "export", - "extern", - "false", - "float", - "for", - "friend", - "goto", - "if", - "inline", - "int", - "long", - "mutable", - "namespace", - "new", - "noexcept", - "not", - "not_eq", - "nullptr", - "operator", - "or", - "or_eq", - "private", - "protected", - "public", - "reflexpr", - "register", - "reinterpret_cast", - "requires", - "return", - "short", - "signed", - "sizeof", - "static", - "static_assert", - "static_cast", - "struct", - "switch", - "synchronized", - "template", - "this", - "thread_local", - "throw", - "true", - "try", - "typedef", - "typeid", - "typename", - "union", - "unsigned", - "using", - "virtual", - "void", - "volatile", - "wchar_t", - "while", - "xor", - "xor_eq" + "alignas", + "alignof", + "and", + "and_eq", + "asm", + "atomic_cancel", + "atomic_commit", + "atomic_noexcept", + "auto", + "bitand", + "bitor", + "bool", + "break", + "case", + "catch", + "char", + "char8_t", + "char16_t", + "char32_t", + "class", + "compl", + "concept", + "const", + "consteval", + "constexpr", + "constinit", + "const_cast", + "continue", + "co_await", + "co_return", + "co_yield", + "decltype", + "default", + "delete", + "do", + "double", + "dynamic_cast", + "else", + "enum", + "explicit", + "export", + "extern", + "false", + "float", + "for", + "friend", + "goto", + "if", + "inline", + "int", + "long", + "mutable", + "namespace", + "new", + "noexcept", + "not", + "not_eq", + "nullptr", + "operator", + "or", + "or_eq", + "private", + "protected", + "public", + "reflexpr", + "register", + "reinterpret_cast", + "requires", + "return", + "short", + "signed", + "sizeof", + "static", + "static_assert", + "static_cast", + "struct", + "switch", + "synchronized", + "template", + "this", + "thread_local", + "throw", + "true", + "try", + "typedef", + "typeid", + "typename", + "union", + "unsigned", + "using", + "virtual", + "void", + "volatile", + "wchar_t", + "while", + "xor", + "xor_eq", } return name in keywords + @memoize def UnixName(name): '''Returns the unix_style name for a given lowerCamelCase string. @@ -740,7 +735,7 @@ def UnixName(name): # Prepend an extra underscore to the |name|'s start if it doesn't start with a # letter or underscore to ensure the generated unix name follows C++ # identifier rules. - assert(name) + assert (name) if name[0].isdigit(): name = '_' + name @@ -811,10 +806,7 @@ def _GetFunctions(parent, json, namespace): """ functions = OrderedDict() for function_json in json.get('functions', []): - function = Function(parent, - function_json['name'], - function_json, - namespace, + function = Function(parent, function_json['name'], function_json, namespace, Origin(from_json=True)) functions[function.name] = function return functions @@ -825,10 +817,7 @@ def _GetEvents(parent, json, namespace): """ events = OrderedDict() for event_json in json.get('events', []): - event = Function(parent, - event_json['name'], - event_json, - namespace, + event = Function(parent, event_json['name'], event_json, namespace, Origin(from_client=True)) events[event.name] = event return events @@ -853,12 +842,13 @@ def _GetManifestKeysType(self, json): # Create a dummy object to parse "manifest_keys" as a type. manifest_keys_type = { - 'type': 'object', - 'properties': json['manifest_keys'], + 'type': 'object', + 'properties': json['manifest_keys'], } return Type(self, 'ManifestKeys', manifest_keys_type, self, Origin(from_manifest_keys=True)) + def _GetWithDefaultChecked(self, json, key, default): if json.get(key) == default: raise ParseException( @@ -867,7 +857,9 @@ def _GetWithDefaultChecked(self, json, key, default): % (key, default)) return json.get(key, default) + class _PlatformInfo(_Enum): + def __init__(self, name): _Enum.__init__(self, name) diff --git a/tools/json_schema_compiler/model_test.py b/tools/json_schema_compiler/model_test.py index 7fb84006f5dde..a0c8e55914a22 100755 --- a/tools/json_schema_compiler/model_test.py +++ b/tools/json_schema_compiler/model_test.py @@ -9,63 +9,62 @@ from model import Platforms import model import unittest + class ModelTest(unittest.TestCase): + def setUp(self): self.model = model.Model() self.permissions_json = CachedLoad('test/permissions.json') self.model.AddNamespace(self.permissions_json[0], - 'path/to/permissions.json') + 'path/to/permissions.json') self.permissions = self.model.namespaces.get('permissions') self.windows_json = CachedLoad('test/windows.json') - self.model.AddNamespace(self.windows_json[0], - 'path/to/window.json') + self.model.AddNamespace(self.windows_json[0], 'path/to/window.json') self.windows = self.model.namespaces.get('windows') self.tabs_json = CachedLoad('test/tabs.json') - self.model.AddNamespace(self.tabs_json[0], - 'path/to/tabs.json') + self.model.AddNamespace(self.tabs_json[0], 'path/to/tabs.json') self.tabs = self.model.namespaces.get('tabs') self.idl_chromeos = Load('test/idl_namespace_chromeos.idl') self.model.AddNamespace(self.idl_chromeos[0], - 'path/to/idl_namespace_chromeos.idl') + 'path/to/idl_namespace_chromeos.idl') self.idl_namespace_chromeos = self.model.namespaces.get( 'idl_namespace_chromeos') self.idl_all_platforms = Load('test/idl_namespace_all_platforms.idl') self.model.AddNamespace(self.idl_all_platforms[0], - 'path/to/idl_namespace_all_platforms.idl') + 'path/to/idl_namespace_all_platforms.idl') self.idl_namespace_all_platforms = self.model.namespaces.get( 'idl_namespace_all_platforms') self.idl_non_specific_platforms = Load( 'test/idl_namespace_non_specific_platforms.idl') self.model.AddNamespace(self.idl_non_specific_platforms[0], - 'path/to/idl_namespace_non_specific_platforms.idl') + 'path/to/idl_namespace_non_specific_platforms.idl') self.idl_namespace_non_specific_platforms = self.model.namespaces.get( 'idl_namespace_non_specific_platforms') self.returns_async_json = CachedLoad('test/returns_async.json') self.model.AddNamespace(self.returns_async_json[0], - 'path/to/returns_async.json') + 'path/to/returns_async.json') self.returns_async = self.model.namespaces.get('returns_async') self.idl_returns_async_idl = Load('test/idl_returns_async.idl') self.model.AddNamespace(self.idl_returns_async_idl[0], - 'path/to/idl_returns_async.idl') + 'path/to/idl_returns_async.idl') self.idl_returns_async = self.model.namespaces.get('idl_returns_async') self.nodoc_json = CachedLoad('test/namespace_nodoc.json') - self.model.AddNamespace(self.nodoc_json[0], - 'path/to/namespace_nodoc.json') + self.model.AddNamespace(self.nodoc_json[0], 'path/to/namespace_nodoc.json') self.nodoc = self.model.namespaces.get('nodoc') self.fakeapi_json = CachedLoad('test/namespace_fakeapi.json') self.model.AddNamespace(self.fakeapi_json[0], - 'path/to/namespace_fakeapi.json') + 'path/to/namespace_fakeapi.json') self.fakeapi = self.model.namespaces.get('fakeapi') self.function_platforms_idl = Load('test/function_platforms.idl') self.model.AddNamespace(self.function_platforms_idl[0], - '/path/to/function_platforms.idl') + '/path/to/function_platforms.idl') self.function_platforms = self.model.namespaces.get('function_platforms') self.function_platform_win_linux_json = CachedLoad( 'test/function_platform_win_linux.json') self.model.AddNamespace(self.function_platform_win_linux_json[0], - 'path/to/function_platform_win_linux.json') + 'path/to/function_platform_win_linux.json') self.function_platform_win_linux = self.model.namespaces.get( 'function_platform_win_linux') @@ -75,7 +74,7 @@ class ModelTest(unittest.TestCase): def testHasFunctions(self): self.assertEqual(["contains", "getAll", "remove", "request"], - sorted(self.permissions.functions.keys())) + sorted(self.permissions.functions.keys())) def testHasTypes(self): self.assertEqual(['Tab'], list(self.tabs.types.keys())) @@ -83,41 +82,38 @@ class ModelTest(unittest.TestCase): self.assertEqual(['Window'], list(self.windows.types.keys())) def testHasProperties(self): - self.assertEqual(["active", "favIconUrl", "highlighted", "id", - "incognito", "index", "pinned", "selected", "status", "title", "url", - "windowId"], - sorted(self.tabs.types['Tab'].properties.keys())) + self.assertEqual([ + "active", "favIconUrl", "highlighted", "id", "incognito", "index", + "pinned", "selected", "status", "title", "url", "windowId" + ], sorted(self.tabs.types['Tab'].properties.keys())) def testProperties(self): string_prop = self.tabs.types['Tab'].properties['status'] - self.assertEqual(model.PropertyType.STRING, - string_prop.type_.property_type) + self.assertEqual(model.PropertyType.STRING, string_prop.type_.property_type) integer_prop = self.tabs.types['Tab'].properties['id'] self.assertEqual(model.PropertyType.INTEGER, - integer_prop.type_.property_type) + integer_prop.type_.property_type) array_prop = self.windows.types['Window'].properties['tabs'] - self.assertEqual(model.PropertyType.ARRAY, - array_prop.type_.property_type) + self.assertEqual(model.PropertyType.ARRAY, array_prop.type_.property_type) self.assertEqual(model.PropertyType.REF, - array_prop.type_.item_type.property_type) + array_prop.type_.item_type.property_type) self.assertEqual('tabs.Tab', array_prop.type_.item_type.ref_type) object_prop = self.tabs.functions['query'].params[0] - self.assertEqual(model.PropertyType.OBJECT, - object_prop.type_.property_type) - self.assertEqual( - ["active", "highlighted", "pinned", "status", "title", "url", - "windowId", "windowType"], - sorted(object_prop.type_.properties.keys())) + self.assertEqual(model.PropertyType.OBJECT, object_prop.type_.property_type) + self.assertEqual([ + "active", "highlighted", "pinned", "status", "title", "url", "windowId", + "windowType" + ], sorted(object_prop.type_.properties.keys())) def testChoices(self): self.assertEqual(model.PropertyType.CHOICES, - self.tabs.functions['move'].params[0].type_.property_type) + self.tabs.functions['move'].params[0].type_.property_type) def testPropertyNotImplemented(self): - (self.permissions_json[0]['types'][0] - ['properties']['permissions']['type']) = 'something' + (self.permissions_json[0]['types'][0]['properties']['permissions']['type'] + ) = 'something' self.assertRaises(model.ParseException, self.model.AddNamespace, - self.permissions_json[0], 'path/to/something.json') + self.permissions_json[0], 'path/to/something.json') def testDefaultSpecifiedRedundantly(self): test_json = CachedLoad('test/redundant_default_attribute.json') @@ -127,20 +123,16 @@ class ModelTest(unittest.TestCase): ' in path/to/redundant_default_attribute.json\n' 'The attribute "optional" is specified as "False", but this is the ' 'default value if the attribute is not included\. It should be ' - 'removed\.', - self.model.AddNamespace, - test_json[0], + 'removed\.', self.model.AddNamespace, test_json[0], 'path/to/redundant_default_attribute.json') def testReturnsAsyncMissingParametersKey(self): test_json = CachedLoad('test/returns_async_missing_parameters_key.json') self.assertRaisesRegex( - ValueError, - 'parameters key not specified on returns_async: ' + ValueError, 'parameters key not specified on returns_async: ' 'returnsAsyncMissingParametersKey.asyncNoParametersKey in ' 'path/to/returns_async_missing_parameters_key.json', - self.model.AddNamespace, - test_json[0], + self.model.AddNamespace, test_json[0], 'path/to/returns_async_missing_parameters_key.json') def testDescription(self): @@ -168,45 +160,44 @@ class ModelTest(unittest.TestCase): def testUnixName(self): expectations = { - 'foo': 'foo', - 'fooBar': 'foo_bar', - 'fooBarBaz': 'foo_bar_baz', - 'fooBARBaz': 'foo_bar_baz', - 'fooBAR': 'foo_bar', - 'FOO': 'foo', - 'FOOBar': 'foo_bar', - 'foo.bar': 'foo_bar', - 'foo.BAR': 'foo_bar', - 'foo.barBAZ': 'foo_bar_baz', - 'foo_Bar_Baz_box': 'foo_bar_baz_box', - } + 'foo': 'foo', + 'fooBar': 'foo_bar', + 'fooBarBaz': 'foo_bar_baz', + 'fooBARBaz': 'foo_bar_baz', + 'fooBAR': 'foo_bar', + 'FOO': 'foo', + 'FOOBar': 'foo_bar', + 'foo.bar': 'foo_bar', + 'foo.BAR': 'foo_bar', + 'foo.barBAZ': 'foo_bar_baz', + 'foo_Bar_Baz_box': 'foo_bar_baz_box', + } for name in expectations: self.assertEqual(expectations[name], model.UnixName(name)) def testCamelName(self): expectations = { - 'foo': 'foo', - 'fooBar': 'fooBar', - 'foo_bar_baz': 'fooBarBaz', - 'FOO_BAR': 'FOOBar', - 'FOO_bar': 'FOOBar', - '_bar': 'Bar', - '_bar_baz': 'BarBaz', - 'bar_': 'bar', - 'bar_baz_': 'barBaz', - } + 'foo': 'foo', + 'fooBar': 'fooBar', + 'foo_bar_baz': 'fooBarBaz', + 'FOO_BAR': 'FOOBar', + 'FOO_bar': 'FOOBar', + '_bar': 'Bar', + '_bar_baz': 'BarBaz', + 'bar_': 'bar', + 'bar_baz_': 'barBaz', + } for testcase, expected in expectations.items(): self.assertEqual(expected, model.CamelName(testcase)) def testPlatforms(self): self.assertEqual([Platforms.CHROMEOS], self.idl_namespace_chromeos.platforms) - self.assertEqual( - [Platforms.CHROMEOS, Platforms.FUCHSIA, Platforms.LINUX, Platforms.MAC, - Platforms.WIN], - self.idl_namespace_all_platforms.platforms) - self.assertEqual(None, - self.idl_namespace_non_specific_platforms.platforms) + self.assertEqual([ + Platforms.CHROMEOS, Platforms.FUCHSIA, Platforms.LINUX, Platforms.MAC, + Platforms.WIN + ], self.idl_namespace_all_platforms.platforms) + self.assertEqual(None, self.idl_namespace_non_specific_platforms.platforms) def testInvalidNamespacePlatform(self): invalid_namespace_platform = Load('test/invalid_platform_namespace.idl') @@ -225,7 +216,7 @@ class ModelTest(unittest.TestCase): def testPlatformsOnFunctionsIDL(self): function_win_linux = self.function_platforms.functions['function_win_linux'] self.assertEqual([Platforms.WIN, Platforms.LINUX], - function_win_linux.platforms) + function_win_linux.platforms) function_all = self.function_platforms.functions['function_all'] self.assertIsNone(function_all.platforms) @@ -269,5 +260,6 @@ class ModelTest(unittest.TestCase): self.assertIn('Enum value cannot be an empty string', str(context.exception)) + if __name__ == '__main__': unittest.main() diff --git a/tools/json_schema_compiler/namespace_resolver.py b/tools/json_schema_compiler/namespace_resolver.py index 7409796921c2a..7b8eb5c3fd827 100644 --- a/tools/json_schema_compiler/namespace_resolver.py +++ b/tools/json_schema_compiler/namespace_resolver.py @@ -18,7 +18,7 @@ def _GenerateFilenames(full_namespace): # 4. sub_name_space.idl, # 5. etc. sub_namespaces = full_namespace.split('.') - filenames = [ ] + filenames = [] basename = None for namespace in reversed(sub_namespaces): if basename is not None: @@ -39,6 +39,7 @@ class NamespaceResolver(object): used when searching for types. - |cpp_namespace_pattern| Default namespace pattern ''' + def __init__(self, root, path, include_rules, cpp_namespace_pattern): self._root = root self._include_rules = [(path, cpp_namespace_pattern)] + include_rules @@ -53,13 +54,12 @@ class NamespaceResolver(object): if cpp_namespace: cpp_namespace_environment = CppNamespaceEnvironment(cpp_namespace) for filename in reversed(filenames): - filepath = os.path.join(path, filename); + filepath = os.path.join(path, filename) if os.path.exists(os.path.join(self._root, filepath)): schema = SchemaLoader(self._root).LoadSchema(filepath)[0] - return Model().AddNamespace( - schema, - filepath, - environment=cpp_namespace_environment) + return Model().AddNamespace(schema, + filepath, + environment=cpp_namespace_environment) return None def ResolveType(self, full_name, default_namespace): diff --git a/tools/json_schema_compiler/preview.py b/tools/json_schema_compiler/preview.py index db8c820b87973..fe6cbf504b30b 100755 --- a/tools/json_schema_compiler/preview.py +++ b/tools/json_schema_compiler/preview.py @@ -17,8 +17,8 @@ import optparse import os import shlex import urlparse -from highlighters import ( - pygments_highlighter, none_highlighter, hilite_me_highlighter) +from highlighters import (pygments_highlighter, none_highlighter, + hilite_me_highlighter) from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer from cpp_namespace_environment import CppNamespaceEnvironment from namespace_resolver import NamespaceResolver @@ -27,6 +27,7 @@ from namespace_resolver import NamespaceResolver class CompilerHandler(BaseHTTPRequestHandler): """A HTTPRequestHandler that outputs the result of tools/json_schema_compiler. """ + def do_GET(self): parsed_url = urlparse.urlparse(self.path) request_path = self._GetRequestPath(parsed_url) @@ -64,38 +65,36 @@ class CompilerHandler(BaseHTTPRequestHandler): Code panes are populated via XHR after links in the nav pane are clicked. """ - (head.Append('<style>') - .Append('body {') - .Append(' margin: 0;') - .Append('}') - .Append('.pane {') - .Append(' height: 100%;') - .Append(' overflow-x: auto;') - .Append(' overflow-y: scroll;') - .Append(' display: inline-block;') - .Append('}') - .Append('#nav_pane {') - .Append(' width: 20%;') - .Append('}') - .Append('#nav_pane ul {') - .Append(' list-style-type: none;') - .Append(' padding: 0 0 0 1em;') - .Append('}') - .Append('#cc_pane {') - .Append(' width: 40%;') - .Append('}') - .Append('#h_pane {') - .Append(' width: 40%;') - .Append('}') + (head.Append('<style>') \ + .Append('body {') \ + .Append(' margin: 0;') \ + .Append('}') \ + .Append('.pane {') \ + .Append(' height: 100%;') \ + .Append(' overflow-x: auto;') \ + .Append(' overflow-y: scroll;') \ + .Append(' display: inline-block;') \ + .Append('}') \ + .Append('#nav_pane {') \ + .Append(' width: 20%;') \ + .Append('}') \ + .Append('#nav_pane ul {') \ + .Append(' list-style-type: none;') \ + .Append(' padding: 0 0 0 1em;') \ + .Append('}') \ + .Append('#cc_pane {') \ + .Append(' width: 40%;') \ + .Append('}') \ + .Append('#h_pane {') \ + .Append(' width: 40%;') \ + .Append('}') \ .Append('</style>') ) - body.Append( - '<div class="pane" id="nav_pane">%s</div>' - '<div class="pane" id="h_pane"></div>' - '<div class="pane" id="cc_pane"></div>' % - self._RenderNavPane(parsed_url.path[1:]) - ) + body.Append('<div class="pane" id="nav_pane">%s</div>' + '<div class="pane" id="h_pane"></div>' + '<div class="pane" id="cc_pane"></div>' % + self._RenderNavPane(parsed_url.path[1:])) # The Javascript that interacts with the nav pane and panes to show the # compiled files as the URL or highlighting options change. @@ -187,41 +186,39 @@ updateEverything(); (file_root, file_ext) = os.path.splitext(request_path) (filedir, filename) = os.path.split(file_root) - namespace_resolver = NamespaceResolver("./", - filedir, + namespace_resolver = NamespaceResolver("./", filedir, self.server.include_rules, self.server.cpp_namespace_pattern) try: # Get main file. namespace = namespace_resolver.ResolveNamespace(filename) type_generator = cpp_type_generator.CppTypeGenerator( - api_model, - namespace_resolver, - namespace) + api_model, namespace_resolver, namespace) # Generate code if file_ext == '.h': - cpp_code = (h_generator.HGenerator(type_generator) - .Generate(namespace).Render()) + cpp_code = ( + h_generator.HGenerator(type_generator).Generate(namespace).Render()) elif file_ext == '.cc': - cpp_code = (cc_generator.CCGenerator(type_generator) - .Generate(namespace).Render()) + cpp_code = (cc_generator.CCGenerator(type_generator).Generate( + namespace).Render()) else: self.send_error(404, "File not found: %s" % request_path) return # Do highlighting on the generated code (highlighter_param, style_param) = self._GetHighlighterParams(parsed_url) - head.Append('<style>' + + head.Append( + '<style>' + self.server.highlighters[highlighter_param].GetCSS(style_param) + '</style>') - body.Append(self.server.highlighters[highlighter_param] - .GetCodeElement(cpp_code, style_param)) + body.Append(self.server.highlighters[highlighter_param].GetCodeElement( + cpp_code, style_param)) except IOError: self.send_error(404, "File not found: %s" % request_path) return - except (TypeError, KeyError, AttributeError, - AssertionError, NotImplementedError) as error: + except (TypeError, KeyError, AttributeError, AssertionError, + NotImplementedError) as error: body.Append('<pre>') body.Append('compiler error: %s' % error) body.Append('Check server log for more details') @@ -233,7 +230,7 @@ updateEverything(); """ query_dict = urlparse.parse_qs(parsed_url.query) return (query_dict.get('highlighter', ['pygments'])[0], - query_dict.get('style', ['colorful'])[0]) + query_dict.get('style', ['colorful'])[0]) def _RenderNavPane(self, path): """Renders an HTML nav pane. @@ -248,7 +245,7 @@ updateEverything(); html.Append('<select id="highlighters" onChange="updateEverything()">') for name, highlighter in self.server.highlighters.items(): html.Append('<option value="%s">%s</option>' % - (name, highlighter.DisplayName())) + (name, highlighter.DisplayName())) html.Append('</select>') html.Append('<br/>') @@ -289,8 +286,7 @@ updateEverything(); html.Append('<li><a href="/%s/">%s/</a>' % (full_path, filename)) elif file_ext in ['.json', '.idl']: # cc/h panes will automatically update via the hash change event. - html.Append('<li><a href="#%s">%s</a>' % - (filename, filename)) + html.Append('<li><a href="#%s">%s</a>' % (filename, filename)) html.Append('</ul>') @@ -298,11 +294,8 @@ updateEverything(); class PreviewHTTPServer(HTTPServer, object): - def __init__(self, - server_address, - handler, - highlighters, - include_rules, + + def __init__(self, server_address, handler, highlighters, include_rules, cpp_namespace_pattern): super(PreviewHTTPServer, self).__init__(server_address, handler) self.highlighters = highlighters @@ -314,11 +307,18 @@ if __name__ == '__main__': parser = optparse.OptionParser( description='Runs a server to preview the json_schema_compiler output.', usage='usage: %prog [option]...') - parser.add_option('-p', '--port', default='8000', - help='port to run the server on') - parser.add_option('-n', '--namespace', default='generated_api_schemas', + parser.add_option('-p', + '--port', + default='8000', + help='port to run the server on') + parser.add_option( + '-n', + '--namespace', + default='generated_api_schemas', help='C++ namespace for generated files. e.g extensions::api.') - parser.add_option('-I', '--include-rules', + parser.add_option( + '-I', + '--include-rules', help='A list of paths to include when searching for referenced objects,' ' with the namespace separated by a \':\'. Example: ' '/foo/bar:Foo::Bar::%(namespace)s') @@ -344,19 +344,16 @@ if __name__ == '__main__': print('') highlighters = { - 'hilite': hilite_me_highlighter.HiliteMeHighlighter(), - 'none': none_highlighter.NoneHighlighter() + 'hilite': hilite_me_highlighter.HiliteMeHighlighter(), + 'none': none_highlighter.NoneHighlighter() } try: highlighters['pygments'] = pygments_highlighter.PygmentsHighlighter() except ImportError as e: pass - server = PreviewHTTPServer(('', int(opts.port)), - CompilerHandler, - highlighters, - include_rules, - opts.namespace) + server = PreviewHTTPServer(('', int(opts.port)), CompilerHandler, + highlighters, include_rules, opts.namespace) server.serve_forever() except KeyboardInterrupt: server.socket.close() diff --git a/tools/json_schema_compiler/schema_loader.py b/tools/json_schema_compiler/schema_loader.py index 0478250872f93..eccbc341bc56f 100644 --- a/tools/json_schema_compiler/schema_loader.py +++ b/tools/json_schema_compiler/schema_loader.py @@ -8,10 +8,12 @@ import sys import idl_schema import json_schema + class SchemaLoader(object): '''Loads a schema from a provided filename. |root|: path to the root directory. ''' + def __init__(self, root): self._root = root diff --git a/tools/json_schema_compiler/schema_util.py b/tools/json_schema_compiler/schema_util.py index 4ea25ee6f7d06..b6285107dc97b 100644 --- a/tools/json_schema_compiler/schema_util.py +++ b/tools/json_schema_compiler/schema_util.py @@ -4,6 +4,7 @@ """Utilies for the processing of schema python structures. """ + def CapitalizeFirstLetter(value): return value[0].capitalize() + value[1:] diff --git a/tools/json_schema_compiler/schema_util_test.py b/tools/json_schema_compiler/schema_util_test.py index 594d4d88d73a7..35a7e943ca08f 100755 --- a/tools/json_schema_compiler/schema_util_test.py +++ b/tools/json_schema_compiler/schema_util_test.py @@ -7,7 +7,9 @@ from schema_util import JsFunctionNameToClassName from schema_util import StripNamespace import unittest + class SchemaUtilTest(unittest.TestCase): + def testStripNamespace(self): self.assertEqual('Bar', StripNamespace('foo.Bar')) self.assertEqual('Baz', StripNamespace('Baz')) @@ -15,11 +17,11 @@ class SchemaUtilTest(unittest.TestCase): def testJsFunctionNameToClassName(self): self.assertEqual('FooBar', JsFunctionNameToClassName('foo', 'bar')) self.assertEqual('FooBar', - JsFunctionNameToClassName('experimental.foo', 'bar')) + JsFunctionNameToClassName('experimental.foo', 'bar')) + self.assertEqual('FooBarBaz', JsFunctionNameToClassName('foo.bar', 'baz')) self.assertEqual('FooBarBaz', - JsFunctionNameToClassName('foo.bar', 'baz')) - self.assertEqual('FooBarBaz', - JsFunctionNameToClassName('experimental.foo.bar', 'baz')) + JsFunctionNameToClassName('experimental.foo.bar', 'baz')) + if __name__ == '__main__': unittest.main() diff --git a/tools/json_schema_compiler/ts_definition_generator.py b/tools/json_schema_compiler/ts_definition_generator.py index 6aa73d6751471..145182de53c3d 100644 --- a/tools/json_schema_compiler/ts_definition_generator.py +++ b/tools/json_schema_compiler/ts_definition_generator.py @@ -1,7 +1,6 @@ # Copyright 2023 The Chromium Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. - """Generator that produces a definition file for typescript. Note: This is a work in progress, and generated definitions may need tweaking. @@ -19,7 +18,6 @@ from js_util import JsUtil from model import * from schema_util import * - CHROMIUM_SRC = os.path.abspath( os.path.join(os.path.dirname(__file__), "..", "..")) @@ -59,8 +57,7 @@ class _Generator(object): # If events are needed, add the import. if self._events_required: main_code.Substitute( - {"imports": "import {ChromeEvent} from './chrome_event.js';"} - ) + {"imports": "import {ChromeEvent} from './chrome_event.js';"}) else: main_code.Substitute({"imports": ""}) main_code = self._ClangFormat(main_code) @@ -111,10 +108,8 @@ class _Generator(object): type_name = self._ExtractType(prop.type_) # If the ref type has additional properties, do a namespace merge. prop_type: Type = prop.type_ - if ( - len(prop_type.properties) > 0 - and prop_type.property_type == PropertyType.REF - ): + if (len(prop_type.properties) > 0 + and prop_type.property_type == PropertyType.REF): type_name = self._AppendInterfaceForProperty(c, prop, type_name) c.Append(f"export const {prop.name}: {type_name};") c.Append() @@ -143,9 +138,8 @@ class _Generator(object): # This appends an local only interface to allow for additional # properties on an already defined type. - def _AppendInterfaceForProperty( - self, c: Code, prop: Property, prop_type_name - ): + def _AppendInterfaceForProperty(self, c: Code, prop: Property, + prop_type_name): if prop.deprecated: return prop_type = prop.type_ @@ -177,16 +171,15 @@ class _Generator(object): # Type alias c.Append(f"export type {type.name} = {type.property_type.name};") c.Append() - elif (type.property_type is PropertyType.ARRAY or - type.property_type is PropertyType.CHOICES) : + elif (type.property_type is PropertyType.ARRAY + or type.property_type is PropertyType.CHOICES): ts_type = self._ExtractType(type) c.Append(f"export type {type.name} = {ts_type};") c.Append() else: # Adding this for things we may not have accounted for here. c.Append( - f"// TODO({os.getlogin()}) -- {type.name}: {type.property_type.name}" - ) + f"// TODO({os.getlogin()}) -- {type.name}: {type.property_type.name}") def _AppendInterface(self, c: Code, interface: Type): c.Sblock(f"export interface {interface.name} {{") @@ -231,8 +224,8 @@ class _Generator(object): ret_type = "void" if func.returns is not None: ret_type = self._ExtractType(func.returns) - elif (func.returns_async is not None and - func.returns_async.can_return_promise): + elif (func.returns_async is not None + and func.returns_async.can_return_promise): ret_type = f"Promise<{self._ExtractPromiseType(func.returns_async)}>" return ret_type @@ -328,9 +321,8 @@ class _Generator(object): # When the return async isn't a promise, we append it as a return callback # at the end of the parameters. - use_callback = ( - func.returns_async and not func.returns_async.can_return_promise - ) + use_callback = (func.returns_async + and not func.returns_async.can_return_promise) if use_callback: callback_params = self._ExtractParams(func.returns_async.params) if param_str: @@ -378,24 +370,21 @@ class _Generator(object): def _ClangFormat(self, c: Code, level=0): # temp = tempfile.NamedTemporaryFile("w", encoding="utf-8", suffix=".js") # f_name = temp.name - with tempfile.NamedTemporaryFile( - "w", encoding="utf-8", suffix=".js", delete=False - ) as f: + with tempfile.NamedTemporaryFile("w", + encoding="utf-8", + suffix=".js", + delete=False) as f: f.write(c.Render()) f_name = f.name script_path = self._GetChromiumClangFormatScriptPath() style_path = self._GetChromiumClangFormatStylePath() - cmd = ( - f'python3 {script_path} --fallback-style=none ' - f'--style=file:{style_path} "{f_name}"' - ) - p = subprocess.Popen( - cmd, - cwd=CHROMIUM_SRC, - encoding="utf-8", - shell=True, - stdout=subprocess.PIPE - ) + cmd = (f'python3 {script_path} --fallback-style=none ' + f'--style=file:{style_path} "{f_name}"') + p = subprocess.Popen(cmd, + cwd=CHROMIUM_SRC, + encoding="utf-8", + shell=True, + stdout=subprocess.PIPE) out = p.communicate()[0] out_code = Code() out_code.Append(out) diff --git a/tools/json_schema_compiler/ts_definition_generator_test.py b/tools/json_schema_compiler/ts_definition_generator_test.py index 3deae3ed0f6ed..ac773576cbfa2 100755 --- a/tools/json_schema_compiler/ts_definition_generator_test.py +++ b/tools/json_schema_compiler/ts_definition_generator_test.py @@ -17,11 +17,8 @@ class TsDefinitionGeneratorTest(unittest.TestCase): def _GetNamespace(self, fake_content, filename): """Returns a namespace object for the given content""" is_idl = filename.endswith('.idl') - api_def = ( - idl_schema.Process(fake_content, filename) - if is_idl - else json_parse.Parse(fake_content) - ) + api_def = (idl_schema.Process(fake_content, filename) + if is_idl else json_parse.Parse(fake_content)) m = model.Model() return m.AddNamespace(api_def[0], filename) diff --git a/tools/json_schema_compiler/util_cc_helper.py b/tools/json_schema_compiler/util_cc_helper.py index 181f4d4dbbc51..0b4914ba03d08 100644 --- a/tools/json_schema_compiler/util_cc_helper.py +++ b/tools/json_schema_compiler/util_cc_helper.py @@ -9,14 +9,15 @@ class UtilCCHelper(object): """A util class that generates code that uses tools/json_schema_compiler/util.cc. """ + def __init__(self, type_manager): self._type_manager = type_manager def PopulateArrayFromListFunction(self, optional): """Returns the function to turn a list into a vector. """ - populate_list_fn = ('PopulateOptionalArrayFromList' if optional - else 'PopulateArrayFromList') + populate_list_fn = ('PopulateOptionalArrayFromList' + if optional else 'PopulateArrayFromList') return ('%s::%s') % (_API_UTIL_NAMESPACE, populate_list_fn) def CreateValueFromArray(self, src): @@ -29,8 +30,8 @@ class UtilCCHelper(object): def AppendToContainer(self, container, value): """Appends |value| to |container|. """ - return '%s::AppendToContainer(%s, %s);' % ( - _API_UTIL_NAMESPACE, container, value) + return '%s::AppendToContainer(%s, %s);' % (_API_UTIL_NAMESPACE, container, + value) def GetIncludePath(self): return '#include "tools/json_schema_compiler/util.h"' diff --git a/tools/json_schema_compiler/web_idl_schema.py b/tools/json_schema_compiler/web_idl_schema.py index 9ae89d3cacac6..833622946c5f6 100755 --- a/tools/json_schema_compiler/web_idl_schema.py +++ b/tools/json_schema_compiler/web_idl_schema.py @@ -21,9 +21,8 @@ from json_parse import OrderedDict # idl_parser expects to be able to import certain files in its directory, # so let's set things up the way it wants. -_idl_generators_path = os.path.join( - os.path.dirname(os.path.realpath(__file__)), os.pardir, os.pardir, 'tools' -) +_idl_generators_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), + os.pardir, os.pardir, 'tools') if _idl_generators_path in sys.path: from idl_parser import idl_parser, idl_lexer, idl_node else: @@ -40,8 +39,7 @@ class SchemaCompilerError(Exception): def __init__(self, message: str, node: IDLNode): super().__init__( - node.GetLogLine(f'Error processing node {node}: {message}') - ) + node.GetLogLine(f'Error processing node {node}: {message}')) def GetChildWithName(node: IDLNode, name: str) -> Optional[IDLNode]: @@ -56,8 +54,7 @@ def GetChildWithName(node: IDLNode, name: str) -> Optional[IDLNode]: name was not found. """ return next( - (child for child in node.GetChildren() if child.GetName() == name), None - ) + (child for child in node.GetChildren() if child.GetName() == name), None) def GetTypeName(node: IDLNode) -> str: @@ -76,8 +73,7 @@ def GetTypeName(node: IDLNode) -> str: if child_node.GetClass() == 'Type': return child_node.GetOneOf('Typeref').GetName() raise SchemaCompilerError( - 'Could not find Type node when looking for Typeref name.', node - ) + 'Could not find Type node when looking for Typeref name.', node) def GetExtendedAttributes(node: IDLNode) -> Optional[List[IDLNode]]: @@ -111,9 +107,8 @@ class Type: def __init__(self, node: IDLNode, additional_properties: dict) -> None: assert node.GetClass() == 'Type', node.GetLogLine( - 'Attempted to process a "Type" node, but was passed a "%s" node.' - % (node.GetClass()) - ) + 'Attempted to process a "Type" node, but was passed a "%s" node.' % + (node.GetClass())) self.node = node self.additional_properties = additional_properties @@ -136,13 +131,11 @@ class Type: properties['type'] = 'string' else: raise SchemaCompilerError( - 'Unsupported basic type found when processing type.', basic_type - ) + 'Unsupported basic type found when processing type.', basic_type) else: unknown_child = self.node.GetChildren()[0] - raise SchemaCompilerError( - 'Unsupported type class when processing type.', unknown_child - ) + raise SchemaCompilerError('Unsupported type class when processing type.', + unknown_child) return properties @@ -241,8 +234,7 @@ class IDLSchema: browser_node = GetChildWithName(self.idl, 'Browser') if browser_node is None or browser_node.GetClass() != 'Interface': raise SchemaCompilerError( - 'Required partial Browser interface not found in schema.', self.idl - ) + 'Required partial Browser interface not found in schema.', self.idl) # The 'Browser' Interface has one attribute describing the name this API is # exposed on. @@ -298,9 +290,8 @@ def Main(): for i, char in enumerate(contents): if not char.isascii(): raise Exception( - 'Non-ascii character "%s" (ord %d) found at offset %d.' - % (char, ord(char), i) - ) + 'Non-ascii character "%s" (ord %d) found at offset %d.' % + (char, ord(char), i)) idl = idl_parser.IDLParser().ParseData(contents, '<stdin>') schema = IDLSchema(idl).process() print(json.dumps(schema, indent=2)) diff --git a/tools/json_schema_compiler/web_idl_schema_test.py b/tools/json_schema_compiler/web_idl_schema_test.py index 2dffb3d81b57d..b8af6a179eb0f 100755 --- a/tools/json_schema_compiler/web_idl_schema_test.py +++ b/tools/json_schema_compiler/web_idl_schema_test.py @@ -36,19 +36,31 @@ class WebIdlSchemaTest(unittest.TestCase): getReturns(schema, 'returnsVoid'), ) self.assertEqual( - {'name': 'returnsBoolean', 'type': 'boolean'}, + { + 'name': 'returnsBoolean', + 'type': 'boolean' + }, getReturns(schema, 'returnsBoolean'), ) self.assertEqual( - {'name': 'returnsDouble', 'type': 'number'}, + { + 'name': 'returnsDouble', + 'type': 'number' + }, getReturns(schema, 'returnsDouble'), ) self.assertEqual( - {'name': 'returnsLong', 'type': 'integer'}, + { + 'name': 'returnsLong', + 'type': 'integer' + }, getReturns(schema, 'returnsLong'), ) self.assertEqual( - {'name': 'returnsDOMString', 'type': 'string'}, + { + 'name': 'returnsDOMString', + 'type': 'string' + }, getReturns(schema, 'returnsDOMString'), ) @@ -62,8 +74,7 @@ class WebIdlSchemaTest(unittest.TestCase): def testMissingBrowserInterface(self): expected_error_regex = ( '.* File\(test\/web_idl\/missing_browser_interface.idl\): Required' - ' partial Browser interface not found in schema\.' - ) + ' partial Browser interface not found in schema\.') self.assertRaisesRegex( SchemaCompilerError, expected_error_regex, @@ -76,8 +87,7 @@ class WebIdlSchemaTest(unittest.TestCase): def testMissingAttributeOnBrowser(self): expected_error_regex = ( '.* Interface\(Browser\): The partial Browser interface should have' - ' exactly one attribute for the name the API will be exposed under\.' - ) + ' exactly one attribute for the name the API will be exposed under\.') self.assertRaisesRegex( Exception, expected_error_regex, @@ -90,8 +100,7 @@ class WebIdlSchemaTest(unittest.TestCase): def testUnsupportedBasicType(self): expected_error_regex = ( '.* PrimitiveType\(float\): Unsupported basic type found when' - ' processing type\.' - ) + ' processing type\.') self.assertRaisesRegex( SchemaCompilerError, expected_error_regex, @@ -103,8 +112,7 @@ class WebIdlSchemaTest(unittest.TestCase): # doesn't support yet throws an error. def testUnsupportedTypeClass(self): expected_error_regex = ( - '.* Any\(\): Unsupported type class when processing type\.' - ) + '.* Any\(\): Unsupported type class when processing type\.') self.assertRaisesRegex( SchemaCompilerError, expected_error_regex,