Beispiel #1
0
    def validate_contents(self, contents, extra_parameters=None) -> List[str]:
        # Check characters contents is indeed a list of strings
        if not isinstance(contents, list):
            msg = (f'Expected the characters list to be a list, got a '
                   f'{type(contents).__name__}: {contents}.')
            raise ServerError.FileSyntaxError(msg)

        for (i, character) in enumerate(contents.copy()):
            if character is None:
                msg = (f'Expected all character names to be defined, but character {i} was not.')
                raise ServerError.FileSyntaxError(msg)
            if not isinstance(character, (str, float, int, bool, complex)):
                msg = (f'Expected all character names to be strings or numbers, but character '
                       f'{i}: {character} was not a string or number.')
                raise ServerError.FileSyntaxError(msg)

            # Otherwise, character i is valid. Cast it as string to deal with YAML doing
            # potential casting of its own
            contents[i] = str(character)

        return contents
Beispiel #2
0
    def validate_contents(self, contents, extra_parameters=None) -> List[str]:
        # Check background contents is indeed a list of strings
        if not isinstance(contents, list):
            msg = (f'Expected the background list to be a list, got a '
                   f'{type(contents).__name__}: {contents}.')
            raise ServerError.FileSyntaxError(msg)

        for (i, background) in enumerate(contents.copy()):
            if background is None:
                msg = (f'Expected all background names to be defined, '
                       f'found background {i} was not.')
                raise ServerError.FileSyntaxError(msg)
            if not isinstance(background, (str, float, int, bool, complex)):
                msg = (
                    f'Expected all background names to be strings or numbers, '
                    f'found background {i}: {background} was not a string or number.'
                )
                raise ServerError.FileSyntaxError(msg)

            # Otherwise, background i is valid. Cast it as string to deal with YAML doing
            # potential casting of its own
            contents[i] = str(background)

        return contents
Beispiel #3
0
    def load_music(self,
                   music_list_file: str = 'config/music.yaml',
                   server_music_list: bool = True) -> List[Dict[str, Any]]:
        music_list = ValidateMusic().validate(music_list_file)

        if server_music_list:
            self.music_list = music_list
            try:
                self.build_music_list(music_list=music_list)
            except ServerError.FileSyntaxError as exc:
                msg = (
                    f'File {music_list_file} returned the following error when loading: '
                    f'`{exc.message}`')
                raise ServerError.FileSyntaxError(msg)

        return music_list.copy()
Beispiel #4
0
    def reload(self):
        # Keep backups in case of failure
        backup = [
            self.char_list.copy(),
            self.music_list.copy(),
            self.backgrounds.copy()
        ]

        # Do a dummy YAML load to see if the files can be loaded and parsed at all first.
        reloaded_assets = [
            self.load_characters, self.load_backgrounds, self.load_music
        ]
        for reloaded_asset in reloaded_assets:
            try:
                reloaded_asset()
            except ServerError.YAMLInvalidError as exc:
                # The YAML exception already provides a full description. Just add the fact the
                # reload was undone to ease the person who ran the command's nerves.
                msg = (f'{exc} Reload was undone.')
                raise ServerError.YAMLInvalidError(msg)
            except ServerError.FileSyntaxError as exc:
                msg = f'{exc} Reload was undone.'
                raise ServerError(msg)

        # Only on success reload
        self.load_characters()
        self.load_backgrounds()

        try:
            self.load_music()
        except ServerError as exc:
            self.char_list, self.music_list, self.backgrounds = backup
            msg = (
                'The new music list returned the following error when loading: `{}`. Fix the '
                'error and try again. Reload was undone.'.format(exc))
            raise ServerError.FileSyntaxError(msg)
