Last active
August 9, 2025 13:44
-
-
Save F4Jonatas/01cecb84c1e504fee5eef59f573114bf to your computer and use it in GitHub Desktop.
<%Python Printing Template%>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # -*- coding: utf-8 -* | |
| # LICENSE | |
| # Apache License 2.0 | |
| import re | |
| import types | |
| from typing import Pattern as regex | |
| ### | |
| # <% Printing Template %> | |
| # Simple string printing - template. | |
| # Sometimes, I need to display text with colors and styles, | |
| # but without having to add a complex library to my project. | |
| # With that in mind, I created this simple and straightforward solution | |
| # to display text with colors and styles using only a basic template string. | |
| # | |
| # Unlike rich, it is more flexible to use and does not require the installation of additional libraries. | |
| # | |
| # As there are still improvements to be made, I left an option to debug the process being done, for future analysis. | |
| # Any improvements or suggestions are welcome. | |
| # | |
| # | |
| # printt( | |
| # template [string/required] Text template to print. | |
| # sep [string/optional] Printed between objects. Default: " ". | |
| # end [string/optional] Appended to the end of the statement. Default: "\n". | |
| # file [object/optional] An object with write(string) method. Default: sys.stdout. | |
| # flush [boolean/optional] (Py >= 3.3) Default: False. | |
| # debug [boolean/optional] Show the process being performed in each template. Default: False. | |
| # ) | |
| # | |
| # Text Color: use c or color | |
| # printt( '<%color:#ff5555 Any Message%>' ) | |
| # | |
| # Background Color: use f or fill | |
| # printt( '<%fill:56,56,56 Any Message%>' ) | |
| # | |
| # Bold Style: use b or bold | |
| # printt( '<%bold Any Message%>' ) | |
| # | |
| # Italic Style: use i or italic. | |
| # printt( '<%italic Any Message%>' ) | |
| # | |
| # Dim Style: use d or dim. | |
| # printt( '<%dim Any Message%>' ) | |
| # | |
| # Underline Style: use u or underline. | |
| # printt( '<%underline Any Message%>' ) | |
| # | |
| # Blink Style: use blink. | |
| # printt( '<%blink Any Message%>' ) | |
| # | |
| # Conceal Style: use conceal. | |
| # printt( '<%conceal Any Message%>' ) | |
| # | |
| # Strike Style: use s or strike. | |
| # printt( '<%strike Any Message%>' ) | |
| # | |
| # Reverse Style: use r or reverse. | |
| # printt( '<%reverse Any Message%>' ) | |
| # | |
| # ANSI Code: use @. ANSI cannot be combined. | |
| # printt( '<%@0;32 Any Message%>' ) | |
| # | |
| # Any attribute can be combined with "-". Except for the ANSI code. | |
| # printt( '<%u-i-f:56,56,56-c:#ff5555 Any Message%>' ) | |
| ### | |
| class printt: | |
| # Constants | |
| __match: regex = re.compile( r'(<%\s*[^\x00]+)([\x00]+)' ) | |
| __keys : regex = re.compile( r'(?i)(?<=%)\s*([@vitafcundermbolsk-]*)(\s*(\d+;\d+)|:\s*(((?P<hex1>#[a-f\d]+)|(?P<rgb1>\d{1,3},\d{1,3},\d{1,3}))(?P<second>[corfil-]+:((?P<hex2>#[a-f\d]+)|(?P<rgb2>\d{1,3},\d{1,3},\d{1,3})))?))?\s?' ) | |
| __key2 : regex = re.compile( r'(?i)-(?P<key>[corfil]+):((?P<hex>#[a-f\d]+)|(?P<rgb>\d{1,3},\d{1,3},\d{1,3}))' ) | |
| __ctype: regex = re.compile( r'(?i)[#,]') | |
| @staticmethod | |
| def extractcor( self, color: str ) -> list | tuple: | |
| tipo: str = self.__ctype.search( color ).group(0) | |
| if tipo == ',': | |
| return color.split( ',' ) | |
| elif tipo == '#': | |
| color : str = color.lstrip( '#' ) | |
| lv : str = len( color ) | |
| color : tuple = tuple( int( color[ i:i + lv // 3 ], 16 ) for i in range( 0, lv, lv // 3 )) | |
| return ( | |
| str( color[0] ), | |
| str( color[1] ), | |
| str( color[2] ) | |
| ) | |
| # Loop through all templates | |
| @staticmethod | |
| def loop( self, template: str, debug: bool = False ): | |
| # template = repr( template ) | |
| for find in self.__match.finditer( re.sub( '%>', chr(0), template )): | |
| # temp = repr().encode('unicode_escape').decode() | |
| template = re.sub( '\x00', '', re.sub( | |
| re.escape( find.group(1) + '%>' ), | |
| self.parser( self, find.group(0), debug ), | |
| template | |
| )) | |
| if self.__keys.findall( template ): | |
| template = self.loop( self, template, debug ) | |
| return template | |
| @staticmethod | |
| def parser( self, template: str, debug: bool = False ) -> str: | |
| text : str = template[:-1] | |
| keywords : list = self.__keys.search( text ) | |
| kwordlist: regex = keywords.group(1).split( '-' ) | |
| exist : bool = False | |
| # Combine Fill and Color | |
| if keywords.group( 'second' ): | |
| kwordlist.append( | |
| self.__key2.search( keywords.group( 'second' )) \ | |
| .group( 'key' ) | |
| ) | |
| if debug: | |
| print( f'[TEMPLATE INPUT UNICODE]', f'`{ text.encode( 'unicode_escape' ).decode() }%>`\n', sep = '\n' ) | |
| print( f'[MATCH TEMPLATE AND CHANGE CLOSE TEMPLATE]', template, '', sep = '\n' ) | |
| if keywords and keywords.group(0) != '': | |
| print( f'[KEYWORDS]', keywords.groups(), '', sep = '\n' ) | |
| # ANSI Code | |
| if '@' in kwordlist: | |
| # Convert to raw string and remove single quotes. | |
| text: str = repr( text[ keywords.span()[1]: ])[1:-1] | |
| # text = re.escape( text[ keywords.span()[1]: ] ) | |
| if debug: | |
| print( f'[TEXT FOUNDED]', f'`{ text.encode( 'unicode_escape' ).decode() }`\n', sep = '\n' ) | |
| print( f'[CODE ANSI FOUNDED]', 'Code ANSI: ' + keywords.group(2), '', sep = '\n' ) | |
| print( f'[PARSED TEMPLATE]', f'`\033[{ keywords.group(2) }m{text}\033[0m`\n-----\n', sep = '\n' ) | |
| return f'\033[{ keywords.group(2) }m{text}\033[0m' | |
| else: | |
| # Remove open template. | |
| text: str = re.sub( fr'^(<%{ keywords.group(0) })|^(<%)', '', text ) | |
| if debug: | |
| print( f'[TEXT FOUNDED]', f'`{text}`\n', sep = '\n' ) | |
| if 's' in kwordlist or 'strike' in kwordlist: | |
| exist: bool = True | |
| text : str = '\033[9m' + text | |
| if 'conceal' in kwordlist: | |
| exist: bool = True | |
| text : str = '\033[8m' + text | |
| if 'r' in kwordlist or 'reverse' in kwordlist: | |
| exist: bool = True | |
| text : str = '\033[7m' + text | |
| if 'blink' in kwordlist: | |
| exist: bool = True | |
| text : str = '\033[5m' + text | |
| if 'u' in kwordlist or 'underline' in kwordlist: | |
| exist: bool = True | |
| text : str = '\033[4m' + text | |
| if 'i' in kwordlist or 'italic' in kwordlist: | |
| exist: bool = True | |
| text : str = '\033[3m' + text | |
| if 'd' in kwordlist or 'dim' in kwordlist: | |
| exist: bool = True | |
| text : str = '\033[2m' + text | |
| if 'b' in kwordlist or 'bold' in kwordlist: | |
| exist: bool = True | |
| text : str = '\033[1m' + text | |
| if 'f' in kwordlist or 'fill' in kwordlist: | |
| exist: bool = True | |
| # Check if there is a second color and if the second color is fill in. | |
| if keywords.group( 'second' ) and 'f' in self.__key2.search( keywords.group( 'second' )).group( 'key' ): | |
| cor = keywords.group( 'hex2' ) or keywords.group( 'rgb2' ) | |
| else: | |
| cor = keywords.group( 'hex1' ) or keywords.group( 'rgb1' ) | |
| cor = self.extractcor( self, cor ) | |
| if cor: | |
| if debug == True: | |
| print( f'[FILL COLOR FOUNDED]', cor, '', sep = '\n' ) | |
| text = '\033[48;2;{0[0]};{0[1]};{0[2]}m'.format( cor ) + text | |
| if 'c' in kwordlist or 'color' in kwordlist: | |
| exist: bool = True | |
| if keywords.group( 'second' ) and 'c' in self.__key2.search( keywords.group( 'second' )).group( 'key' ): | |
| cor = keywords.group( 'hex2' ) or keywords.group( 'rgb2' ) | |
| else: | |
| cor = keywords.group( 'hex1' ) or keywords.group( 'rgb1' ) | |
| cor = self.extractcor( self, cor ) | |
| if cor: | |
| if debug: | |
| print( f'[TEXT COLOR FOUNDED]', cor, '', sep = '\n' ) | |
| text = '\033[38;2;{0[0]};{0[1]};{0[2]}m'.format( cor ) + text | |
| # If there are no keywords, simply return the template. | |
| if not exist: | |
| text: str = template[:-1] + '%>' | |
| else: | |
| if debug: | |
| print( f'[PARSED TEMPLATE]', f'`\033[{ text }\033[0m`\n-----\n', sep = '\n' ) | |
| return text + '\033[0m' | |
| def __init__( self, template: str, debug: bool = False, **args ): | |
| template = self.loop( self, template, debug ) | |
| print( template, **args ) | |
| if __name__ == '__main__': | |
| # Testing the printt | |
| printt( '<%color:#ff5555 Any "Font Color" Message%>' ) | |
| printt( '<%fill:56,56,56 Any "Background Color" Message%>' ) | |
| printt( '<%bold Any "Bold" Message%>' ) | |
| printt( '<%italic Any "Italic" Message%>' ) | |
| printt( '<%dim Any "Dim" Message%>' ) | |
| printt( '<%underline Any "Underline" Message%>' ) | |
| printt( '<%blink Any "Blink" Message%>' ) | |
| printt( '<%conceal Any "Conceal" Message%>' ) | |
| printt( '<%strike Any "Strike" Message%>' ) | |
| printt( '<%reverse Any "Reverse" Message%>' ) | |
| printt( '<%@0;32 Any "ANSI Code" Message%>' ) | |
| printt( '<%u-i-c:#ff5555-f:56,56,56 Any "Combined" Message%>' ) | |
| printt( '<%u-i-c:255,255,255-f:#ff5555 Any "Combined" Message%>' ) | |
| printt( 'Founded <%b-i-fill:80,16,40 14 backups <%i-c:#cf8a00 of 39 <%s apps%>%>%>...' ) | |
| printt( r'<%@0;36 Transmission/2.77 D:\Download%>' ) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment