pylint: enable the set_membership plugin

Which adds the `use-set-for-membership` check. It's generally faster in
python to use a set with the `in` keyword, because it's a hash check
instead of a linear walk, this is especially true with strings, where
it's actually O(n^2), one loop over the container, and an inner loop of
the strings (as string comparison works by checking that `a[n] == b[n]`,
in a loop).

Also, I'm tired of complaining about this in reviews, let the tools do
it for me :)
pull/11122/head
Dylan Baker 2 years ago committed by Eli Schwartz
parent 50f35039e7
commit 2d349eae8c
No known key found for this signature in database
GPG Key ID: CEB167EFB5722BD6
  1. 1
      .pylintrc
  2. 2
      mesonbuild/ast/introspection.py
  3. 2
      mesonbuild/backend/ninjabackend.py
  4. 4
      mesonbuild/backend/vs2010backend.py
  5. 8
      mesonbuild/build.py
  6. 4
      mesonbuild/cmake/common.py
  7. 2
      mesonbuild/cmake/generator.py
  8. 6
      mesonbuild/cmake/interpreter.py
  9. 6
      mesonbuild/cmake/traceparser.py
  10. 2
      mesonbuild/compilers/cuda.py
  11. 2
      mesonbuild/compilers/d.py
  12. 4
      mesonbuild/compilers/fortran.py
  13. 2
      mesonbuild/compilers/java.py
  14. 2
      mesonbuild/compilers/mixins/clike.py
  15. 6
      mesonbuild/compilers/mixins/visualstudio.py
  16. 2
      mesonbuild/dependencies/base.py
  17. 14
      mesonbuild/dependencies/boost.py
  18. 4
      mesonbuild/dependencies/detect.py
  19. 2
      mesonbuild/dependencies/misc.py
  20. 2
      mesonbuild/dependencies/mpi.py
  21. 2
      mesonbuild/depfile.py
  22. 4
      mesonbuild/environment.py
  23. 5
      mesonbuild/mesonmain.py
  24. 2
      mesonbuild/minit.py
  25. 2
      mesonbuild/modules/__init__.py
  26. 2
      mesonbuild/modules/gnome.py
  27. 4
      mesonbuild/modules/python.py
  28. 6
      mesonbuild/munstable_coredata.py
  29. 10
      mesonbuild/rewriter.py
  30. 4
      mesonbuild/scripts/cmake_run_ctgt.py
  31. 2
      mesonbuild/scripts/symbolextractor.py
  32. 2
      mesonbuild/scripts/tags.py
  33. 2
      mesonbuild/utils/universal.py
  34. 2
      mesonbuild/wrap/wrap.py

@ -2,6 +2,7 @@
jobs=0
load-plugins=
pylint.extensions.bad_builtin,
pylint.extensions.set_membership,
[REPORTS]
score=no

@ -261,7 +261,7 @@ class IntrospectionInterpreter(AstInterpreter):
extraf_nodes = traverse_nodes(extra_queue)
# Make sure nothing can crash when creating the build class
kwargs_reduced = {k: v for k, v in kwargs.items() if k in targetclass.known_kwargs and k in ['install', 'build_by_default', 'build_always']}
kwargs_reduced = {k: v for k, v in kwargs.items() if k in targetclass.known_kwargs and k in {'install', 'build_by_default', 'build_always'}}
kwargs_reduced = {k: v.value if isinstance(v, ElementaryNode) else v for k, v in kwargs_reduced.items()}
kwargs_reduced = {k: v for k, v in kwargs_reduced.items() if not isinstance(v, BaseNode)}
for_machine = MachineChoice.HOST

