Ejemplo n.º 1
0
def discover_ur_install_path():
    """
    Tries to find Saved Games on this system

    Returns: Saved Games dir
    """
    from esst import FS
    LOGGER.debug('discovering UR install path')
    if not ATISConfig.UR_PATH():
        LOGGER.debug(
            'no UR install path in Config, looking it up in the registry')
        _ur_install_path = _get_ur_install_path_from_registry()

    else:
        LOGGER.debug('UR install path found in Config')
        _ur_install_path = Path(ATISConfig.UR_PATH())
        if not _ur_install_path.is_dir():
            LOGGER.error(
                'UR install path provided in config file is not a directory: %s',
                _ur_install_path)
            sys.exit(1)

    LOGGER.debug('using UR install path: %s', _ur_install_path)
    FS.ur_install_path = _ur_install_path
    URStatus.install_path = _ur_install_path
    FS.ur_settings_folder = Path(FS.saved_games_path, 'UniversRadio')
    URStatus.settings_folder = Path(FS.saved_games_path, 'UniversRadio')
    LOGGER.debug('UR settings folder: %s', FS.ur_settings_folder)
    FS.ur_voice_settings_file = Path(FS.ur_settings_folder, 'VoiceService.dat')
    URStatus.voice_settings_file = Path(FS.ur_settings_folder,
                                        'VoiceService.dat')
    LOGGER.debug('UR voice service data file: %s', FS.ur_voice_settings_file)
    utils.create_simple_backup(FS.ur_voice_settings_file,
                               file_must_exist=False)
def showfor(icao: list):
    """
    Show ATIS info for a specific airfield
    """
    icao_str = ''.join(icao).upper()
    try:
        info = get_info_for_icao(icao_str)
    except KeyError:
        LOGGER.error('ICAO not found in the list of currently active ATIS:  %s', icao_str)
        return
    if core.Status.metar == 'unknown':
        LOGGER.error('no weather information available at this time')
        return
    running = 'running' if URVoiceService.is_running() else 'not running'
    # type: ignore
    _weather = core.Status.metar.as_str()  # pylint: disable=no-member
    # type: ignore
    _metar = core.Status.metar.raw_metar_str  # pylint: disable=no-member
    info_str = f'UR voice service is {running}\n\n' \
               f'METAR:\n{_metar}\n\n' \
               f'Weather:\n{_weather}\n\n' \
               f'Active runway: {info.active_runway}\n' \
               f'Information ID: {info.info_letter}\n' \
               f'ATIS speech: {core.CTX.atis_speech}'
    LOGGER.info(info_str)
Ejemplo n.º 3
0
    async def run(self):
        """
        Infinite loop that manages a UDP socket and does two things:

        1. Retrieve incoming messages from DCS and update :py:class:`esst.core.status.status`
        2. Sends command to the DCS application via the socket
        """
        if not CTX.start_listener_loop:
            LOGGER.debug('skipping startup of socket loop')
            return

        try:
            self.sock.bind(self.server_address)
        except socket.error as exc:
            if exc.errno == 10048:
                LOGGER.error(
                    'cannot bind socket, maybe another instance of ESST is already running?'
                )
                sys.exit(-1)

        self.sock.settimeout(1)

        while not CTX.exit:
            await self._read_socket()
            await self._parse_commands()
            await self._monitor_server_startup()
            await self._monitor_server()
            await asyncio.sleep(0.1)

        self.sock.close()
        LOGGER.debug('end of listener loop')
Ejemplo n.º 4
0
    def set_priority(self):
        """
        Sets the DCS process CPU priority to the CFG value
        """
        def _command():
            if self.app.nice() != self.valid_priorities[
                    DCSConfig.DCS_CPU_PRIORITY()]:
                LOGGER.debug('setting DCS process priority to: %s',
                             DCSConfig.DCS_CPU_PRIORITY())
                self.app.nice(
                    self.valid_priorities[DCSConfig.DCS_CPU_PRIORITY()])

        time.sleep(15)
        while True:
            if DCSConfig.DCS_CPU_PRIORITY():
                if core.CTX.exit:
                    return
                if DCSConfig.DCS_CPU_PRIORITY(
                ) not in self.valid_priorities.keys():
                    LOGGER.error(
                        f'invalid priority: %s\n'
                        f'Choose one of: %s',
                        DCSConfig.DCS_CPU_PRIORITY(),
                        self.valid_priorities.keys(),
                    )
                    return
                self._work_with_dcs_process(_command)
            else:
                LOGGER.warning(
                    'no CPU priority given in config file for dcs.exe')
                return
            time.sleep(30)
Ejemplo n.º 5
0
def get_running_mission() -> typing.Union['MissionPath', str]:
    """

    Returns: currently running mission as a MissionPath instance

    """
    mission = None
    if core.Status.mission_file and core.Status.mission_file != 'unknown':
        mission_path = Path(core.Status.mission_file)
        if mission_path.parent == 'AUTO':
            mission_path = Path(mission_path.parent.parent, mission_path.name)
        mission = MissionPath(mission_path)

    else:
        try:
            dcs_settings = _get_settings_file_path().read_text()
        except FileNotFoundError:
            LOGGER.error(
                'please start a DCS server at least once before using ESST')
            sys.exit(1)
        else:
            for line in dcs_settings.split('\n'):
                if '[1]' in line:
                    mission = MissionPath(line.split('"')[1])
                    break

    if mission:
        LOGGER.debug('returning active mission: %s', mission.name)
        return mission

    LOGGER.error('current mission is "%s", but that file does not exist',
                 mission)
    return ''
Ejemplo n.º 6
0
    async def on_ready(self):
        """
        Triggers when the bot is ready.
        """
        if not self.ready:
            self._user = self.client.user
            await self._update_profile()
            LOGGER.debug('Logged in as: %s', self.client.user.name)
            try:
                self._server = set(self.client.servers).pop()
            except KeyError:
                LOGGER.error('Your discord bot has not server to connect to\n'
                             'Go to https://discordapp.com/developers/applications/me to create a bot, and note '
                             'the client ID.\n'
                             'Use the client ID in the following URL to join you bot to your Discord server:\n'
                             'https://discordapp.com/oauth2/authorize?client_id=CLIENT_ID&scope=bot')
            else:
                self._member = self.server.get_member(self.user.id)
                if self.user.display_name != DiscordBotConfig.DISCORD_BOT_NAME():
                    await self.client.change_nickname(self.member, DiscordBotConfig.DISCORD_BOT_NAME())
                await self._update_presence()

            await self.get_channel()

            self._ready = True
Ejemplo n.º 7
0
def get_latest_mission_from_github():
    """
    Downloads the latest mission from a Github repository

    The repository needs to have releases (tagged)
    The function will download the first MIZ file found in the latest release
    """
    if core.CTX.dcs_auto_mission:
        LOGGER.debug('getting latest mission from Github')
        commands.DCS.block_start('loading mission')
        if DCSConfig.DCS_AUTO_MISSION_GH_OWNER(
        ) and DCSConfig.DCS_AUTO_MISSION_GH_REPO():
            LOGGER.debug('looking for newer mission file')
            latest_version, asset_name, download_url = utils.get_latest_release(
                DCSConfig.DCS_AUTO_MISSION_GH_OWNER(),
                DCSConfig.DCS_AUTO_MISSION_GH_REPO())
            LOGGER.debug('latest release: %s', latest_version)
            local_file = MissionPath(Path(_get_mission_folder(), asset_name))
            if not local_file:
                LOGGER.info('downloading new mission: %s', asset_name)
                req = requests.get(download_url)
                if req.ok:
                    local_file.path.write_bytes(req.content)
                    local_file.set_as_active()
                else:
                    LOGGER.error('failed to download latest mission')
        else:
            LOGGER.warning('no config values given for [auto mission]')
        commands.DCS.unblock_start('loading mission')
    else:
        LOGGER.debug('skipping mission update')
Ejemplo n.º 8
0
def init() -> None:
    """
    Makes sure the configuration is valid before starting ESST

    :raise: SystemExit
    """
    # Setup elib_config
    elib_config.ELIBConfig.setup(
        app_version=__version__,
        app_name='ESST',
        config_file_path='esst.toml',
        config_sep_str='__',
    )

    # Write example config file
    elib_config.write_example_config('esst.toml.example')

    # Validate config
    try:
        elib_config.validate_config()
    except elib_config.ConfigMissingValueError as error:
        LOGGER.error('missing mandatory config value: %s', error.value_name)
        LOGGER.error(
            'please read "esst.toml.example" for instructions on how to setup the configuration for ESST'
        )
        sys.exit(1)

    for config in SentryConfigContext.__subclasses__():
        SENTRY.register_context(context_name=config.__name__,
                                context_provider=config)
Ejemplo n.º 9
0
def _get_server_settings_path() -> Path:
    if not FS.dcs_server_settings:
        LOGGER.error('FS.dcs_server_settings undefined')
        sys.exit(1)
    if not FS.dcs_server_settings.exists():
        LOGGER.error('please start a DCS server at least once before using ESST')
        sys.exit(1)
    return FS.dcs_server_settings
Ejemplo n.º 10
0
def external_ip():
    """
    Returns: external IP of this machine
    """
    try:
        return requests.get('https://api.ipify.org').text
    except requests.ConnectionError:
        LOGGER.error('unable to obtain external IP')
        return 'unknown'
Ejemplo n.º 11
0
 async def _monitor_server(self):
     await asyncio.sleep(0.1)
     if self.monitoring:
         if time.time() - self.last_ping > DCSConfig.DCS_PING_INTERVAL():
             LOGGER.error(
                 'It has been %s seconds since I heard from DCS. '
                 'It is likely that the server has crashed.',
                 DCSConfig.DCS_PING_INTERVAL())
             CTX.dcs_do_restart = True
             self.monitoring = False
Ejemplo n.º 12
0
def get_base_missions_folder() -> Path:
    """
    Returns the folder in which all ESST related missions are contained

    Returns: Path object

    """
    if FS.dcs_mission_folder:
        return FS.dcs_mission_folder

    LOGGER.error('FS.dcs_mission_folder undefined')
    sys.exit(1)
Ejemplo n.º 13
0
 async def _monitor_server_startup(self):
     await asyncio.sleep(0.1)
     if CTX.listener_monitor_server_startup:
         if self.startup_age is None:
             self.startup_age = time.time()
         if time.time(
         ) - self.startup_age > DCSConfig.DCS_START_GRACE_PERIOD():
             LOGGER.error(
                 f'DCS is taking more than %s seconds to start a '
                 'multiplayer server.\n'
                 'Something is wrong ...',
                 DCSConfig.DCS_START_GRACE_PERIOD())
             CTX.listener_monitor_server_startup = False
Ejemplo n.º 14
0
def initial_setup():
    """
    Runs at the start of the DCS loop, to initialize the first mission
    """
    LOGGER.debug('initializing first mission')
    mission = get_running_mission()
    if isinstance(mission, MissionPath):
        LOGGER.info('building METAR for initial mission: %s',
                    mission.orig_name)
        weather = elib_wx.Weather(str(mission.path))
        core.Status.metar = weather
        esst.atis.create.generate_atis(weather)
    else:
        LOGGER.error('no initial mission found')
Ejemplo n.º 15
0
    def restart(force: bool = False):
        """
        Sets the context to restart the DCS application

        Returns: None if restart is OK, err as a str otherwise

        """
        CANCEL_QUEUED_KILL.put(1)
        if DCS.there_are_connected_players() and not force:
            LOGGER.error(
                'there are connected players; cannot restart the server now '
                ' (use "--force" to kill anyway)')
            return
        LOGGER.debug('setting context for DCS restart')
        CTX.dcs_do_restart = True
Ejemplo n.º 16
0
def _get_current_mission_path() -> str:
    """
    Extracts the path of the current (first) defined mission in "serverSettings.lua"

    :return: path to the mission
    :rtype: str
    """
    server_settings = _get_server_settings_path()
    text: str = Path(server_settings).read_text()
    for line in text.split('\n'):
        match = _CURRENT_MIS_RE.match(line)
        if match:
            return match.group('mission_path')
    LOGGER.error('please start a DCS server at least once before using ESST')
    sys.exit(1)
