Source code for aiida.cmdline.commands.cmd_verdi

# -*- coding: utf-8 -*-
###########################################################################
# Copyright (c), The AiiDA team. All rights reserved.                     #
# This file is part of the AiiDA code.                                    #
#                                                                         #
# The code is hosted on GitHub at https://github.com/aiidateam/aiida-core #
# For further information on the license, see the LICENSE.txt file        #
# For further information please visit http://www.aiida.net               #
###########################################################################
"""The main `verdi` click group."""
import difflib

import click
from click import shell_completion

from aiida import __version__
from aiida.cmdline.params import options, types

GIU = (
    'ABzY8%U8Kw0{@klyK?I~3`Ki?#qHQ&IIM|J;6yB`9_+{&w)p(JK}vokj-11jhve8xcx?dZ>+9nwrEF!x*S>9A+EWYrR?6GA-u?jFa+et65GF@1+D{%'
    '8{C~xjt%>uVM4RTSS?j2M)XH%T#>M{K$lE2XGD`<aYaOFUit|Wh*Q?-n)Bs}n0}hc2!oAkoBQyEOaSkqfd=oq;sTs(x@(H&qOKnXDFA52~gq4CvNbk'
    '%p9&pE+KH!2lm^MThvE$xC2x*>RS0T67213wbAs!SZmn+;(-m!>f(T@e%@oxd`yRBp9nu+9N`4xv8AS@O$CaQ;7FXzM=ug^$?3ta2551EDL`wK4|Cm'
    '%RnJdS#0UF<by)B<XQ+8vZ}j$b$@4Q1#kj{G|g6{1#bLd+w)SZ16_wTnfo<QXMd-s4ImikcvSj#7~2Yq-1l@K)y|aS%KqzrV$m)VgR$xs8bGr7%$lJ'
    '_ObpObgS_mhZ7M97P}ATen9Zil^7;P;<I08Mv_;!$Eb2P)F|&t+wT?>wVwe<Fez}g$`Zi>DkcfdNjtUv1N^iSQui#TL(q!FmIeKb!yW4|L`@!@-4x6'
    'B6I^ptRdH+4o0ODM;1_f^}4@LMe@#_YHz0wQdq@d)@n)uYNtAb2OLo&fpBkct5{~3kbRag^_5QG%qrTksHMXAYAQoz1#2wtHCy0}h?CJtzv&@Q?^9r'
    'd&02;isB7NJMMr7F@>$!ELj(sbwzIR4)rnch=oVZrG;8)%R6}FUk*fv2O&!#ZA)$HloK9!es&4Eb+h=OIyWFha(8PPy9u?NqfkuPYg;GO1RVzBLX)7'
    'ORMM>1hEM`-96mGjJ+A!e-_}4X{M|4CkKE~uF4j+LW#6IsFa*_da_mLqzr)E<`%ikthkMO2<vdLNlWMLBrceLb&%p<SlbT6NAh+Tz~72^U2(&}V&4H'
    'd8r#{;M;(*9swiCZ2RIx2&yLd0wV^AQs5$V63{q89vFwvV_?Vk!HuDTPr83yG~Wm}YG_*0gIL7B~{>>65cNMtpDE*VejqZV^MyewPJJAS*VM6jY;QY'
    '#g7gOKgPbFg{@;YDL6Gbxxr|2T&BQunB?PBetq?X<bp0b)qASa|!TspwLp%9CE7Y#XI@}Wa3E6#mZC62W0F#k>>jW1hFF7&>EaYkKYqIa_ld(Z@AJT'
    '+lJ(Pd;+?<&&M>A0agti19^z3n4Z6_WG}c~_+XHyJI_iau7+V$#YA$pJ~H)yHEVy1D?5^Sw`tb@{nnNNo=eSMZLf0>m^A@7f{y$nb_HJWgLRtZ?<Vq'
    'Glx*4Tkeba)%-_D&vG1uUL0(DEK)&;4yGQ@C_0d4x%#)?G?XfN*{PN40X3QeC3@;*9uMwABesu!8?(0#>x2?*>SwM?JoQ>p|-1ZRU0#+{^UhK22+~o'
    'R9k7rh<eeM8~?e5)U+OloQYk-ebZsXE8KFmR5;uE)C;wlw}@dIbx-{${l+HbC|PrK*6{Q(q6jMpni4Be``)PJJRU>(GH9y|jm){jY9_xAI4N_EfU#4'
    'taTUXFY4a4l$v=N-+f+w&wuH;Z(6p6#=n8XwlZ;*L&-rcL~T_vEm@#-Xi8&g06!MO+R(<NRBkE$(0Y_~^2C_k<o~_a3*HAHukZKXCs8^y%UZZVvze'
)


