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)
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))
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))
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))
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)
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