Ejemplo n.º 17
0
    async def kill_running_app(self):  # noqa: C901
        """
        Kills the running DCS.exe process
        """
        async def _ask_politely():
            if not self.app or not self.app.is_running():
                return True
            LOGGER.debug('sending socket command to DCS for graceful exit')
            commands.LISTENER.exit_dcs()
            await asyncio.sleep(1)
            LOGGER.debug(
                'waiting on DCS to close itself (grace period: %s seconds)',
                DCSConfig.DCS_CLOSE_GRACE_PERIOD())
            now_ = utils.now()
            while self.app.is_running():
                await asyncio.sleep(1)
                if utils.now() - now_ > DCSConfig.DCS_CLOSE_GRACE_PERIOD():
                    LOGGER.debug('grace period time out!')
                    return False

            LOGGER.info('DCS closed itself, nice')
            return True

        async def _no_more_mr_nice_guy():
            if not self.app or not self.app.is_running():
                return True
            LOGGER.debug('killing dcs.exe application')
            self.app.kill()
            now_ = utils.now()
            while self.app.is_running():
                await asyncio.sleep(1)
                if utils.now() - now_ > 10:
                    return False

            return True

        core.CTX.dcs_do_kill = False
        await self._check_if_dcs_is_running()
        if not self.app or not self.app.is_running():
            LOGGER.debug('DCS process was not running')
            return
        LOGGER.info('closing DCS')
        if not await _ask_politely():
            LOGGER.info('DCS will not exit gracefully, killing it')
            if not await _no_more_mr_nice_guy():
                LOGGER.error('I was not able to kill DCS, something is wrong')
                raise RuntimeError()
        await self._check_if_dcs_is_running()
Ejemplo n.º 18
0
def download_mission_from_discord(discord_attachment,
                                  overwrite: bool = False,
                                  load: bool = False,
                                  force: bool = False):
    """
    Downloads a mission from a discord message attachment

    Args:
        force: force restart even with players connected
        discord_attachment: url to download the mission from
        overwrite: whether or not to overwrite an existing file
        load: whether or not to restart the server with the downloaded mission
    """
    url = discord_attachment['url']
    size = discord_attachment['size']
    filename = discord_attachment['filename']
    local_file = MissionPath(Path(_get_mission_folder(), filename))

    overwriting = ''
    if local_file:
        if overwrite:
            overwriting = ' (replacing existing file)'
        else:
            LOGGER.warning(
                'this mission already exists: %s\nuse "overwrite" to replace it',
                local_file.path)
            return

    LOGGER.info(
        'downloading: %s (%s) %s',
        filename,
        humanize.naturalsize(size),
        overwriting,
    )
    with requests.get(url) as response:
        local_file.path.write_bytes(response.content)

    if load:
        if commands.DCS.there_are_connected_players() and not force:
            LOGGER.error(
                'there are connected players; cannot restart the server now (use "force" to kill anyway)'
            )
            return
        LOGGER.info('restarting the server with this mission')
        local_file.set_as_active()
        commands.DCS.restart(force=force)
    else:
        LOGGER.info('download successful, mission is now available')
Ejemplo n.º 19
0
 def kill(force: bool = False, queue: bool = False):
     """Kills DCS application"""
     CANCEL_QUEUED_KILL.put(1)
     if DCS.there_are_connected_players():
         if not force:
             if queue:
                 DCS.queue_kill()
             else:
                 LOGGER.error(
                     'there are connected players; cannot kill the server now'
                     ' (use "--force" to kill anyway)')
             return
         else:
             LOGGER.warning('forcing kill with connected players')
     LOGGER.debug('setting context for DCS kill')
     CTX.dcs_do_kill = True
Ejemplo n.º 20
0
def get_esst_changelog_path() -> str:
    """

    Returns: changelog path

    """
    changelog_path = os.path.join(os.path.dirname(__file__), 'CHANGELOG.rst')
    if not os.path.exists(changelog_path):
        LOGGER.debug('changelog not found, trying from pkg_resource')
        changelog_path = pkg_resources.resource_filename(
            'esst', 'CHANGELOG.rst')
    if not os.path.exists(changelog_path):
        LOGGER.error('changelog not found')
        return ''

    return changelog_path
Ejemplo n.º 21
0
def delete(name: str):
    """
    Removes a mission file from the server
    """
    try:
        mission_number = int(name)
    except ValueError:
        mission = missions_manager.MissionPath(name)
        if not mission:
            LOGGER.error('mission file does not exist: %s', mission.path)
            return
    else:
        mission = _mission_index_to_mission_name(mission_number)
        if not mission:
            LOGGER.error(
                'invalid mission index: %s; use "!mission show" to see available indices',
                mission_number)
            return

    missions_manager.delete(mission)
Ejemplo n.º 22
0
    def discover_saved_games_path():
        """
        Tries to find Saved Games on this system

        Returns: Saved Games dir
        """
        from esst import ESSTConfig
        if not ESSTConfig.SAVED_GAMES_DIR():
            LOGGER.debug('no Saved Games path in Config, looking it up')
            return FS._get_saved_games_from_registry()

        LOGGER.debug('Saved Games path found in Config')
        base_sg = Path(ESSTConfig.SAVED_GAMES_DIR())
        if not base_sg.is_dir():
            LOGGER.error(
                'Saved Games dir provided in config file is invalid:  %s',
                base_sg)
            return FS._get_saved_games_from_registry()

        return base_sg
Ejemplo n.º 23
0
    async def on_message(self, message: discord.Message):  # noqa: C901
        """
        Triggers on any message received from the Discord server

        Args:
            message: message received

        """
        if message.author.id == self.member.id:
            return
        if message.channel != self.channel:
            return
        if DiscordBotConfig.DISCORD_ADMIN_ROLES():
            is_admin = bool([
                role for role in DiscordBotConfig.DISCORD_ADMIN_ROLES()  # pylint: disable=not-an-iterable
                if role in [role.name for role in message.author.roles]
            ])
        else:
            is_admin = True
        if message.attachments:
            for attach in message.attachments:
                if attach['filename'].endswith('.miz'):
                    if not is_admin:
                        LOGGER.error(
                            f'only users with privileges can load missions on the server'
                        )
                        return
                    overwrite = 'overwrite' in message.content
                    load = 'load' in message.content
                    force = 'force' in message.content
                    missions_manager.download_mission_from_discord(
                        attach, overwrite, load, force)
        if message.content.startswith('!'):
            LOGGER.debug('received "%s" command from: %s%s', message.content,
                         message.author.display_name,
                         " (admin)" if is_admin else "")

            self.parser.parse_discord_message(message.content, is_admin)
Ejemplo n.º 24
0
    def set_as_active(self, weather: elib_wx.Weather = None):
        """
        Write the settings file to set this mission as active

        :param weather: current weather; if not provided,  will be inferred from MIZ file
        :type weather: elib_wx.Weather
        """

        LOGGER.info('setting active mission to: %s', self.name)
        if not self:
            LOGGER.error('mission file not found: %s', self.path)
            return

        write_server_settings(str(self.path).replace('\\', '/'))

        if weather is None:
            LOGGER.debug('building metar from mission: %s', self.name)
            # noinspection SpellCheckingInspection
            weather = elib_wx.Weather(str(self.path))
            LOGGER.info('metar for %s:\n%s', self.name, weather)
        else:
            esst.atis.create.generate_atis(weather)
        core.Status.metar = weather
Ejemplo n.º 25
0
def text_to_speech(text: str,
                   file_path: typing.Union[str, Path],
                   overwrite: bool = False) -> Path:
    """
    Creates MP3 file from text

    Args:
        text: text to encode
        file_path: path to MP3 file
        overwrite: whether or not to overwrite existing file

    Returns: path to saved MP3

    """
    LOGGER.debug('%s\n->%s', text, file_path)
    file_path = Path(file_path)
    if file_path.exists() and not overwrite:
        LOGGER.error('"%s" already exists', file_path)
        raise FileExistsError(file_path)
    LOGGER.debug('encoding text')
    tts = gtts.gTTS(text=text, lang_check=False)
    LOGGER.debug('saving MP3 file')
    tts.save(str(file_path))
    return file_path
Ejemplo n.º 26
0
def _load(name, metar_or_icao, time, max_wind, min_wind, force):  # noqa: C901
    if max_wind or min_wind:
        LOGGER.warning(
            '"min_wind" and "max_wind" have been disabled for the time being')
    if name is None:
        mission = missions_manager.get_running_mission()
        if not mission:
            LOGGER.error('unable to retrieve current mission')
            return
    else:
        try:
            LOGGER.debug('trying to cast mission name into an int: %s', name)
            mission_number = int(name)
        except ValueError:
            LOGGER.debug('loading mission name: %s', name)
            mission = missions_manager.MissionPath(name)
            if not mission:
                LOGGER.debug('mission path not found: %s', mission.path)
                LOGGER.error('mission file not found: %s', mission.name)
                return
        else:
            LOGGER.debug('loading mission number: %s', mission_number)
            mission = _mission_index_to_mission_name(mission_number)
            if not mission:
                LOGGER.error(
                    'invalid mission index: %s; use "!mission  show" to see available indices',
                    mission_number)
                return

    LOGGER.info('loading mission file: %s', mission.path)
    if time:
        try:
            mission_time = elib_miz.MissionTime.from_string(time)
            LOGGER.info('setting mission time: %s', mission_time.iso_format)
        except elib_miz.exc.InvalidDateTimeString:
            LOGGER.error('invalid date-time string: %s', time)
            return
        except ValueError as err:
            LOGGER.error(err)
            return
    else:
        mission_time = None
    if metar_or_icao:
        LOGGER.info('analyzing METAR string: %s', metar_or_icao)
        try:
            weather_ = elib_wx.Weather(metar_or_icao)
            LOGGER.info('setting mission weather: %s', weather_.as_str())
        except elib_wx.BadStationError:
            LOGGER.error('wrong ICAO code: %s', metar_or_icao)
            return
        LOGGER.info('METAR: %s', weather_.raw_metar_str)
    else:
        LOGGER.info('building METAR from mission file')
        # noinspection SpellCheckingInspection
        weather_ = elib_wx.Weather(str(mission.path))
        LOGGER.info('METAR: %s', weather_.as_str())

    commands.DCS.block_start('loading mission')
    commands.DCS.kill(force=force)
    try:
        LOGGER.debug('waiting on DCS application to close')
        while core.Status.dcs_application != 'not running':
            sleep(1)
        LOGGER.debug('DCS has closed, carrying on')
        active_mission = mission
        if time:
            mission_time.apply_to_miz(str(mission.path),
                                      str(mission.auto.path),
                                      overwrite=True)
            active_mission = mission.auto
        if metar_or_icao:
            weather_.apply_to_miz(str(mission.path),
                                  str(mission.auto.path),
                                  overwrite=True)
            active_mission = mission.auto
        active_mission.set_as_active(weather_)
    finally:
        commands.DCS.unblock_start('loading mission')