Beispiel #5
0
    def validate_contents(self,
                          contents,
                          extra_parameters=None) -> List[Dict[str, Any]]:
        # Check music list contents is indeed a list
        if not isinstance(contents, list):
            msg = (f'Expected the music list to be a list, got a '
                   f'{type(contents).__name__}: {contents}.')
            raise ServerError.FileSyntaxError(msg)

        # Check top level description is ok
        for (i, item) in enumerate(contents.copy()):
            if item is None:
                msg = (
                    f'Expected all music list items to be defined, but item {i} was not.'
                )
                raise ServerError.FileSyntaxError(msg)
            if not isinstance(item, dict):
                msg = (
                    f'Expected all music list items to be dictionaries, but item '
                    f'{i}: {item} was not a dictionary.')
                raise ServerError.FileSyntaxError(msg)
            if set(item.keys()) != {'category', 'songs'}:
                msg = (
                    f'Expected all music list items to have exactly two keys: category and '
                    f'songs, but item {i} had keys {set(item.keys())}')
                raise ServerError.FileSyntaxError(msg)

            category, songs = item['category'], item['songs']
            if category is None:
                msg = (
                    f'Expected all music list categories to be defined, but category {i} was '
                    f'not.')
                raise ServerError.FileSyntaxError(msg)
            if songs is None:
                msg = (
                    f'Expected all music list song descriptions to be defined, but song '
                    f'description {i} was not.')
                raise ServerError.FileSyntaxError(msg)

            if not isinstance(category, (str, float, int, bool, complex)):
                msg = (
                    f'Expected all music list category names to be strings or numbers, but '
                    f'category {i}: {category} was not a string or number.')
                raise ServerError.FileSyntaxError(msg)
            if not isinstance(songs, list):
                msg = (
                    f'Expected all music list song descriptions to be a list, but '
                    f'description {i}: {songs} was not a list.')
                raise ServerError.FileSyntaxError(msg)

        # Check each song description dictionary is ok
        for (i, item) in enumerate(contents.copy()):
            category = item['category']
            songs = item['songs']
            for (j, song) in enumerate(songs):
                if song is None:
                    msg = (
                        f'Expected all music list song descriptions to be defined, but song '
                        f'description {j} in category {i}: {category} was not defined.'
                    )
                    raise ServerError.FileSyntaxError(msg)
                if not isinstance(song, dict):
                    msg = (
                        f'Expected all music list song descriptions to be dictionaries: but '
                        f'song description {j} in category {i}: {category} was not a '
                        f'dictionary: {song}.')
                    raise ServerError.FileSyntaxError(msg)
                if 'name' not in song.keys():
                    msg = (
                        f'Expected all music lists song descriptions to have a name as key, but '
                        f'song description {j} in category {i}: {category} '
                        f'had keys {set(song.keys())}.')
                    raise ServerError.FileSyntaxError(msg)
                if not set(song.keys()).issubset({'name', 'length', 'source'}):
                    msg = (
                        f'Expected all music list song description keys be contained in the set '
                        f"{{'name', 'length', 'source'}} but song description {j} in category "
                        f'{i}: {category} had keys {set(song.keys())}.')
                    raise ServerError.FileSyntaxError(msg)

                name = song['name']
                length = song['length'] if 'length' in song else -1
                source = song['source'] if 'source' in song else ''

                if not isinstance(name, (str, float, int, bool, complex)):
                    msg = (
                        f'Expected all music list song names to be strings or numbers, but '
                        f'song {j}: {name} in category {i}: {category} was not a string or '
                        f'number.')
                    raise ServerError.FileSyntaxError(msg)
                if not isinstance(length, (int, float)):
                    msg = (
                        f'Expected all music list song lengths to be numbers, but song {j}: '
                        f'{name} in category {i}: {category} had non-numerical length {length}.'
                    )
                    raise ServerError.FileSyntaxError(msg)
                if not isinstance(source, (str, float, int, bool, complex)):
                    msg = (
                        f'Expected all music list song sources to be strings or numbers, but '
                        f'song {j}: {name} in category {i}: {category} was not a string or '
                        f'number.')

                # Prevent names that may be interpreted as a directory with . or ..
                # This prevents sending the client an entry to their music list which may be read as
                # including a relative directory
                if Constants.includes_relative_directories(name):
                    info = (
                        f'Music {name} could be interpreted as referencing current or '
                        f'parent directories, so it is invalid.')
                    raise ServerError.FileSyntaxError(info)
        return contents