Exemplo n.º 1
0
 def test_get_logo_width(self):
     """Test `logos.get_logo_width` behavior"""
     self.assertEqual(get_logo_width(['{c[0]}   {c[1]}'], 2), 3)
     self.assertEqual(get_logo_width(['{c[0]} {{ {c[1]}']), 3)
     self.assertEqual(
         get_logo_width([
             '{c[0]}   {c[1]}>>>>{c[2]}<<<<{c[3]}',
             '{c[0]}   {c[1]}>>>>{c[2]}<<<<<{c[3]}'  # Ignored from computation...
         ]),
         11)
Exemplo n.º 2
0
    def test_distribution_logos_width(self):
        """
        Check that each distribution logo got a _consistent_ width across its lines.

        For this test, we have to trick the `get_logo_width` call.
        We actually pass each logos line as if they were a "complete" logo.
        """
        for distribution in Distributions:
            # Make Archey compute the logo width.
            logo_width = get_logo_width(LOGOS_DICT[distribution])
            # Then, check that each logo line got the same effective width.
            for i, line in enumerate(LOGOS_DICT[distribution][1:], start=1):
                line_width = get_logo_width([line])
                self.assertEqual(
                    line_width,
                    logo_width,
                    msg=
                    '[{0}] line index {1}, got an unexpected width {2} (expected {3})'
                    .format(distribution, i, line_width, logo_width))
Exemplo n.º 3
0
    def test_distribution_logos_consistency(self):
        """
        Verify each distribution identifier got a logo module.
        Verify each distribution logo module contain `LOGO` & `COLORS` ("truthy") attributes.
        Also check they got _consistent_ widths across their respective lines.
        Additionally verify they don't contain any (useless) empty line.

        This test also indirectly checks `lazy_load_logo_module` behavior!
        """
        distributions_identifiers = Distributions.get_distribution_identifiers(
        )

        for i, logo_module_info in enumerate(pkgutil.iter_modules(
                logos.__path__),
                                             start=1):
            # `iter_modules` yields `pkgutil.ModuleInfo` named tuple starting with Python 3.6.
            # So we manually extract the module name from `(module_finder, name, ispkg)` tuple.
            logo_module_name = logo_module_info[1]

            # Check each logo module name corresponds to a distribution identifier.
            self.assertIn(logo_module_name,
                          distributions_identifiers,
                          msg='No distribution identifier for [{0}]'.format(
                              logo_module_name))

            logo_module = lazy_load_logo_module(logo_module_name)

            # Attributes checks.
            self.assertTrue(
                getattr(logo_module, 'LOGO', []),
                msg='[{0}] logo module missing `LOGO` attribute'.format(
                    logo_module_name))
            self.assertTrue(
                getattr(logo_module, 'COLORS', []),
                msg='[{0}] logo module missing `COLORS` attribute'.format(
                    logo_module_name))

            # Make Archey compute the logo width.
            logo_width = get_logo_width(logo_module.LOGO)

            # Then, check that each logo line got the same effective width.
            for j, line in enumerate(logo_module.LOGO[1:], start=1):
                # Here we gotta trick the `get_logo_width` call.
                # We actually pass each logo line as if it was a "complete" logo.
                line_width = get_logo_width([line])

                # Width check.
                self.assertEqual(
                    line_width,
                    logo_width,
                    msg=
                    '[{0}] line index {1}, got an unexpected width {2} (expected {3})'
                    .format(logo_module_name, j, line_width, logo_width))

                # Non-empty line check.
                self.assertTrue(
                    Colors.remove_colors(line).strip(),
                    msg='[{0}] line index {1}, got an useless empty line'.
                    format(logo_module_name, j))

        # Finally, check each distributions identifier got a logo!
        # pylint: disable=undefined-loop-variable
        self.assertEqual(i,
                         len(distributions_identifiers),
                         msg='[{0}] Expected {1} logo modules, got {2}'.format(
                             logo_module_name, len(distributions_identifiers),
                             i))