@ -3535,7 +3535,7 @@ def _scan_fortran_file_deps(src: Path, srcdir: Path, dirname: Path, tdeps, compi
submodmatch = submodre.match(line)
if submodmatch is not None:
parents = submodmatch.group(1).lower().split(':')
assert len(parents) in (1, 2), (
assert len(parents) in {1, 2}, (
'submodule ancestry must be specified as'
f' ancestor:parent but Meson found {parents}')

@ -852,13 +852,13 @@ class Vs2010Backend(backends.Backend):
def _get_cl_compiler(self, target):
for lang, c in target.compilers.items():
if lang in ('c', 'cpp'):
if lang in {'c', 'cpp'}:
return c
# No source files, only objects, but we still need a compiler, so
# return a found compiler
if len(target.objects) > 0:
for lang, c in self.environment.coredata.compilers[target.for_machine].items():
if lang in ('c', 'cpp'):
if lang in {'c', 'cpp'}:
return c
raise MesonException('Could not find a C or C++ compiler. MSVC can only build C/C++ projects.')

@ -1829,8 +1829,8 @@ class Executable(BuildTarget):
self.suffix = 'abs'
elif ('c' in self.compilers and self.compilers['c'].get_id().startswith('xc16')):
self.suffix = 'elf'
elif ('c' in self.compilers and self.compilers['c'].get_id() in ('ti', 'c2000') or
'cpp' in self.compilers and self.compilers['cpp'].get_id() in ('ti', 'c2000')):
elif ('c' in self.compilers and self.compilers['c'].get_id() in {'ti', 'c2000'} or
'cpp' in self.compilers and self.compilers['cpp'].get_id() in {'ti', 'c2000'}):
self.suffix = 'out'
else:
self.suffix = machine.get_exe_suffix()
@ -2176,10 +2176,10 @@ class SharedLibrary(BuildTarget):
raise InvalidArguments('Shared library darwin_versions: must be X.Y.Z where '
'X, Y, Z are numbers, and Y and Z are optional')
parts = v.split('.')
if len(parts) in (1, 2, 3) and int(parts[0]) > 65535:
if len(parts) in {1, 2, 3} and int(parts[0]) > 65535:
raise InvalidArguments('Shared library darwin_versions: must be X.Y.Z '
'where X is [0, 65535] and Y, Z are optional')
if len(parts) in (2, 3) and int(parts[1]) > 255:
if len(parts) in {2, 3} and int(parts[1]) > 255:
raise InvalidArguments('Shared library darwin_versions: must be X.Y.Z '
'where Y is [0, 255] and Y, Z are optional')
if len(parts) == 3 and int(parts[2]) > 255:

@ -100,9 +100,9 @@ def _flags_to_list(raw: str) -> T.List[str]:
escape = False
elif i == '\\':
escape = True
elif i in ['"', "'"]:
elif i in {'"', "'"}:
in_string = not in_string
elif i in [' ', '\n']:
elif i in {' ', '\n'}:
if in_string:
curr += i
else:

@ -99,7 +99,7 @@ def parse_generator_expressions(
supported = {
# Boolean functions
'BOOL': lambda x: '0' if x.upper() in ['0', 'FALSE', 'OFF', 'N', 'NO', 'IGNORE', 'NOTFOUND'] or x.endswith('-NOTFOUND') else '1',
'BOOL': lambda x: '0' if x.upper() in {'0', 'FALSE', 'OFF', 'N', 'NO', 'IGNORE', 'NOTFOUND'} or x.endswith('-NOTFOUND') else '1',
'AND': lambda x: '1' if all(y == '1' for y in x.split(',')) else '0',
'OR': lambda x: '1' if any(y == '1' for y in x.split(',')) else '0',
'NOT': lambda x: '0' if x == '1' else '1',

@ -320,7 +320,7 @@ class ConverterTarget:
)
continue
self.override_options += [f'{i}_std={std}']
elif j in ['-fPIC', '-fpic', '-fPIE', '-fpie']:
elif j in {'-fPIC', '-fpic', '-fPIE', '-fpie'}:
self.pie = True
elif isinstance(ctgt, ConverterCustomTarget):
# Sometimes projects pass generated source files as compiler
@ -1173,10 +1173,10 @@ class CMakeInterpreter:
src_node = assign(src_var, function('files', sources))
tgt_node = assign(tgt_var, function(tgt_func, [tgt_var, id_node(src_var), *generated], tgt_kwargs))
node_list += [src_node, tgt_node]
if tgt_func in ['static_library', 'shared_library']:
if tgt_func in {'static_library', 'shared_library'}:
dep_node = assign(dep_var, function('declare_dependency', kwargs=dep_kwargs))
node_list += [dep_node]
elif tgt_func in ['shared_module']:
elif tgt_func == 'shared_module':
del dep_kwargs['link_with']
dep_node = assign(dep_var, function('declare_dependency', kwargs=dep_kwargs))
node_list += [dep_node]

@ -682,14 +682,14 @@ class CMakeTraceParser:
if i in ignore:
continue
if i in ['INTERFACE', 'LINK_INTERFACE_LIBRARIES', 'PUBLIC', 'PRIVATE', 'LINK_PUBLIC', 'LINK_PRIVATE']:
if i in {'INTERFACE', 'LINK_INTERFACE_LIBRARIES', 'PUBLIC', 'PRIVATE', 'LINK_PUBLIC', 'LINK_PRIVATE'}:
mode = i
continue
if mode in ['INTERFACE', 'LINK_INTERFACE_LIBRARIES', 'PUBLIC', 'LINK_PUBLIC']:
if mode in {'INTERFACE', 'LINK_INTERFACE_LIBRARIES', 'PUBLIC', 'LINK_PUBLIC'}:
interface += i.split(';')
if mode in ['PUBLIC', 'PRIVATE', 'LINK_PRIVATE']:
if mode in {'PUBLIC', 'PRIVATE', 'LINK_PRIVATE'}:
private += i.split(';')
if paths:

@ -747,7 +747,7 @@ class CudaCompiler(Compiler):
# native option to override it; override it with /NODEFAULTLIB
host_link_arg_overrides = []
host_crt_compile_args = self.host_compiler.get_crt_compile_args(crt_val, buildtype)
if any(arg in ['/MDd', '/MD', '/MTd'] for arg in host_crt_compile_args):
if any(arg in {'/MDd', '/MD', '/MTd'} for arg in host_crt_compile_args):
host_link_arg_overrides += ['/NODEFAULTLIB:LIBCMT.lib']
return self._to_host_flags(host_link_arg_overrides + self.host_compiler.get_crt_link_args(crt_val, buildtype), _Phase.LINKER)

@ -463,7 +463,7 @@ class DmdLikeCompilerMixin(CompilerMixinBase):
if crt_val in self.mscrt_args:
return self.mscrt_args[crt_val]
assert crt_val in ['from_buildtype', 'static_from_buildtype']
assert crt_val in {'from_buildtype', 'static_from_buildtype'}
dbg = 'mdd'
rel = 'md'

@ -105,9 +105,9 @@ class FortranCompiler(CLikeCompiler, Compiler):
def module_name_to_filename(self, module_name: str) -> str:
if '_' in module_name: # submodule
s = module_name.lower()
if self.id in ('gcc', 'intel', 'intel-cl'):
if self.id in {'gcc', 'intel', 'intel-cl'}:
filename = s.replace('_', '@') + '.smod'
elif self.id in ('pgi', 'flang'):
elif self.id in {'pgi', 'flang'}:
filename = s.replace('_', '-') + '.mod'
else:
filename = s + '.mod'

@ -75,7 +75,7 @@ class JavaCompiler(BasicLinkerIsCompilerMixin, Compiler):
def compute_parameters_with_absolute_paths(self, parameter_list: T.List[str],
build_dir: str) -> T.List[str]:
for idx, i in enumerate(parameter_list):
if i in ['-cp', '-classpath', '-sourcepath'] and idx + 1 < len(parameter_list):
if i in {'-cp', '-classpath', '-sourcepath'} and idx + 1 < len(parameter_list):
path_list = parameter_list[idx + 1].split(os.pathsep)
path_list = [os.path.normpath(os.path.join(build_dir, x)) for x in path_list]
parameter_list[idx + 1] = os.pathsep.join(path_list)

@ -1320,7 +1320,7 @@ class CLikeCompiler(Compiler):
# don't work
m = env.machines[self.for_machine]
if not (m.is_windows() or m.is_cygwin()):
if name in ['dllimport', 'dllexport']:
if name in {'dllimport', 'dllexport'}:
return False, False
return self.compiles(self.attribute_check_func(name), env,

@ -228,7 +228,7 @@ class VisualStudioLikeCompiler(Compiler, metaclass=abc.ABCMeta):
for i in args:
# -mms-bitfields is specific to MinGW-GCC
# -pthread is only valid for GCC
if i in ('-mms-bitfields', '-pthread'):
if i in {'-mms-bitfields', '-pthread'}:
continue
if i.startswith('-LIBPATH:'):
i = '/LIBPATH:' + i[9:]
@ -361,7 +361,7 @@ class VisualStudioLikeCompiler(Compiler, metaclass=abc.ABCMeta):
def get_crt_compile_args(self, crt_val: str, buildtype: str) -> T.List[str]:
if crt_val in self.crt_args:
return self.crt_args[crt_val]
assert crt_val in ['from_buildtype', 'static_from_buildtype']
assert crt_val in {'from_buildtype', 'static_from_buildtype'}
dbg = 'mdd'
rel = 'md'
if crt_val == 'static_from_buildtype':
@ -385,7 +385,7 @@ class VisualStudioLikeCompiler(Compiler, metaclass=abc.ABCMeta):
def has_func_attribute(self, name: str, env: 'Environment') -> T.Tuple[bool, bool]:
# MSVC doesn't have __attribute__ like Clang and GCC do, so just return
# false without compiling anything
return name in ['dllimport', 'dllexport'], False
return name in {'dllimport', 'dllexport'}, False
def get_argument_syntax(self) -> str:
return 'msvc'

@ -274,7 +274,7 @@ class InternalDependency(Dependency):
assert isinstance(result, InternalDependency)
memo[id(self)] = result
for k, v in self.__dict__.items():
if k in ['libraries', 'whole_libraries']:
if k in {'libraries', 'whole_libraries'}:
setattr(result, k, copy.copy(v))
else:
setattr(result, k, copy.deepcopy(v, memo))

@ -152,9 +152,9 @@ class BoostLibraryFile():
self.version_lib = '{}_{}'.format(self.vers_raw[0], self.vers_raw[1])
# Detecting library type
if self.nvsuffix in ['so', 'dll', 'dll.a', 'dll.lib', 'dylib']:
if self.nvsuffix in {'so', 'dll', 'dll.a', 'dll.lib', 'dylib'}:
self.static = False
elif self.nvsuffix in ['a', 'lib']:
elif self.nvsuffix in {'a', 'lib'}:
self.static = True
else:
raise UnknownFileException(self.path)
@ -177,7 +177,7 @@ class BoostLibraryFile():
for i in tags:
if i == 'mt':
self.mt = True
elif len(i) == 3 and i[1:] in ['32', '64']:
elif len(i) == 3 and i[1:] in {'32', '64'}:
self.arch = i
elif BoostLibraryFile.reg_abi_tag.match(i):
self.runtime_static = 's' in i
@ -316,13 +316,13 @@ class BoostLibraryFile():
# If no vscrt tag present, assume that it fits ['/MD', '/MDd', '/MT', '/MTd']
if not vscrt:
return True
if vscrt in ['/MD', '-MD']:
if vscrt in {'/MD', '-MD'}:
return not self.runtime_static and not self.runtime_debug
elif vscrt in ['/MDd', '-MDd']:
elif vscrt in {'/MDd', '-MDd'}:
return not self.runtime_static and self.runtime_debug
elif vscrt in ['/MT', '-MT']:
elif vscrt in {'/MT', '-MT'}:
return (self.runtime_static or not self.static) and not self.runtime_debug
elif vscrt in ['/MTd', '-MTd']:
elif vscrt in {'/MTd', '-MTd'}:
return (self.runtime_static or not self.static) and self.runtime_debug
mlog.warning(f'Boost: unknown vscrt tag {vscrt}. This may cause the compilation to fail. Please consider reporting this as a bug.', once=True)

@ -52,8 +52,8 @@ def get_dep_identifier(name: str, kwargs: T.Dict[str, T.Any]) -> 'TV_DepID':
# 'default_options' is only used in fallback case
# 'not_found_message' has no impact on the dependency lookup
# 'include_type' is handled after the dependency lookup
if key in ('version', 'native', 'required', 'fallback', 'allow_fallback', 'default_options',
'not_found_message', 'include_type'):
if key in {'version', 'native', 'required', 'fallback', 'allow_fallback', 'default_options',
'not_found_message', 'include_type'}:
continue
# All keyword arguments are strings, ints, or lists (or lists of lists)
if isinstance(value, list):

@ -214,7 +214,7 @@ class Python3DependencySystem(SystemDependency):
return None
elif pyplat == 'win32':
return '32'
elif pyplat in ('win64', 'win-amd64'):
elif pyplat in {'win64', 'win-amd64'}:
return '64'
mlog.log(f'Unknown Windows Python platform {pyplat!r}')
return None

@ -136,7 +136,7 @@ class _MPIConfigToolDependency(ConfigToolDependency):
for f in args:
if self._is_link_arg(f):
result.append(f)
if f in ('-L', '-Xlinker'):
if f in {'-L', '-Xlinker'}:
include_next = True
elif include_next:
include_next = False

@ -36,7 +36,7 @@ def parse(lines: T.Iterable[str]) -> T.List[T.Tuple[T.List[str], T.List[str]]]:
if c in {'\\', '$'}:
escape = c
continue
elif c in (' ', '\n'):
elif c in {' ', '\n'}:
if out != '':
if in_deps:
deps.append(out)

@ -306,7 +306,7 @@ def detect_cpu_family(compilers: CompilersDict) -> str:
trial = 'ppc64'
elif trial.startswith(('powerpc', 'ppc')) or trial in {'macppc', 'power macintosh'}:
trial = 'ppc'
elif trial in ('amd64', 'x64', 'i86pc'):
elif trial in {'amd64', 'x64', 'i86pc'}:
trial = 'x86_64'
elif trial in {'sun4u', 'sun4v'}:
trial = 'sparc64'
@ -353,7 +353,7 @@ def detect_cpu(compilers: CompilersDict) -> str:
else:
trial = platform.machine().lower()
if trial in ('amd64', 'x64', 'i86pc'):
if trial in {'amd64', 'x64', 'i86pc'}:
trial = 'x86_64'
if trial == 'x86_64':
# Same check as above for cpu_family

@ -160,7 +160,6 @@ class CommandLineParser:
def run(self, args):
implicit_setup_command_notice = False
pending_python_deprecation_notice = False
# If first arg is not a known command, assume user wants to run the setup
# command.
known_commands = list(self.commands.keys()) + ['-h', '--help']
@ -187,8 +186,8 @@ class CommandLineParser:
# Bump the version here in order to add a pre-exit warning that we are phasing out
# support for old python. If this is already the oldest supported version, then
# this can never be true and does nothing.
if command in ('setup', 'compile', 'test', 'install') and sys.version_info < (3, 7):
pending_python_deprecation_notice = True
pending_python_deprecation_notice = \
command in {'setup', 'compile', 'test', 'install'} and sys.version_info < (3, 7)
try:
return options.run_func(options)

@ -104,7 +104,7 @@ def autodetect_options(options: 'argparse.Namespace', sample: bool = False) -> N
if f.suffix == '.c':
options.language = 'c'
break
if f.suffix in ('.cc', '.cpp'):
if f.suffix in {'.cc', '.cpp'}:
options.language = 'cpp'
break
if f.suffix == '.cs':

@ -237,7 +237,7 @@ def is_module_library(fname):
if hasattr(fname, 'fname'):
fname = fname.fname
suffix = fname.split('.')[-1]
return suffix in ('gir', 'typelib')
return suffix in {'gir', 'typelib'}
class ModuleReturnValue:

@ -874,7 +874,7 @@ class GnomeModule(ExtensionModule):
for girtarget in girtargets:
for lang, compiler in girtarget.compilers.items():
# XXX: Can you use g-i with any other language?
if lang in ('c', 'cpp', 'objc', 'objcpp', 'd'):
if lang in {'c', 'cpp', 'objc', 'objcpp', 'd'}:
ret.append((lang, compiler))
break

@ -194,7 +194,7 @@ class PythonSystemDependency(SystemDependency, _PythonDependencyBase):
return None
elif self.platform == 'win32':
return '32'
elif self.platform in ('win64', 'win-amd64'):
elif self.platform in {'win64', 'win-amd64'}:
return '64'
mlog.log(f'Unknown Windows Python platform {self.platform!r}')
return None
@ -727,7 +727,7 @@ class PythonModule(ExtensionModule):
# on various platforms, let's not give up just yet, if an executable
# named python is available and has a compatible version, let's use
# it
if not python.found() and name_or_path in ['python2', 'python3']:
if not python.found() and name_or_path in {'python2', 'python3'}:
python = PythonExternalProgram('python')
if python.found():

@ -61,17 +61,17 @@ def run(options):
coredata = cdata.load(options.builddir)
backend = coredata.get_option(OptionKey('backend'))
for k, v in sorted(coredata.__dict__.items()):
if k in ('backend_options', 'base_options', 'builtins', 'compiler_options', 'user_options'):
if k in {'backend_options', 'base_options', 'builtins', 'compiler_options', 'user_options'}:
# use `meson configure` to view these
pass
elif k in ['install_guid', 'test_guid', 'regen_guid']:
elif k in {'install_guid', 'test_guid', 'regen_guid'}:
if all_backends or backend.startswith('vs'):
print(k + ': ' + v)
elif k == 'target_guids':
if all_backends or backend.startswith('vs'):
print(k + ':')
dump_guids(v)
elif k in ['lang_guids']:
elif k == 'lang_guids':
if all_backends or backend.startswith('vs') or backend == 'xcode':
print(k + ':')
dump_guids(v)

@ -420,7 +420,7 @@ class Rewriter:
if target in self.interpreter.assignments:
node = self.interpreter.assignments[target]
if isinstance(node, FunctionNode):
if node.func_name in ['executable', 'jar', 'library', 'shared_library', 'shared_module', 'static_library', 'both_libraries']:
if node.func_name in {'executable', 'jar', 'library', 'shared_library', 'shared_module', 'static_library', 'both_libraries'}:
tgt = self.interpreter.assign_vals[target]
return tgt
@ -440,7 +440,7 @@ class Rewriter:
if dependency in self.interpreter.assignments:
node = self.interpreter.assignments[dependency]
if isinstance(node, FunctionNode):
if node.func_name in ['dependency']:
if node.func_name == 'dependency':
name = self.interpreter.flatten_args(node.args)[0]
dep = check_list(name)
@ -952,15 +952,15 @@ class Rewriter:
while raw[end] != '=':
end += 1
end += 1 # Handle the '='
while raw[end] in [' ', '\n', '\t']:
while raw[end] in {' ', '\n', '\t'}:
end += 1
files[i['file']]['raw'] = raw[:start] + i['str'] + raw[end:]
for i in str_list:
if i['action'] in ['modify', 'rm']:
if i['action'] in {'modify', 'rm'}:
remove_node(i)
elif i['action'] in ['add']:
elif i['action'] == 'add':
files[i['file']]['raw'] += i['str'] + '\n'
# Write the files back

@ -49,10 +49,10 @@ def run(argsv: T.List[str]) -> int:
capture_file = ''
for j in i:
if j in ['>', '>>']:
if j in {'>', '>>'}:
stdout = subprocess.PIPE
continue
elif j in ['&>', '&>>']:
elif j in {'&>', '&>>'}:
stdout = subprocess.PIPE
stderr = subprocess.STDOUT
continue

@ -118,7 +118,7 @@ def gnu_syms(libfilename: str, outfilename: str) -> None:
# Store the size of symbols pointing to data objects so we relink
# when those change, which is needed because of copy relocations
# https://github.com/mesonbuild/meson/pull/7132#issuecomment-628353702
if line_split[1].upper() in ('B', 'G', 'D') and len(line_split) >= 4:
if line_split[1].upper() in {'B', 'G', 'D'} and len(line_split) >= 4:
entry += [line_split[3]]
result += [' '.join(entry)]
write_if_changed('\n'.join(result) + '\n', outfilename)

@ -47,7 +47,7 @@ def run(args: T.List[str]) -> int:
tool_name = args[0]
srcdir_name = args[1]
os.chdir(srcdir_name)
assert tool_name in ['cscope', 'ctags', 'etags']
assert tool_name in {'cscope', 'ctags', 'etags'}
res = globals()[tool_name]()
assert isinstance(res, int)
return res

@ -1002,7 +1002,7 @@ def get_library_dirs() -> T.List[str]:
return unixdirs
# FIXME: this needs to be further genericized for aarch64 etc.
machine = platform.machine()
if machine in ('i386', 'i486', 'i586', 'i686'):
if machine in {'i386', 'i486', 'i586', 'i686'}:
plat = 'i386'
elif machine.startswith('arm'):
plat = 'arm'

@ -613,7 +613,7 @@ class Resolver:
def is_git_full_commit_id(self, revno: str) -> bool:
result = False
if len(revno) in (40, 64): # 40 for sha1, 64 for upcoming sha256
if len(revno) in {40, 64}: # 40 for sha1, 64 for upcoming sha256
result = all(ch in '0123456789AaBbCcDdEeFf' for ch in revno)
return result

Loading…
Cancel
Save