Ejemplo n.º 27
0
def _execute_command(func, namespace_obj, pre_call=None):  # noqa: C901
    # noinspection SpellCheckingInspection
    """
    Assumes that `function` is a callable.  Tries different approaches
    to call it (with `namespace_obj` or with ordinary signature).
    Yields the results line by line.

    If :class:`~argh.exceptions.CommandError` is raised, its message is
    appended to the results (i.e. yielded by the generator as a string).
    All other exceptions propagate unless marked as wrappable
    by :func:`wrap_errors`.
    """
    if pre_call:
        LOGGER.debug('running pre_call: %s', pre_call)
        pre_call(namespace_obj)

    # namespace -> dictionary
    def _flat_key(key):
        return key.replace('-', '_')

    # noinspection SpellCheckingInspection
    def _call():
        # Actually call the function
        if getattr(func, ATTR_EXPECTS_NAMESPACE_OBJECT, False):
            result_ = func(namespace_obj)
        else:

            all_input = dict((_flat_key(k), v)
                             for k, v in vars(namespace_obj).items())

            # filter the namespace variables so that only those expected
            # by the actual function will pass

            spec = get_arg_spec(func)

            positional = [all_input[k] for k in spec.args]
            # noinspection SpellCheckingInspection
            kw_only = getattr(spec, 'kwonlyargs', [])
            keywords = dict((k, all_input[k]) for k in kw_only)

            # *args
            if spec.varargs:
                positional += getattr(namespace_obj, spec.varargs)

            # **kwargs
            varkw = getattr(spec, 'varkw', getattr(spec, 'keywords', []))
            if varkw:
                not_kwargs = [DEST_FUNCTION] + spec.args + [spec.varargs] + kw_only
                for k in vars(namespace_obj):
                    if k.startswith('_') or k in not_kwargs:
                        continue
                    keywords[k] = getattr(namespace_obj, k)

            result_ = func(*positional, **keywords)

        # Yield the results
        if isinstance(result_, (GeneratorType, list, tuple)):
            # yield each line ASAP, convert CommandError message to a line
            for line_ in result_:
                yield line_
        else:
            # yield non-empty non-iterable result as a single line
            if result_ is not None:
                yield result_

    # noinspection SpellCheckingInspection
    wrappable_exceptions = [CommandError, Exception]
    wrappable_exceptions += getattr(func, ATTR_WRAPPED_EXCEPTIONS, [])

    try:
        LOGGER.debug('running func: %s', func)
        result = _call()
        return '\n'.join(result)
    # pylint: disable=catching-non-exception
    except tuple(wrappable_exceptions) as exc:
        # pylint: disable=unnecessary-lambda
        processor = getattr(
            func, ATTR_WRAPPED_EXCEPTIONS_PROCESSOR,
            lambda exc_: '{0.__class__.__name__}: {0}'.format(exc_)
        )

        LOGGER.error(compat.text_type(processor(exc)))
        LOGGER.exception(exc)
Ejemplo n.º 28
0
    def dispatch(self,  # noqa: C901
                 argv=None,
                 add_help_command=True,
                 completion=True,  # pylint: disable=unused-argument
                 pre_call=None,
                 output_file=sys.stdout,  # pylint: disable=unused-argument
                 errors_file=sys.stderr,  # pylint: disable=unused-argument
                 raw_output=False,  # pylint: disable=unused-argument
                 namespace=None,
                 skip_unknown_args=False,
                 is_admin: bool = False, ):
        """
        Passes arguments to linked function

        Args:
            is_admin: is the user that issued the command an admin ?
            argv:
            add_help_command:
            completion:
            pre_call:
            output_file:
            errors_file:
            raw_output:
            namespace:
            skip_unknown_args:

        """
        try:
            for arg in argv:
                if arg == 'help':
                    argv.remove('help')
                    argv.append('--help')
            for arg in argv:
                if arg in ['-h', '--help']:
                    pre_call = _cancel_execution

            if argv is None:
                argv = sys.argv[1:]

            if add_help_command:
                if argv:
                    if argv[0] in ['help', '-h']:
                        argv.pop(0)
                        argv.append('--help')

            if skip_unknown_args:
                parse_args = self.parse_known_args
            else:
                parse_args = self.parse_args

            if not namespace:
                namespace = ArghNamespace()

            # this will raise SystemExit if parsing fails
            namespace_obj = parse_args(argv, namespace=namespace)

            func = _get_function_from_namespace_obj(namespace_obj)

            if func:
                if hasattr(func, 'protected_') and not is_admin:
                    LOGGER.error(f'only users with privileges have access to this command')
                    return None
                LOGGER.debug('running func: %s', func)
                return _execute_command(func, namespace_obj, pre_call=pre_call)

            # no commands declared, can't dispatch; display help message
            return [self.format_usage()]

        except SystemExit:
            pass
Ejemplo n.º 29
0
def _get_mission_folder() -> Path:
    if FS.dcs_mission_folder:
        return FS.dcs_mission_folder

    LOGGER.error('FS.dcs_mission_folder undefined')
    sys.exit(0)
Ejemplo n.º 30
0
def _get_settings_file_path() -> Path:
    if FS.dcs_server_settings:
        return FS.dcs_server_settings

    LOGGER.error('FS.dcs_server_settings undefined')
    sys.exit(1)