From a33edecad61c0072ae3a3a1f608767211780e5c5 Mon Sep 17 00:00:00 2001 From: Takashi Kajinami Date: Sat, 19 Oct 2024 23:20:34 +0900 Subject: [PATCH] Run pyupgrade to clean up Python 2 syntaxes Update all .py source files by $ pyupgrade --py3-only $(git ls-files | grep ".py$") to modernize the code according to Python 3 syntaxes. Also add the pyupgrade hook to pre-commit to avoid merging additional Python 2 syntaxes. Change-Id: I282706b4b2986da32ce813045620f5a145d91c45 --- .pre-commit-config.yaml | 9 +++++++-- doc/source/conf.py | 1 - oslo_policy/_cache_handler.py | 2 +- oslo_policy/_checks.py | 3 +-- oslo_policy/_external.py | 1 - oslo_policy/_parser.py | 3 +-- oslo_policy/fixture.py | 8 ++++---- oslo_policy/generator.py | 14 +++++++------- oslo_policy/policy.py | 19 +++++++++---------- oslo_policy/shell.py | 2 +- oslo_policy/sphinxext.py | 6 ++---- oslo_policy/sphinxpolicygen.py | 2 +- oslo_policy/tests/base.py | 2 +- oslo_policy/tests/test_checks.py | 12 ++++++------ oslo_policy/tests/test_external.py | 4 ++-- oslo_policy/tests/test_generator.py | 26 +++++++++++++------------- oslo_policy/tests/test_opts.py | 2 +- oslo_policy/tests/test_policy.py | 17 ++++++++--------- oslo_policy/tests/test_shell.py | 4 ++-- releasenotes/source/conf.py | 1 - 20 files changed, 67 insertions(+), 71 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d77300c1..eb81aa9f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.5.0 + rev: v5.0.0 hooks: - id: trailing-whitespace # Replaces or checks mixed line ending @@ -24,7 +24,12 @@ repos: - id: hacking additional_dependencies: [] - repo: https://github.com/PyCQA/bandit - rev: 1.7.6 + rev: 1.7.10 hooks: - id: bandit args: ['-x', 'tests', '--skip', 'B113'] + - repo: https://github.com/asottile/pyupgrade + rev: v3.18.0 + hooks: + - id: pyupgrade + args: [--py3-only] diff --git a/doc/source/conf.py b/doc/source/conf.py index 503967e7..87ef1c08 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright (C) 2020 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/oslo_policy/_cache_handler.py b/oslo_policy/_cache_handler.py index fd349150..e3e72659 100644 --- a/oslo_policy/_cache_handler.py +++ b/oslo_policy/_cache_handler.py @@ -51,7 +51,7 @@ def read_cached_file(cache, filename, force_reload=False): try: with open(filename) as fap: cache_info['data'] = fap.read() - except IOError as err: + except OSError as err: msg = err.strerror err_code = err.errno LOG.error('IO error loading %(filename)s: %(msg)s', diff --git a/oslo_policy/_checks.py b/oslo_policy/_checks.py index 5246c2a5..e4c69f69 100644 --- a/oslo_policy/_checks.py +++ b/oslo_policy/_checks.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (c) 2015 OpenStack Foundation. # All Rights Reserved. @@ -138,7 +137,7 @@ class Check(BaseCheck): def __str__(self): """Return a string representation of this check.""" - return '%s:%s' % (self.kind, self.match) + return '{}:{}'.format(self.kind, self.match) class NotCheck(BaseCheck): diff --git a/oslo_policy/_external.py b/oslo_policy/_external.py index 52417023..eaa5d34b 100644 --- a/oslo_policy/_external.py +++ b/oslo_policy/_external.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (c) 2015 OpenStack Foundation. # All Rights Reserved. diff --git a/oslo_policy/_parser.py b/oslo_policy/_parser.py index 29e76a5c..02fa6fcc 100644 --- a/oslo_policy/_parser.py +++ b/oslo_policy/_parser.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (c) 2015 OpenStack Foundation. # All Rights Reserved. @@ -67,7 +66,7 @@ class ParseStateMeta(type): cls_dict['reducers'] = reducers - return super(ParseStateMeta, mcs).__new__(mcs, name, bases, cls_dict) + return super().__new__(mcs, name, bases, cls_dict) class ParseState(metaclass=ParseStateMeta): diff --git a/oslo_policy/fixture.py b/oslo_policy/fixture.py index 4980caa4..d94c66e4 100644 --- a/oslo_policy/fixture.py +++ b/oslo_policy/fixture.py @@ -25,11 +25,11 @@ class HttpCheckFixture(fixtures.Fixture): implies that the policy check failed :type return_value: boolean """ - super(HttpCheckFixture, self).__init__() + super().__init__() self.return_value = return_value def setUp(self): - super(HttpCheckFixture, self).setUp() + super().setUp() def mocked_call(target, cred, enforcer, rule): return self.return_value @@ -52,11 +52,11 @@ class HttpsCheckFixture(fixtures.Fixture): implies that the policy check failed :type return_value: boolean """ - super(HttpsCheckFixture, self).__init__() + super().__init__() self.return_value = return_value def setUp(self): - super(HttpsCheckFixture, self).setUp() + super().setUp() def mocked_call(target, cred, enforcer, rule): return self.return_value diff --git a/oslo_policy/generator.py b/oslo_policy/generator.py index b4e93d59..50041edb 100644 --- a/oslo_policy/generator.py +++ b/oslo_policy/generator.py @@ -229,11 +229,11 @@ def _format_rule_default_yaml(default, include_help=True, comment_rule=True, 'name': default.name, 'check_str': default.check_str, } - text = '%(text)s# DEPRECATED\n%(deprecated_text)s\n%(reason)s\n' % { - 'text': text, - 'reason': _format_help_text(deprecated_reason), - 'deprecated_text': _format_help_text(deprecated_text) - } + text = '{text}# DEPRECATED\n{deprecated_text}\n{reason}\n'.format( + text=text, + reason=_format_help_text(deprecated_reason), + deprecated_text=_format_help_text(deprecated_text) + ) if default.name != default.deprecated_rule.name: text += ('# WARNING: A rule name change has been identified.\n' @@ -452,7 +452,7 @@ def _validate_policy(namespace): def _convert_policy_json_to_yaml(namespace, policy_file, output_file=None): - with open(policy_file, 'r') as rule_data: + with open(policy_file) as rule_data: file_policies = jsonutils.loads(rule_data.read()) yaml_format_rules = [] @@ -585,7 +585,7 @@ def upgrade_policy(args=None, conf=None): conf.register_opts(GENERATOR_OPTS + RULE_OPTS + UPGRADE_OPTS) conf(args) _check_for_namespace_opt(conf) - with open(conf.policy, 'r') as input_data: + with open(conf.policy) as input_data: policies = policy.parse_file_contents(input_data.read()) default_policies = get_policies_dict(conf.namespace) diff --git a/oslo_policy/policy.py b/oslo_policy/policy.py index 9ca53768..33e0dd5a 100644 --- a/oslo_policy/policy.py +++ b/oslo_policy/policy.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright (c) 2012 OpenStack Foundation. # All Rights Reserved. @@ -310,7 +309,7 @@ class PolicyNotAuthorized(Exception): def __init__(self, rule, target, creds): msg = _("%(rule)s is disallowed by policy") % {'rule': rule} - super(PolicyNotAuthorized, self).__init__(msg) + super().__init__(msg) class InvalidScope(Exception): @@ -325,40 +324,40 @@ class InvalidScope(Exception): 'token_scope': token_scope } ) - super(InvalidScope, self).__init__(msg) + super().__init__(msg) class DuplicatePolicyError(Exception): def __init__(self, name): msg = _('Policy %(name)s is already registered') % {'name': name} - super(DuplicatePolicyError, self).__init__(msg) + super().__init__(msg) class PolicyNotRegistered(Exception): def __init__(self, name): msg = _('Policy %(name)s has not been registered') % {'name': name} - super(PolicyNotRegistered, self).__init__(msg) + super().__init__(msg) class InvalidDefinitionError(Exception): def __init__(self, names): msg = _('Policies %(names)s are not well defined. Check logs for ' 'more details.') % {'names': names} - super(InvalidDefinitionError, self).__init__(msg) + super().__init__(msg) class InvalidRuleDefault(Exception): def __init__(self, error): msg = (_('Invalid policy rule default: ' '%(error)s.') % {'error': error}) - super(InvalidRuleDefault, self).__init__(msg) + super().__init__(msg) class InvalidContextObject(Exception): def __init__(self, error): msg = (_('Invalid context object: ' '%(error)s.') % {'error': error}) - super(InvalidContextObject, self).__init__(msg) + super().__init__(msg) def pick_default_policy_file(conf, fallback_to_json_file=True): @@ -467,7 +466,7 @@ class Rules(dict): def __init__(self, rules=None, default_rule=None): """Initialize the Rules store.""" - super(Rules, self).__init__(rules or {}) + super().__init__(rules or {}) self.default_rule = default_rule def __missing__(self, key): @@ -507,7 +506,7 @@ class Rules(dict): return jsonutils.dumps(out_rules, indent=4) -class Enforcer(object): +class Enforcer: """Responsible for loading and enforcing rules. :param conf: A configuration object. diff --git a/oslo_policy/shell.py b/oslo_policy/shell.py index 43e212f5..c3c2a8bc 100644 --- a/oslo_policy/shell.py +++ b/oslo_policy/shell.py @@ -23,7 +23,7 @@ from oslo_policy import opts from oslo_policy import policy -class FakeEnforcer(object): +class FakeEnforcer: def __init__(self, rules, config): self.rules = rules self.conf = None diff --git a/oslo_policy/sphinxext.py b/oslo_policy/sphinxext.py index a6c02b68..de1cd587 100644 --- a/oslo_policy/sphinxext.py +++ b/oslo_policy/sphinxext.py @@ -86,16 +86,14 @@ def _format_policy_section(section, rules): yield '' for rule in rules: - for line in _format_policy_rule(rule): - yield line + yield from _format_policy_rule(rule) def _format_policy(namespaces): policies = generator.get_policies_dict(namespaces) for section in sorted(policies.keys()): - for line in _format_policy_section(section, policies[section]): - yield line + yield from _format_policy_section(section, policies[section]) class ShowPolicyDirective(rst.Directive): diff --git a/oslo_policy/sphinxpolicygen.py b/oslo_policy/sphinxpolicygen.py index 8dd30092..8d5a4ec5 100644 --- a/oslo_policy/sphinxpolicygen.py +++ b/oslo_policy/sphinxpolicygen.py @@ -53,7 +53,7 @@ def _get_default_basename(config_file): def _generate_sample(app, policy_file, base_name, exclude_deprecated): def info(msg): - LOG.info('[%s] %s' % (__name__, msg)) + LOG.info('[{}] {}'.format(__name__, msg)) # If we are given a file that isn't an absolute path, look for it # in the source directory if it doesn't exist. diff --git a/oslo_policy/tests/base.py b/oslo_policy/tests/base.py index 698e4dde..6f4d8cdc 100644 --- a/oslo_policy/tests/base.py +++ b/oslo_policy/tests/base.py @@ -30,7 +30,7 @@ from oslo_policy import policy class PolicyBaseTestCase(test_base.BaseTestCase): def setUp(self): - super(PolicyBaseTestCase, self).setUp() + super().setUp() self.conf = self.useFixture(config.Config()).conf self.config_dir = self.useFixture(fixtures.TempDir()).path self.conf(args=['--config-dir', self.config_dir]) diff --git a/oslo_policy/tests/test_checks.py b/oslo_policy/tests/test_checks.py index 61b5154e..e4e79215 100644 --- a/oslo_policy/tests/test_checks.py +++ b/oslo_policy/tests/test_checks.py @@ -281,7 +281,7 @@ class NotCheckTestCase(test_base.BaseTestCase): def test_rule_takes_current_rule(self): results = [] - class TestCheck(object): + class TestCheck: def __call__(self, target, cred, enforcer, current_rule=None): results.append((target, cred, enforcer, current_rule)) return True @@ -297,7 +297,7 @@ class NotCheckTestCase(test_base.BaseTestCase): def test_rule_does_not_take_current_rule(self): results = [] - class TestCheck(object): + class TestCheck: def __call__(self, target, cred, enforcer): results.append((target, cred, enforcer)) return True @@ -378,7 +378,7 @@ class AndCheckTestCase(test_base.BaseTestCase): def test_rule_takes_current_rule(self): results = [] - class TestCheck(object): + class TestCheck: def __call__(self, target, cred, enforcer, current_rule=None): results.append((target, cred, enforcer, current_rule)) return False @@ -394,7 +394,7 @@ class AndCheckTestCase(test_base.BaseTestCase): def test_rule_does_not_take_current_rule(self): results = [] - class TestCheck(object): + class TestCheck: def __call__(self, target, cred, enforcer): results.append((target, cred, enforcer)) return False @@ -468,7 +468,7 @@ class OrCheckTestCase(test_base.BaseTestCase): def test_rule_takes_current_rule(self): results = [] - class TestCheck(object): + class TestCheck: def __call__(self, target, cred, enforcer, current_rule=None): results.append((target, cred, enforcer, current_rule)) return False @@ -484,7 +484,7 @@ class OrCheckTestCase(test_base.BaseTestCase): def test_rule_does_not_take_current_rule(self): results = [] - class TestCheck(object): + class TestCheck: def __call__(self, target, cred, enforcer): results.append((target, cred, enforcer)) return False diff --git a/oslo_policy/tests/test_external.py b/oslo_policy/tests/test_external.py index 797e70f9..c2d62b9b 100644 --- a/oslo_policy/tests/test_external.py +++ b/oslo_policy/tests/test_external.py @@ -29,7 +29,7 @@ from oslo_policy.tests import base class HttpCheckTestCase(base.PolicyBaseTestCase): def setUp(self): - super(HttpCheckTestCase, self).setUp() + super().setUp() opts._register(self.conf) self.requests_mock = self.useFixture(rm_fixture.Fixture()) @@ -152,7 +152,7 @@ class HttpCheckTestCase(base.PolicyBaseTestCase): class HttpsCheckTestCase(base.PolicyBaseTestCase): def setUp(self): - super(HttpsCheckTestCase, self).setUp() + super().setUp() opts._register(self.conf) self.requests_mock = self.useFixture(rm_fixture.Fixture()) diff --git a/oslo_policy/tests/test_generator.py b/oslo_policy/tests/test_generator.py index 71c55fbe..7bb38a7b 100644 --- a/oslo_policy/tests/test_generator.py +++ b/oslo_policy/tests/test_generator.py @@ -46,7 +46,7 @@ OPTS = {'base_rules': [policy.RuleDefault('admin', 'is_admin:True', class GenerateSampleYAMLTestCase(base.PolicyBaseTestCase): def setUp(self): - super(GenerateSampleYAMLTestCase, self).setUp() + super().setUp() self.enforcer = policy.Enforcer(self.conf, policy_file='policy.yaml') def test_generate_loadable_yaml(self): @@ -112,7 +112,7 @@ class GenerateSampleYAMLTestCase(base.PolicyBaseTestCase): on_load_failure_callback=generator.on_load_failure_callback, invoke_on_load=True) - with open(output_file, 'r') as written_file: + with open(output_file) as written_file: written_policy = written_file.read() self.assertEqual(expected, written_policy) @@ -309,7 +309,7 @@ class GenerateSampleYAMLTestCase(base.PolicyBaseTestCase): on_load_failure_callback=generator.on_load_failure_callback, invoke_on_load=True) - with open(output_file, 'r') as written_file: + with open(output_file) as written_file: written_policy = written_file.read() self.assertEqual(expected, written_policy) @@ -387,7 +387,7 @@ We have some text... class GenerateSampleJSONTestCase(base.PolicyBaseTestCase): def setUp(self): - super(GenerateSampleJSONTestCase, self).setUp() + super().setUp() self.enforcer = policy.Enforcer(self.conf, policy_file='policy.json') def test_generate_loadable_json(self): @@ -449,7 +449,7 @@ class GenerateSampleJSONTestCase(base.PolicyBaseTestCase): on_load_failure_callback=generator.on_load_failure_callback, invoke_on_load=True) - with open(output_file, 'r') as written_file: + with open(output_file) as written_file: written_policy = written_file.read() self.assertEqual(expected, written_policy) @@ -507,7 +507,7 @@ class GeneratorRaiseErrorTestCase(testtools.TestCase): class FakeException(Exception): pass - class FakeEP(object): + class FakeEP: def __init__(self): self.name = 'callback_is_expected' @@ -533,7 +533,7 @@ class GeneratorRaiseErrorTestCase(testtools.TestCase): class GeneratePolicyTestCase(base.PolicyBaseTestCase): def setUp(self): - super(GeneratePolicyTestCase, self).setUp() + super().setUp() def test_merged_rules(self): extensions = [] @@ -589,7 +589,7 @@ class GeneratePolicyTestCase(base.PolicyBaseTestCase): class ListRedundantTestCase(base.PolicyBaseTestCase): def setUp(self): - super(ListRedundantTestCase, self).setUp() + super().setUp() @mock.patch('warnings.warn') def test_matched_rules(self, mock_warn): @@ -664,7 +664,7 @@ class ListRedundantTestCase(base.PolicyBaseTestCase): class UpgradePolicyTestCase(base.PolicyBaseTestCase): def setUp(self): - super(UpgradePolicyTestCase, self).setUp() + super().setUp() policy_json_contents = jsonutils.dumps({ "deprecated_name": "rule:admin" }) @@ -706,7 +706,7 @@ class UpgradePolicyTestCase(base.PolicyBaseTestCase): with mock.patch('sys.argv', testargs): generator.upgrade_policy(conf=self.local_conf) new_file = self.get_config_file_fullname('new_policy.json') - with open(new_file, 'r') as fh: + with open(new_file) as fh: new_policy = jsonutils.loads(fh.read()) self.assertIsNotNone(new_policy.get('new_policy_name')) self.assertIsNone(new_policy.get('deprecated_name')) @@ -743,7 +743,7 @@ class UpgradePolicyTestCase(base.PolicyBaseTestCase): with mock.patch('sys.argv', testargs): generator.upgrade_policy(conf=self.local_conf) new_file = self.get_config_file_fullname('new_policy.yaml') - with open(new_file, 'r') as fh: + with open(new_file) as fh: new_policy = yaml.safe_load(fh) self.assertIsNotNone(new_policy.get('new_policy_name')) self.assertIsNone(new_policy.get('deprecated_name')) @@ -852,7 +852,7 @@ class ValidatorTestCase(base.PolicyBaseTestCase): class ConvertJsonToYamlTestCase(base.PolicyBaseTestCase): def setUp(self): - super(ConvertJsonToYamlTestCase, self).setUp() + super().setUp() policy_json_contents = jsonutils.dumps({ "rule1_name": "rule:admin", "rule2_name": "rule:overridden", @@ -932,7 +932,7 @@ class ConvertJsonToYamlTestCase(base.PolicyBaseTestCase): with mock.patch('sys.argv', testargs): generator.convert_policy_json_to_yaml(conf=self.local_conf) if output_to_file: - with open(self.output_file_path, 'r') as fh: + with open(self.output_file_path) as fh: converted_policy_data = fh.read() return converted_policy_data diff --git a/oslo_policy/tests/test_opts.py b/oslo_policy/tests/test_opts.py index 59a8355a..884c2d21 100644 --- a/oslo_policy/tests/test_opts.py +++ b/oslo_policy/tests/test_opts.py @@ -21,7 +21,7 @@ from oslo_policy import opts class OptsTestCase(test_base.BaseTestCase): def setUp(self): - super(OptsTestCase, self).setUp() + super().setUp() self.conf = cfg.ConfigOpts() self.original_opts = opts._options opts._options = copy.deepcopy(opts._options) diff --git a/oslo_policy/tests/test_policy.py b/oslo_policy/tests/test_policy.py index 4172f640..7c6498e4 100644 --- a/oslo_policy/tests/test_policy.py +++ b/oslo_policy/tests/test_policy.py @@ -55,8 +55,7 @@ class FieldCheck(_checks.Check): # Process the match resource, field_value = match.split(':', 1) field, value = field_value.split('=', 1) - super(FieldCheck, self).__init__(kind, '%s:%s:%s' % - (resource, field, value)) + super().__init__(kind, '{}:{}:{}'.format(resource, field, value)) self.field = field self.value = value @@ -235,7 +234,7 @@ default: [ class EnforcerTest(base.PolicyBaseTestCase): def setUp(self): - super(EnforcerTest, self).setUp() + super().setUp() self.create_config_file('policy.yaml', POLICY_YAML_CONTENTS) def _test_scenario_with_opts_registered(self, scenario, *args, **kwargs): @@ -843,7 +842,7 @@ class EnforcerTest(base.PolicyBaseTestCase): rule = policy.RuleDefault(name='fake_rule', check_str='role:test') self.enforcer.register_default(rule) - class InvalidContext(object): + class InvalidContext: pass request_context = InvalidContext() @@ -1047,7 +1046,7 @@ class EnforcerTest(base.PolicyBaseTestCase): class EnforcerNoPolicyFileTest(base.PolicyBaseTestCase): def setUp(self): - super(EnforcerNoPolicyFileTest, self).setUp() + super().setUp() def test_load_rules(self): # Check that loading rules with no policy file does not error @@ -1079,7 +1078,7 @@ class EnforcerNoPolicyFileTest(base.PolicyBaseTestCase): class CheckFunctionTestCase(base.PolicyBaseTestCase): def setUp(self): - super(CheckFunctionTestCase, self).setUp() + super().setUp() self.create_config_file('policy.yaml', POLICY_YAML_CONTENTS) def test_check_explicit(self): @@ -1209,7 +1208,7 @@ class RuleDefaultTestCase(base.PolicyBaseTestCase): self.assertNotEqual(opt1, opt2) def test_not_equal_class(self): - class NotRuleDefault(object): + class NotRuleDefault: def __init__(self, name, check_str): self.name = name self.check = _parser.parse_rule(check_str) @@ -1962,7 +1961,7 @@ class DeprecatedRuleTestCase(base.PolicyBaseTestCase): class EnforcerCheckRulesTest(base.PolicyBaseTestCase): def setUp(self): - super(EnforcerCheckRulesTest, self).setUp() + super().setUp() def test_no_violations(self): self.create_config_file('policy.yaml', POLICY_YAML_CONTENTS) @@ -2051,7 +2050,7 @@ class EnforcerCheckRulesTest(base.PolicyBaseTestCase): class PickPolicyFileTestCase(base.PolicyBaseTestCase): def setUp(self): - super(PickPolicyFileTestCase, self).setUp() + super().setUp() self.data = { 'rule_admin': 'True', 'rule_admin2': 'is_admin:True' diff --git a/oslo_policy/tests/test_shell.py b/oslo_policy/tests/test_shell.py index b27347c1..4bd9830d 100644 --- a/oslo_policy/tests/test_shell.py +++ b/oslo_policy/tests/test_shell.py @@ -47,7 +47,7 @@ class CheckerTestCase(base.PolicyBaseTestCase): ''' def setUp(self): - super(CheckerTestCase, self).setUp() + super().setUp() self.create_config_file("policy.yaml", self.SAMPLE_POLICY) self.create_config_file( "access.json", @@ -229,7 +229,7 @@ passed: sampleservice:sample_rule2 self.create_config_file( "target.json", jsonutils.dumps(target)) - with open(self.get_config_file_fullname('target.json'), 'r') as fh: + with open(self.get_config_file_fullname('target.json')) as fh: target_from_file = fh.read() result = shell.flatten(jsonutils.loads(target_from_file)) self.assertEqual(result, {"target.secret.project_id": "1234"}) diff --git a/releasenotes/source/conf.py b/releasenotes/source/conf.py index 90ab8596..6ac7915a 100644 --- a/releasenotes/source/conf.py +++ b/releasenotes/source/conf.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at