Exemplo n.º 4
0
    def test_distribution_logos_consistency(self):
        """
        Verify each distribution identifier got a logo module.
        Verify each distribution logo module contain `LOGO` & `COLORS` ("truthy") attributes.
        Also check they got _consistent_ widths across their respective lines.
        Additionally verify they don't contain any (useless) empty line.

        This test also indirectly checks `lazy_load_logo_module` behavior!
        """
        distributions_identifiers = Distributions.get_identifiers()

        for i, logo_module_info in enumerate(pkgutil.iter_modules(
                logos.__path__),
                                             start=1):

            # Check each logo module name corresponds to a distribution identifier.
            self.assertIn(
                logo_module_info.name,
                distributions_identifiers,
                msg=f'No distribution identifier for [{logo_module_info.name}]'
            )

            logo_module = lazy_load_logo_module(logo_module_info.name)

            # Attributes checks.
            self.assertTrue(
                getattr(logo_module, 'LOGO', []),
                msg=
                f'[{logo_module_info.name}] logo module missing `LOGO` attribute'
            )
            self.assertTrue(
                getattr(logo_module, 'COLORS', []),
                msg=
                f'[{logo_module_info.name}] logo module missing `COLORS` attribute'
            )

            # Compute once and for all the number of defined colors for this logo.
            nb_colors = len(logo_module.COLORS)

            # Make Archey compute the logo (effective) width.
            logo_width = get_logo_width(logo_module.LOGO, nb_colors)

            # Then, check that each logo line got the same effective width.
            for j, line in enumerate(logo_module.LOGO[1:], start=1):
                # Here we gotta trick the `get_logo_width` call.
                # We actually pass each logo line as if it was a "complete" logo.
                line_width = get_logo_width([line], nb_colors)

                # Width check.
                self.assertEqual(
                    line_width,
                    logo_width,
                    msg=
                    '[{}] line index {}, got an unexpected width {} (expected {})'
                    .format(logo_module_info.name, j, line_width, logo_width))

                # Non-empty line check.
                self.assertTrue(
                    Colors.remove_colors(line.format(c=[''] *
                                                     nb_colors)).strip(),
                    msg=
                    f'[{logo_module_info.name}] line index {j}, got an useless empty line'
                )

        # Finally, check each distributions identifier got a logo!
        # pylint: disable=undefined-loop-variable
        self.assertEqual(i,
                         len(distributions_identifiers),
                         msg='[{}] Expected {} logo modules, got {}'.format(
                             logo_module_info.name,
                             len(distributions_identifiers), i))