[docs]def _start_of_option(value: str) -> bool: """Check if the value looks like the start of an option. This is an adaptation of :py:func:`click.shell_completion._start_of_option` that simply add ``.``, ``~``, ``$`` as the characters that are interpreted as the start of a filepath, and so not the start of an option. This will ensure that filepaths starting with these characters are autocompleted once again. Here ``.`` indicates a relative path, ``~`` indicates the home directory, and ``$`` allows to expand environment variables such as ``$HOME`` and ``$PWD``. """ if not value: return False # Allow characters that typically designate the start of a path. return not value[0].isalnum() and value[0] not in ['/', '.', '~', '$']
shell_completion._start_of_option = _start_of_option # pylint: disable=protected-access
[docs]class VerdiCommandGroup(click.Group): """Custom class for ``verdi`` top-level command group."""
[docs] @staticmethod def add_verbosity_option(cmd): """Apply the ``verbosity`` option to the command, which is common to all ``verdi`` commands.""" # Only apply the option if it hasn't been already added in a previous call. if cmd is not None and 'verbosity' not in [param.name for param in cmd.params]: cmd = options.VERBOSITY()(cmd) return cmd
[docs] def fail_with_suggestions(self, ctx, cmd_name): """Fail the command while trying to suggest commands to resemble the requested ``cmd_name``.""" # We might get better results with the Levenshtein distance or more advanced methods implemented in FuzzyWuzzy # or similar libs, but this is an easy win for now. matches = difflib.get_close_matches(cmd_name, self.list_commands(ctx), cutoff=0.5) if not matches: # Single letters are sometimes not matched so also try with a simple startswith matches = [c for c in sorted(self.list_commands(ctx)) if c.startswith(cmd_name)][:3] if matches: formatted = '\n'.join(f'\t{m}' for m in sorted(matches)) ctx.fail(f'`{cmd_name}` is not a {self.name} command.\n\nThe most similar commands are:\n{formatted}') else: ctx.fail(f'`{cmd_name}` is not a {self.name} command.\n\nNo similar commands found.')
[docs] def get_command(self, ctx, cmd_name): """Return the command that corresponds to the requested ``cmd_name``. This method is overridden from the base class in order to two functionalities: * If the command is found, automatically add the verbosity option. * If the command is not found, attempt to provide a list of suggestions with existing commands that resemble the requested command name. Note that if the command is not found and ``resilient_parsing`` is set to True on the context, then the latter feature is disabled because most likely we are operating in tab-completion mode. """ if int(cmd_name.lower().encode('utf-8').hex(), 16) == 0x6769757365707065: import base64 import gzip click.echo(gzip.decompress(base64.b85decode(GIU.encode('utf-8'))).decode('utf-8')) return None cmd = super().get_command(ctx, cmd_name) if cmd is not None: return self.add_verbosity_option(cmd) # If this command is called during tab-completion, we do not want to print an error message if the command can't # be found, but instead we want to simply return here. However, in a normal command execution, we do want to # execute the rest of this method to try and match commands that are similar in order to provide the user with # some hints. The problem is that there is no one canonical way to determine whether the invocation is due to a # normal command execution or a tab-complete operation. The `resilient_parsing` attribute of the `Context` is # designed to allow things like tab-completion, however, it is not the only purpose. For now this is our best # bet though to detect a tab-complete event. When `resilient_parsing` is switched on, we assume a tab-complete # and do nothing in case the command name does not match an actual command. if ctx.resilient_parsing: return self.fail_with_suggestions(ctx, cmd_name)
[docs] def group(self, *args, **kwargs): """Ensure that sub command groups use the same class but do not override an explicitly set value.""" kwargs.setdefault('cls', self.__class__) return super().group(*args, **kwargs)
# Pass the version explicitly to ``version_option`` otherwise editable installs can show the wrong version number @click.command(cls=VerdiCommandGroup, context_settings={'help_option_names': ['--help']}) @options.PROFILE(type=types.ProfileParamType(load_profile=True), expose_value=False) @options.VERBOSITY() @click.version_option(__version__, package_name='aiida_core', message='AiiDA version %(version)s') def verdi(): """The command line interface of AiiDA."""