From 8ee8b8861e8b3f435c72f03a765b0b0d5f892855 Mon Sep 17 00:00:00 2001 From: Youhei Sakurai Date: Thu, 2 Nov 2023 10:08:43 +0900 Subject: [PATCH] Add props to prevent typos in config properties --- esrally/metrics.py | 12 +++--- esrally/props.py | 93 ++++++++++++++++++++++++++++++++++++++++++++++ esrally/rally.py | 13 ++++--- 3 files changed, 106 insertions(+), 12 deletions(-) create mode 100644 esrally/props.py diff --git a/esrally/metrics.py b/esrally/metrics.py index d70c921ba..88567e8ee 100644 --- a/esrally/metrics.py +++ b/esrally/metrics.py @@ -33,7 +33,7 @@ import tabulate -from esrally import client, config, exceptions, paths, time, version +from esrally import client, config, exceptions, paths, props, time, version from esrally.utils import console, convert, io, versions @@ -1595,13 +1595,13 @@ def store_race(self, race): raise NotImplementedError("abstract method") def _max_results(self): - return int(self.cfg.opts("system", "list.max_results")) + return int(self.cfg.opts(*props.SYSTEM.LIST.MAX_RESULTS)) def _track(self): - return self.cfg.opts("system", "admin.track", mandatory=False) + return self.cfg.opts(*props.SYSTEM.ADMIN.TRACK, mandatory=False) def _benchmark_name(self): - return self.cfg.opts("system", "list.races.benchmark_name", mandatory=False) + return self.cfg.opts(*props.SYSTEM.LIST.RACES.BANCHMARK_NAME, mandatory=False) def _race_timestamp(self): return self.cfg.opts("system", "add.race_timestamp") @@ -1616,10 +1616,10 @@ def _chart_name(self): return self.cfg.opts("system", "add.chart_name", mandatory=False) def _from_date(self): - return self.cfg.opts("system", "list.from_date", mandatory=False) + return self.cfg.opts(*props.SYSTEM.LIST.FROM_DATE, mandatory=False) def _to_date(self): - return self.cfg.opts("system", "list.to_date", mandatory=False) + return self.cfg.opts(*props.SYSTEM.LIST.TO_DATE, mandatory=False) def _dry_run(self): return self.cfg.opts("system", "admin.dry_run", mandatory=False) diff --git a/esrally/props.py b/esrally/props.py new file mode 100644 index 000000000..dd7ddd8d1 --- /dev/null +++ b/esrally/props.py @@ -0,0 +1,93 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +from enum import auto, EnumType, member, StrEnum + + +class PropEnumType(EnumType): + __iter__ = None + + +class PropEnum(StrEnum, metaclass=PropEnumType): + def __new__(cls, value): + if isinstance(value, PropEnumType): + return value + elif isinstance(value, str): + value = f"{cls.__qualname__.lower()}.{value}" + member = str.__new__(cls, value) + member._value_ = value + return member + else: + raise TypeError(f"{value!r} is not a string or enum") + + +class Property(PropEnum): + def __sections(self): + node = globals() + for name in type(self).__qualname__.split('.'): + node = node[name] + if issubclass(node, Section): + yield name + else: + break + + @property + def __section(self): + return '.'.join(map(str.lower, self.__sections())) + + @property + def __key(self): + return self.value.replace(f"{self.__section}.", "", 1) + + def __iter__(self): + return iter((self.__section, self.__key, )) + + def __repr__(self): + return f"<{type(self).__qualname__}.{self.name}: {self.value}>" + + +class Section(Property): + pass + + +class Key(Property): + pass + + +def desc(*_): + return auto() + + +class SYSTEM(Section): + + @member + class ADMIN(Key): + TRACK = desc() + + @member + class LIST(Key): + FROM_DATE = desc() + MAX_RESULTS = desc() + TO_DATE = desc() + + @member + class CONFIG(Key): + OPTION = desc() + + @member + class RACES(Key): + BANCHMARK_NAME = desc() diff --git a/esrally/rally.py b/esrally/rally.py index c98c59db6..0f17165b7 100644 --- a/esrally/rally.py +++ b/esrally/rally.py @@ -41,6 +41,7 @@ log, metrics, paths, + props, racecontrol, reporter, telemetry, @@ -1086,12 +1087,12 @@ def dispatch_sub_command(arg_parser, args, cfg): configure_reporting_params(args, cfg) reporter.compare(cfg, args.baseline, args.contender) elif sub_command == "list": - cfg.add(config.Scope.applicationOverride, "system", "list.config.option", args.configuration) - cfg.add(config.Scope.applicationOverride, "system", "list.max_results", args.limit) - cfg.add(config.Scope.applicationOverride, "system", "admin.track", args.track) - cfg.add(config.Scope.applicationOverride, "system", "list.races.benchmark_name", args.benchmark_name) - cfg.add(config.Scope.applicationOverride, "system", "list.from_date", args.from_date) - cfg.add(config.Scope.applicationOverride, "system", "list.to_date", args.to_date) + cfg.add(config.Scope.applicationOverride, *props.SYSTEM.LIST.CONFIG.OPTION, args.configuration) + cfg.add(config.Scope.applicationOverride, *props.SYSTEM.LIST.MAX_RESULTS, args.limit) + cfg.add(config.Scope.applicationOverride, *props.SYSTEM.ADMIN.TRACK, args.track) + cfg.add(config.Scope.applicationOverride, *props.SYSTEM.LIST.RACES.BANCHMARK_NAME, args.benchmark_name) + cfg.add(config.Scope.applicationOverride, *props.SYSTEM.LIST.FROM_DATE, args.from_date) + cfg.add(config.Scope.applicationOverride, *props.SYSTEM.LIST.TO_DATE, args.to_date) configure_mechanic_params(args, cfg, command_requires_car=False) configure_track_params(arg_parser, args, cfg, command_requires_track=False) dispatch_list(cfg)