Exemplo n.º 5
0
    def _output_text(self):
        """
        Finally render the output entries.
        It handles text centering additionally to value and colors replacing.
        """
        # Let's copy the logo (so we don't modify the constant!)
        logo = LOGOS_DICT[self._distribution].copy()
        logo_width = get_logo_width(logo, len(self._colors_palette))

        # Let's center the entries and the logo (handles odd numbers)
        height_diff = len(logo) - len(self._results)
        if height_diff >= 0:
            self._results[0:0] = [''] * (height_diff // 2)
            self._results.extend([''] * (len(logo) - len(self._results)))
        else:
            colored_empty_line = [
                str(self._colors_palette[0]) + ' ' * logo_width
            ]
            logo[0:0] = colored_empty_line * (-height_diff // 2)
            logo.extend(colored_empty_line * (len(self._results) - len(logo)))

        text_wrapper = TextWrapper(width=(get_terminal_size().columns -
                                          logo_width),
                                   expand_tabs=False,
                                   replace_whitespace=False,
                                   drop_whitespace=False,
                                   break_on_hyphens=False,
                                   max_lines=1,
                                   placeholder='...')
        placeholder_length = len(text_wrapper.placeholder)

        # Using `TextWrapper`, shortens each entry to remove any line overlapping
        for i, entry in enumerate(self._results):
            # Shortens the entry according to the terminal width.
            # We have to remove any ANSI color, or the result would be skewed.
            wrapped_entry = text_wrapper.fill(Colors.remove_colors(entry))
            placeholder_offset = (placeholder_length if wrapped_entry.endswith(
                text_wrapper.placeholder) else 0)

            # By using previous positions, re-inserts ANSI colors back in the wrapped string.
            for color_match in ANSI_ECMA_REGEXP.finditer(entry):
                match_index = color_match.start()
                if match_index <= len(wrapped_entry) - placeholder_offset:
                    wrapped_entry = (wrapped_entry[:match_index] +
                                     color_match.group() +
                                     wrapped_entry[match_index:])

            # Add a color reset character before the placeholder (if any).
            # Rationale :
            # We cannot set `Colors.CLEAR` in the placeholder as it would skew its internals.
            if placeholder_offset:
                wrapped_entry = (wrapped_entry[:-placeholder_length] +
                                 str(Colors.CLEAR) +
                                 wrapped_entry[-placeholder_length:])

            self._results[i] = wrapped_entry

        # Merge entry results to the distribution logo.
        logo_with_entries = os.linesep.join([
            logo_part + entry_part
            for logo_part, entry_part in zip(logo, self._results)
        ])

        try:
            print(
                logo_with_entries.format(c=self._colors_palette) +
                str(Colors.CLEAR))
        except UnicodeError:
            print("""\
Your locale or TTY does not seem to support UTF-8 encoding.
Please disable Unicode within your configuration file.\
""",
                  file=sys.stderr)
Exemplo n.º 6
0
    def _output_text(self) -> None:
        """
        Finally render the output entries.
        It handles text centering additionally to value and colors replacing.
        """
        # Compute the effective logo "width" from the loaded ASCII art.
        logo_width = get_logo_width(self._logo, len(self._colors))

        # Let's center the entries and the logo (handles odd numbers)
        height_diff = len(self._logo) - len(self._results)
        if height_diff >= 0:
            self._results[0:0] = [''] * (height_diff // 2)
            self._results.extend([''] * (len(self._logo) - len(self._results)))
        else:
            colored_empty_line = [str(self._colors[0]) + ' ' * logo_width]
            self._logo[0:0] = colored_empty_line * (-height_diff // 2)
            self._logo.extend(colored_empty_line *
                              (len(self._results) - len(self._logo)))

        # When writing to a pipe (for instance), prevent `TextWrapper` from truncating output.
        if not sys.stdout.isatty():
            text_width = cast(int, float("inf"))
        else:
            text_width = get_terminal_size().columns - logo_width - len(
                self.__LOGO_RIGHT_PADDING)

        text_wrapper = TextWrapper(width=text_width,
                                   expand_tabs=False,
                                   replace_whitespace=False,
                                   drop_whitespace=False,
                                   break_on_hyphens=False,
                                   max_lines=1,
                                   placeholder='...')
        placeholder_length = len(text_wrapper.placeholder)

        # Using `TextWrapper`, shortens each entry to remove any line overlapping
        for i, entry in enumerate(self._results):
            # Shortens the entry according to the terminal width.
            # We have to remove any ANSI color, or the result would be skewed.
            wrapped_entry = text_wrapper.fill(Colors.remove_colors(entry))
            placeholder_offset = (placeholder_length if wrapped_entry.endswith(
                text_wrapper.placeholder) else 0)

            # By using previous positions, re-inserts ANSI colors back in the wrapped string.
            for color_match in ANSI_ECMA_REGEXP.finditer(entry):
                match_index = color_match.start()
                if match_index <= len(wrapped_entry) - placeholder_offset:
                    wrapped_entry = (wrapped_entry[:match_index] +
                                     color_match.group() +
                                     wrapped_entry[match_index:])

            # Add a color reset character before the placeholder (if any).
            # Rationale :
            # We cannot set `Colors.CLEAR` in the placeholder as it would skew its internals.
            if placeholder_offset:
                wrapped_entry = (wrapped_entry[:-placeholder_length] +
                                 str(Colors.CLEAR) +
                                 wrapped_entry[-placeholder_length:])

            self._results[i] = wrapped_entry

        # Merge entry results to the distribution logo.
        logo_with_entries = os.linesep.join([
            f"{logo_part}{self.__LOGO_RIGHT_PADDING}{entry_part}"
            for logo_part, entry_part in zip(self._logo, self._results)
        ])

        try:
            print(logo_with_entries.format(c=self._colors) + str(Colors.CLEAR))
        except UnicodeError as unicode_error:
            raise ArcheyException("""\
Your locale or TTY does not seem to support UTF-8 encoding.
Please disable Unicode within your configuration file.\
""") from unicode_error