Skip to main content

Python development tools

Project description

BB_AppUtils

Utilities for developing python applications/scripts

README is incomplete!

Contents

Shell Script

apputils --help

AppUtils

module apputils

Multiple functions/tools for various tasks in python applications

AppData

apputils.appdata.AppData

class AppData:
    def __init__(self, appname = PROJECT_APP_NAME):
        """
          'appname': set application name for base directory of app files
        """

return to contents

AppData Functions
AppData.appdir()
    - Locates and creates application directories as needed according to application name

AppData.sysdata()
    - Retrieve default system folders and info
    def appdir( self, d, *folders, file = '', datefile = '',
                create_dir = True, unique = False, move_existing = False):
        """
        *args
          'd': system directory type
                - varies on different operating systems - see sysdata
                - 'cache' : users cache directory
                - 'config': users config directory
                - 'data'  : main app directory
                - 'launch': directory where app launchers are placed
          '*folders': path names to append to returned path

        **kwargs
          'create_dir': create directories before they're returned
          'datefile': strftime format to add to filename if file != None
                       - adds date data before extension or at end of filename
                       - if set to 'timestamp', the standard timestamp (without
                         nanoseconds) is used
                       - if set to True, the default dating format is used
                       - default date format: '_%Y-%m-%d_%H-%M-%S'
          'file': add a filename to the end of the returned path
          'move_existing': if file is set and exists, will move the old file to
                           '~filename'. If that already exists, then '~filename (n)',
                           'n' being the first number to make a unique pathname
                             - only applies if 'file' is set

          'unique': avoid overwriting a file by setting unique to True
                     - renames file by adding an integer to the end
                     - does nothing if 'file' is not set
        """

    def sysdata(self, *args, as_tuple = False):
        """
        Return systeminfo
          *args:
            app      : [ 'cache'||'data'||'config'||'launch' ]
            home     : [ 'home'||'userhome' ]
            user name: [ 'user'||'username' ]
            os type  : [ 'os'||'system' ]

          **kwargs:
            'as_tuple': if getting a directory and True, return as a tuple instead
                        of using os.path.join
        """

return to contents

Date

Imports for working with datetime objects
    def __call__(self, *args, **kwargs):
        """
        Use date functions
          kwargs:
            'date'    : assign date otherwise date is set to now
                         - must be a datetime object
            'strftime': datetime.strftime()
            'strptime': datetime.strptime()
            'parse'   : parse a date with dateutil.parser
                         - ignored if 'date' is in kwargs
        """

return to contents

Path

apputils._import.Path

Imports for working with system files and directories
class Path:
    """
    File path modification tools
        - contains mostly builtins ... mostly (some aliased)
    """
    from os import ( access as __access__,
                     environ as env,
                     getcwd as pwd,
                     getlogin,
                     link as ln,
                     listdir as ls,
                     makedirs as mkdir,
                     readlink,
                     remove as rm,
                     removedirs as rmdir_R,
                     rmdir,
                     stat,
                     walk,
                     R_OK as __R_OK__,
                     W_OK as __W_OK__,
                     X_OK as __X_OK__   )
    from shutil import ( copyfile as cp,
                         copytree as cp_R,
                         move as mv     )
    from os.path import ( abspath as abs,
                          basename as bn,
                          dirname as dn,
                          exists,
                          expanduser,
                          isabs,
                          isfile,
                          isdir,
                          islink,
                          join as jn,
                          sep as pathsep,
                          splitext      )

return to contents

Tools

apputils.tools

MISSING DOCUMENTATION

appendFile
apputils.tools.appendFile
def appendFile( path, data, *, mode = 'a', encoding = 'utf-8', **kwargs ):

return to contents

attribNameFromString
apputils.tools.attribNameFromString
def attribNameFromString( string ):

return to contents

filterList
apputils.tools.filterList
def filterList( _list, match, index = None, *,
                not_in = False, _type = None):

return to contents

filter_dir
apputils.tools.filter_dir
def filter_dir(attr, **kwargs):

return to contents

getBoolFromString
apputils.tools.getBoolFromString
def getBoolFromString(s):

return to contents

getIndexed
apputils.tools.getIndexed
def getIndexed(item, args):
      """
    getIndexed(item, args) >>> ( item, args )
        Get item from list of arguments. Returns ( indexed item, args minus item )
      if found else ( None, original args )
    """

return to contents

isBinary
apputils.tools.isBinary
def isBinary(filename):

return to contents

isSafeAttribName
apputils.tools.isSafeAttribName
def isSafeAttribName(name):

return to contents

listDepth
apputils.tools.listDepth
def listDepth( L: list, *, tuples = False ):

return to contents

listFlatten
apputils.tools.listFlatten
def listFlatten( *L, depth: int = 1, tuples = False ):

return to contents

matchSimilar
apputils.tools.matchSimilar
def matchSimilar( find, _list, *,
                  ratio = STRING_SEQUENCE_MATCH_RATIO,
                  getall = False, getalldata = False ):

return to contents

mkData
apputils.tools.mkData
def mkData(obj):
    """
    Make data dictionary from dataclass, ignoring keys that start with '_'
        - embedded dataclasses are recursively moved to a dict() object as well
    """

return to contents

moveListItem
apputils.tools.moveListItem
def moveListItem( L: list, from_index, to_index, *, items = False ):

return to contents

parseDate
apputils.tools.parseDate
def parseDate(s, fmt = ''):

return to contents

readFile
apputils.tools.readFile
def readFile( path, *, mode = None, encoding = 'utf-8', **kwargs ):

return to contents

sortDiverseList
apputils.tools.sortDiverseList
def sortDiverseList( L: list|tuple, *,
                     reverse = False, unknowns_last = False ):
    """
    Sorts lists|tuples containing both str and int
        - integers are returned before strings
        - always returns a list object

        Types other than int|float|str are sorted by their '__name__'
      attribute if existing. These will come before the unkown types,
      which will be first in the list, unless 'unknowns_last' is set
      to True, and sorted by their __repr__ value.
    """

return to contents

stringFilter
apputils.tools.stringFilter
def stringFilter( obj: Any, *,
                  func = None, start = [], end = [],
                  re_search: re.compile = None, re_match: re.compile = None ):

return to contents

timeDeltaString
apputils.tools.timeDeltaString
def timeDeltaString(t):
    """
    Convert time from timedelta object to xx:xx:xx
    """

return to contents

tupleCalc
apputils.tools.tupleCalc
def tupleCalc( a, b, op, *, diff = False, round_int = False ):

return to contents

uniqueName
apputils.tools.uniqueName
def uniqueName(path, *, move_existing = False):

return to contents

writeFile
apputils.tools.writeFile
def writeFile( path, data, *, mode = 'w', encoding = 'utf-8', **kwargs ):

return to contents

BBLogger

module bblogger

Custom log formatting and file handling

Subclasses of the python logging module

Environment Variables

Set logging options using shell variables

"""
User Shell Options:

    'BBLOGGER_APPNAME'         : (string) set a name for the root logger
    'BBLOGGER_AVOID_OVERWRITE' : (bool value) don't overwrite log file [default = True]
    'BBLOGGER_COLORED_OUTPUT'  : (bool value) use color in console logging [default = True]
    'BBLOGGER_CONSOLE_FORMAT'  : 'debug'|'basic' provide more info on each line with 'debug' [default = 'basic']
    'BBLOGGER_CONSOLE_STREAM'  : output stream - 'stderr'[default] or 'stdout'
    'BBLOGGER_FILE_FORMAT'     : 'html'|'plaintext' [default = 'html']
    'BBLOGGER_FILE_ROTATE_TIME': amount of seconds before log file is overwritten (default -1)
                                  - ignored if less than 0
    'BBLOGGER_FILE_VERBOSITY'  : (1 - 5) logging level for file output [default = 1]
    'BBLOGGER_FILE_WRITE_MODE' : (string) 'a' - append, 'b' - backup, or 'w' - write
    'BBLOGGER_FILEPATH'        : path to log file to [default = None]
    'BBLOGGER_MAX_FILE_SIZE'   : maximum size for logfile in bytes before rotation
    'BBLOGGER_ROOT_VERBOSITY'  : (1 - 5) log level to set the root logger [default = 1]
    'BBLOGGER_VERBOSITY'       : (1 - 5) log level for console output [default = 3]

"""
  • run script in shell bblogger-shell-settings to view available environment variables

getLogger

bblogger.logger.getLogger

Use this for initial logger to set the logging class and load environment variables

def getLogger( name, level = None, **opts ):
    """
    Set custom logger class and return logger
      - only use this for initial call to logger. Use logging.getLogger() for
        further logging modules

        'name'    = Name of returned logger
        'level'   = Log level for the returned logger. Defaults to 1.

        **opts:
              More options for logger. To print the log to a file, 'filepath'
            must be present in the opts.

            'appname'          : [ DEFAULT 'BB-Logger' ] Application name
            'console'          : [ DEFAULT = True ] Print logging to console
            'consoleformat'    : [ DEFAULT = 'basic' ] Console formatting. Options are
                                    'basic' or 'debug'.
            'color'            : [ DEFAULT = True ] colorized console logging
            'filepath'         : [ DEFAULT None ] The path for a file to be written to. The
                                    directory for the file must exist.
                                    - shell var =
            'filelevel'        : [ DEFAULT = 1 ] Set log level for file. Default is 1, DEBUGGING
                                    - must be set with initial call to logger.getLogger()
                                    - shell var =
            'rootlevel'        : [ DEFAULT = 1 ] Set the root logger level
                                    - usually best to keep this default
            'fileformat'       : [ DEFAULT = 'html' ] Text formatting for file - 'plaintext'
                                    or 'html'
                                    - shell var =
            'file_write_mode'  : [ DEFAULT = 'a' ] write mode for file stream
                                    - 'a': append, 'b': backup old logfile, 'w': overwrite old logfile
            'overwrite_time'   : [ DEFAULT = -1 ] Ignored if < 0. Amount of seconds before logfile
                                    is overwritten
                                    - shell var =
            'avoid_overwrite'  : [ DEFAULT = True ] Don't overwrite the logfile if existing


              A new file will be created if not existing as long as the directory
            already exists. If only a filename is given for 'path', the file will
            be written in the user's HOME folder. Extra options should only need
            applied for the first time initiating a logger in your app/script.

        NOTE: Shell environment variables will override programatic calls to BBLogger
    """

return to contents

Logger Class

bblogger.logger.BBLogger
class BBLogger( logging.getLoggerClass() ):
    """
    Console and file logging, formatted with BBFormatter
        - options are set through logger.getLogger() with initial call
        - subsequent loggers should be called with python's logging
          module: logging.getLogger()
    """

  # Added (or overridden) functions

    def setLevel(self, level = None, *opts, set_global = False):
        """
        Set logging level for individual log object
            'set_global': set global verbosity level
                           - optionally use 'opts'
                'opts':
                    'file_level': include setting file log level
                    'ignore_console': don't set level of already initiated
                                      console loggers

            - acceptable modes:
                'debug'    | logging.DEBUG    | 10 | 1 <or> 0
                'info'     | logging.INFO     | 20 | 2
                'warning'  | logging.WARNING  | 30 | 3
                'error'    | logging.ERROR    | 40 | 4
                'critical' | logging.CRITICAL | 50 | 5
        """

    def set_format(self, formatting):
        """
        Change formatting for console logging
            'basic' - simple, nicely formatted messaging
            'debug' - more info pertaining to each message
                      * defaults to log level 1
        """

return to contents

Custom Formatter

bblogger.logger.BBFormatter
class BBFormatter(logging.Formatter):
    """
    BBFormatter - Color logging output with ansi or html

        mode = 'basic' (console)
                Basic logging for end user messages

               'debug' (console)
                More output information for debugging

               'html' (file)
                Uses html format to color logs to send to an html file or gui
                program that supports html formatting

               'plaintext' (file)
                No escapes inserted for logging plaintext to a file

    """

return to contents

Logger Example

# __init__.py

from bblogger import getLogger
log = getLogger( __name__, 3,
                 appname   = "The_Awesomest_Application_Ever",
                 filepath  = "/path/to/logfile",
                 filelevel = 1,
                 consoleformat = 'basic',
                 fileformat = 'html' )

    # NOTE subsequent calls to bblogger.getLogger will return a logger with initial options

# -------------------------------------------------------------

# __main__.py

import logging
log = logging.getLogger(__name__)

return to contents

TextTools

module texttools
  • Collection of console text and ansi manipulation tools
  • Add/create text colors for console
  • Includes limited html formatting
  • Adds a custom color template to user app folder that is loaded on import
  • Some consoles may not accept all available attributes
  • Ansi and AnsiList for working with strings containing ansi escapes

Ansi

texttools.ansitools.Ansi

Work with strings containing ansi escape codes

    - Reports accurate (printed) string length
    - Built in method to convert to an html `<span>` string
        - wrapper for TextTools classmethod `esc2html`
    - Provides a 'clean' (escapes removed) version of the string
    - Will not throw an 'IndexError' with __getitem__, instead will return an empty string
    - Overloaded Constructor
        - If multiple strings are passed to Ansi(), an [AnsiList](#ansilist) is returned
    - Tries to always return an Ansi object when standard strings are inserted/added
        - This won't work when using an Ansi object in an f-string
class Ansi(str):
    """
        Holds a string object with escapes while still reporting an accurate
      length. also provides a cleaned version (no escapes).

        Using f-strings will 're-class' the object to a standard str() object.
      It is recommended to use the '+' operator to join standard string objects
      with Ansi objects so that they remain Ansi types.
    """
    def __init__(self, string = '', **kwargs):
        """
        Create an Ansi string
        ========================================================================
        **kwargs:
            'last_esc_on_slices': add last ansi escape to end of string if
                                    slice leaves it out
                                    - DEFAULT = False
            'reset_on_slice'    : add ansi text reset to end of slices
                                    - overrides 'last_esc_on_slices' if True
                                    - DEFAULT = False
            'strsep'            : passed to AnsiList if split is used

            * Also will look for environment variables 'LAST_ESC_ON_SLICES' and
                'RESET_ON_SLICES' in os.environ. Boolean values are expected.
                Anything other than 'false', '0', or 'no' will set these values
                to True.
        """

Ansi is a subclass of str, made to facilitate the formatting and printing of strings containing ansi escape codes. Because escape codes, on terminals that accept escapes, don't use any columns when printed, it makes it hard to do print formatting and text coloring. This attempts to account for that by manipulating the character count. This greatly changes iteration, indexing, and slicing of an Ansi object.

It can be confusing to use getitem and iter methods. Iterating through 'string' will separate the escapes as single characters. In the case of 'string', the last yielded item is '\x1b[0m', yet string[-1] returns '!\x1b[0m', prepending the last non escaped character to the ansi escape. This is by design because the Ansi and AnsiList classes are specifically made to assist in printing and print formatting with strings containing ansi escapes. The attributes clean and string are provided in Ansi() as native str objects for more complicated uses. Another attribute escapes will list all the escapes in the string. This was the best I could come up with for handling iteration, indexing, and slicing.

Because of the complications of iteration and slicing, it will never throw an IndexError when using getitem. Instead, an empty string '' will be returned.

Here is showing some characteristics of Ansi objects:

>>> from texttools import Ansi
>>> string = Ansi( '\x1b[38;2;51;153;255mHello\x1b[38;2;102;255;102m World!\x1b[0m' )

# Actual length
>>> string.rlen()
53

# Normal reported length
>>> len(string)
12

# Iteration
>>> string_list = list(string)
>>> string_list
['\x1b[38;2;51;153;255m',
 'H',
 'e',
 'l',
 'l',
 'o',
 '\x1b[38;2;102;255;102m',
 ' ',
 'W',
 'o',
 'r',
 'l',
 'd',
 '!',
 '\x1b[0m']
>>> len(string_list)
15

# indexing and slicing
>>> string[-1]
'!\x1b[0m'

>>> string[:5]
'\x1b[38;2;51;153;255mHello'

>>> string[:6]
'\x1b[38;2;51;153;255mHello\x1b[38;2;102;255;102m '
    # notice the space after the escape code

>>> string[:7]
'\x1b[38;2;51;153;255mHello\x1b[38;2;102;255;102m W'

Because of the characteristics of Ansi strings, splitting is also a bit different. Ansi.split() with no arguments will split the string using the value in Ansi.strsep and return an AnsiList containing the same strsep value. This works vice-versa as well when using the added list method join in AnsiList.

>>> from texttools import Ansi

>>> string = Ansi( '\x1b[38;2;51;153;255mHello\x1b[38;2;102;255;102m World!\x1b[0m' )
>>> string.split()
[ '\x1b[38;2;51;153;255mHello\x1b[38;2;102;255;102m',
  'World!\x1b[0m' ]

# let's try setting the 'strsep' kwarg to 'l'
>>> string = Ansi( '\x1b[38;2;51;153;255mHello\x1b[38;2;102;255;102m World!\x1b[0m', strsep = 'l' )
>>> split_string = string.split()
>>> split_string
[ '\x1b[38;2;51;153;255mHe',
  '',
  'o\x1b[38;2;102;255;102m Wor',
  'd!\x1b[0m' ]

>>> type(split_string)
texttools.ansitools.AnsiList

return to contents

AnsiList

texttools.ansitools.AnsiList
  • subclass of list
  • Accepts 0 or more string arguments to form a list of Ansi strings
  • Shares the same keyword arguments as Ansi
  • Returns Ansi objects with join and __str__

Added methods:

    def iterclean(self):
        """
        A generator of this list with all ansi escapes removed
        """

    def join(self, s = None, **kwargs):
        """
        Join items in the list with the character in self.strsep
            - returns an Ansi object
        """

    def strip(self, item = None, *, strip_none = False ):
        """
        Removes any list items matching 'item'
            - returns a new AnsiList object
        """

return to contents

TextTools Class

texttools.TextTools

Most of the attributes/functions can be imported directly from the texttools package

class TextTools:
    """
    Tools for getting and printing colorized/styled text
    """

      # All functions in this class are classmethods

TextTools Attributes

  • FG - foreground RGB ansi escape types
  • BG - background RGB ansi escape types
  • Styles - text styles (bold, italic, underline, strikethrough, blink)

TextTools Classmethods

ansi2rgb
texttools.TextTools.ansi2rgb
@classmethod
def ansi2rgb(cls, ansi, *, fg_color = True, bg_color = True):
    """
    ansi2rgb( ansi, *, fg_color = True, bg_color = True ) >>> tuple()

        Returns a 5 or 10 part tuple from a string containing ansi escapes
          *args
            'ansi': string containing ansi escape patterns

          **kwargs
            'fg_color': include foreground tuple in results
                         - default = True
            'bg_color': include background color in results
                         - default = True

            Foreground color is always at the beginning of the tuple if both
          'fg_color' and 'bg_color' are True and both values are found
    """

return to contents

blockTxt
texttools.TextTools.blockTxt
    @classmethod
    def blockTxt( cls, txt, *,
                  fill_indent = False,
                  fill_width = False,
                  indent = 0,
                  indent_first = 0,
                  keep_newlines = False,
                  line_break = ' ',
                  push = 0,
                  push_first = False,
                  split_escapes = True,
                  start_column = 0,
                  width = 0,
                  width_includes_indent = True ):
        """
        Indents text for neater printing
          *args:
            'indent'   : amount of indenting to use after 1st line
                          - 'txt' is split by newlines and 'line_break' value
                          - if 'keep_newlines' == True, only 'line_break' value is
                            used to split 'txt'
                          - default = 0
            'push'     : push all indent values (does not include width)
                         to the right [n] amount of columns
                          - default = 0
            'txt'      : full text string
            'width'    : total width of text block
                          - if negative, subtract from the width found
                            using os.get_terminal_size
                          - does not include indent if width_includes_indent == False

          **kwargs:
            'indent_first'         : Amount of columns to indent to first line
                                      - 'indent' to matches the indent value
                                      - a negative value subtracts from indent value
                                        ( minumum value will be 0 )
                                      - default = 0
            'width_includes_indent': If True (default), includes indent in total width,
                                     otherwise total width = width + indent
            'start_column'         : Column that first line starts on
                                      - use 'indent' to match the indent value
                                      - default = 0
            'split_escapes'        : Resets text at end of lines and uses escapes from
                                     the previous line to reset it.
                                      - default = True
            'keep_newlines'        : Doesn't remove newlines from provided text
                                      - might have double newlines in result
            'line_break'           : Provide a string pattern to try to break lines on
                                      - default = ' '
            'push_first'           : Include the first line when using 'push' argument
                                      - default = False
           ** use fill options to make sure background colors, if used, will cover before
                and/or after the text characters

            'fill_width'           : Fill the remaining width in each line with a space
                                      - default = False
            'fill_indent'          : Insert escape code before the indent spaces when using
                                     'split_escapes'
                                      - default = False
        """

return to contents

hex2rgb
texttools.TextTools.hex2rgb
    @classmethod
    def hex2rgb(cls, HEX):
        """
        Returns an RGB tuple from a hexidecimal code
            'HEX': hexidecimal value representing a color
        """

return to contents

rgb2ansi
texttools.TextTools.rgb2ansi
    @classmethod
    def rgb2ansi(cls, R = 0, G = 0, B = 0, *, bg = False):
        """
        Returns an ansi escape string from RGB values
            'R': integer for Red    [0-255]
            'G': integer for Green  [0-255]
            'B': integer for Blue   [0-255]

          * default values are set to Black - (0,0,0)
        """

return to contents

rgb2hex
texttools.TextTools.rgb2hex
    @classmethod
    def rgb2hex(cls, RGB):
        """
        Returns a hexidecimal code from an rgb code
            'RGB': tuple|list of 3 integers
        """

return to contents

rgbString
texttools.TextTools.rgbString
    @classmethod
    def rgbString(cls, value):
        """
        Create an rgb string
            'value': hex, string of integers, list|tuple, or FG_Color|BG_Color
        """

return to contents

money_fmt
texttools.TextTools.money_fmt
    @classmethod
    def money_fmt(cls, n, *, color_type = 'term'):
        """
        Return formatted number to money string
            accepted color_type:
                'term', 'term_reverse', 'html', or 'html_reverse'
        """

return to contents

t2a
texttools.TextTools.t2a
    @classmethod
    def t2a(cls, t: tuple|list, *, end = 'm'):
        """
        Tuple to ansi escape
            - provide a tuple or list of ansi escape integers
            - returns an ansi escape string
        """

return to contents

from16
texttools.TextTools.from16
    @classmethod
    def from16(cls, color, *, return_data = False):
        """
        16 color tuple to rgb tuple
            - if len(tup) == 1, assumes 0 for first value
        """

return to contents

to16color
texttools.TextTools.to16color
    @classmethod
    def to16color(cls, rgb, *, bg = False, color_dict = False, diff_key = COLORS_RGB_DIFF_KEY, all_results = False ):
        """
        Rgb to 16 color tuple
            - rgb length can be 3 or 5
            - if rgb doesn't match color, tries to find the closest match
            - if len(rgb) != 5, a fg color is returned by default

            'bg'         : True to force or set to return a bg color
            'data'       : True to return a dict
            'diff_key'   : key to sort matches by
                            [ 'mean'||'median'||'max'||'min'||'min_max_avg' ]
            'all_results': return a list of all result data
                            - assumes 'data' == True
        """

return to contents

TextTools Help

texttools.TextTools.help
    @classmethod
    def help(cls, *, return_list = False):
        """
        TextTools help message
            - prints detailed list of available colors

          'return_list': return as a list instead of printing
                          - default = False
        """

return to contents

Ansi Escape Types

  • types of ansi escape objects - subclasses of str
    • FG_Color
    • BG_Color
    • Style
    • Reset
    • AnsiCombo

Color Modifiers

  • functions defined in escape types that have a color attributes
    • Dimmed
    • Brightened
    • Blended
    • Inverted

return to contents

Styles

Blink:
    Ansi style: Blink
      - Set text to blinking

    Attribute Name:   Bl|blink
    Ansi Code:        (5,)
Remove Blink:
    Ansi style: Remove Blink
      - Remove the blinking text escape

    Attribute Name:   Bl_|remove_blink
    Ansi Code:        (25,)
Bold:
    Ansi style: Bold
      - Set text to bold

    Attribute Name:   B|bold
    Ansi Code:        (1,)
Remove Bold:
    Ansi style: Remove Bold
      - Remove the bold text option

    Attribute Name:   B_|remove_bold
    Ansi Code:        (21,)
Italic:
    Ansi style: Italic
      - Set text to italic

    Attribute Name:   I|italic
    Ansi Code:        (3,)
Remove Italic:
    Ansi style: Remove Italic
      - Remove the italic text option

    Attribute Name:   I_|remove_italic
    Ansi Code:        (23,)
Strikethrough:
    Ansi style: Strikethrough
      - Set strikethrough decoration to text

    Attribute Name:   S|strikethrough
    Ansi Code:        (9,)
Remove Strikethrough:
    Ansi style: Remove Strikethrough
      - Remove the strike-through text option

    Attribute Name:   S_|remove_strikethrough
    Ansi Code:        (29,)
Underline:
    Ansi style: Underline
      - Set underline decoration to text

    Attribute Name:   U|underline
    Ansi Code:        (4,)
Remove Underline:
    Ansi style: Remove Underline
      - Remove the underline text option

    Attribute Name:   U_|remove_underline
    Ansi Code:        (24,)
Reset:
    Ansi style: Reset
      - Reset all ansi styles and colors

    Attribute Name:   _|reset
    Ansi Code:        (0,)

return to contents

Working With Color Types

View a list of all default colors from FG or BG escape group

>>> from texttools import FG
>>> FG.help()
"""
  Colors:

    Attributes can be 'stacked' using the BITAND '&' to create an AnsiCombo
      Example:
        FG.white & BG.charcoal & S.Bold

      You can only have a Reset object at the beginning of an AnsiCombo
        S._ & FG.red

      Only one of each color type or Style type is accepted when combining. If 2
    or more objects of the same type are detected, a ValueError will be raised. If a Reset
    object is added anywhere except the beginning of the combo, a TypeError is raised

      NOTE: all color groups share a Reset ('\x1b[0m') object

  Colors in class:
                    ...
"""
# Import colors

>>> from texttools import ( FG as c,
>>>                         BG as bg,
>>>                         Styles as S )

# Get colors by full name or 'code' name

>>> color = c.charcoal
>>> color
AnsiColor(< Charcoal (Fg) >)

>>> color = c.ch
>>> color
AnsiColor(< Charcoal (Fg) >)

>>> str(color)
'\x1b[38;2;26;26;26m'

# AnsiColor attributes

>>> color.rgb       # RGB tuple
(26, 26, 26)
>>> ch.name         # object name
'Charcoal (Fg)'
>>> ch.hex          # hexidecimal value
'#1a1a1a'

return to contents

Color Functions

Use functions in color objects to modify RGB values

  • AnsiCombos that have color attributes will also have these functions
  • If an AnsiCombo has both foreground and background colors, both will be affected

Brightened

texttools.types.Brightened

Lighten AnsiColor with 'bright' function

  • Optionally add a percentage of brightness (default is 15%)
>>> gray = c.dark_gray
>>> gray.rgb
(56, 56, 56)

>>> gray_brightened = color.bright(20)
>>> gray_brightened
'Brightened(< Dark Gray (Fg) - %20 >)'

>>> gray_brightened.rgb
(96, 96, 96)

# You can also use the '+' operator to add the default (15%) bright value
>>> +gray
'Brightened(< Dark Gray (Fg) - %15 >)'

# Doing this more than once will exponentially brighten the object
>>> (+gray).rgb
(86, 86, 86)

>>> (+++gray).rgb
(133, 133, 133)

# NOTE when using operators like this in AnsiCombo creation, the expressions
#   need to be wrapped in parentheses.

return to contents

Dimmed

texttools.types.Dimmed

Darken AnsiColor with 'dim' function

  • Optionally add a percentage of dimness (default is 15%)
>>> blue = c.light_blue
>>> blue.rgb
(51, 153, 255)

>>> blue_dimmed = blue.dim(20)
>>> blue_dimmed
'Dimmed(< Light Blue (Fg) - %20 >)'

>>> blue_dimmed.rgb
(41, 122, 204)

# You can also use the '-' operator to subtract the default (15%) dim value
>>> (-blue).rgb
(43, 130, 217)

# Doing this more than once will exponentially dim the object
>>> (---blue).rgb
(31, 94, 156)

return to contents

Inverted

texttools.types.Inverted

Invert AnsiColor with the 'invert' function

  • this function takes no arguments
  • This still needs work and will change in the future, but for now...
>>> blue = c.light_blue
>>> blue.rgb
(51, 153, 255)

>>> blue_inverted = blue.invert()
>>> blue_inverted
'Inverted(< Light Blue (Fg) >)'

>>> blue_inverted.rgb
(204, 102, 0)

# You can also use the '~' operator to invert
>>> (~blue).rgb
(204, 102, 0)

# Doing this more than once or using invert() on the inverted object
# will just revert back to the original AnsiColor

>>> blue_inverted.invert()
AnsiColor(< Light Blue (Fg) >)

return to contents

Blended

texttools.types.Blended

Blend 2 or more AnsiColors or AnsiCombos with the 'blend' function

  • probably the coolest function :)
  • first argument is the color to blend
  • optional 2nd argument to set the blend percentage
  • default blend value = 15%
    • usually requires a higher percentage to notice a difference I've written a linear gradient string function that will be incorporated in TextTools in a future release
>>> white = c.white
>>> white.rgb
(255, 255, 255)

>>> orange = c.orange
>>> orange.rgb
(255, 128, 0)

>>> white_orange_blend = white.blend( orange, 40 )
>>> white_orange_blend
'Blended(< White||Orange- %40 >)'

>>> white_orange_blend.rgb
(255, 204, 153)

# AnsiColors can also be blended using the BITOR '|' operator
#  - this uses the default 15% blend value

>>> white_orange = white|orange
>>> white_orange
'Blended(< White||Orange- %15 >)'

return to contents

AnsiCombo

texttools.types.AnsiCombo

AnsiColors can be combined using the BITAND operator &. Only one of each color type ( FG_Color, BG_Color ) can be combined. Every Style object can also be added as long as the 'remove' version isn't combined with the non 'removed' version. A Reset object can be used in the combination but must be the first to the list or else a TypeError is raised. Multiple objects that conflict will raise a ValueError.

Example:

>>> from texttools import FG, BG, Styles as S

>>> combo = FG.yellow & BG.blue
>>> combo
"AnsiCombo(< Yellow (Fg) + Bg Blue (Bg) >)"

    # here, FG._ is actually a Reset object, not an FG_Color
>>> combo = FG._ & FG.gold & BG.dark_gray & S.italic
>>> combo
"AnsiCombo(< Reset + Gold (Fg) + Bg Silver (Bg) + Italic >)"

    # trying to add a Reset in the middle of Combo
>>> combo = FG.gold & FG._ & BG.dark_gray & S.italic
"TypeError: Can't add 'Reset' type to another attribute"

    # adding 2 conflicting objects
>>> combo = FG.gold & S.remove_italic & BG.dark_gray & S.italic
"ValueError: 'AnsiCombo' already contains 'italic' attributes"

return to contents

Cursor Controls

texttools.cursorcontrols.CursorControls
>>> from texttools import Cursor

>>> Cursor.Clear.__doc__
        """
        Clear screen or line
          - alias = 'Cursor.clr'
          - clear entire screen (2J)

            Clear.line
              - clear current line (2K)
                L: clear line left of cursor (1K)
                R: clear line right of cursor (0K)

            Clear.screen
              - clear entire screen (2J)
                up: clear screen above cursor (1J)
                dn: clear screen below cursor (0J)
        """

>>> Cursor.L.__doc__
        """
        Cursor Left
          'n': Number of columns
        """

>>> Cursor.R.__doc__
        """
        Cursor Right
          'n': Number of columns
        """

>>> Cursor.col.__doc__
        """
        Cursor To Column
          'n': column number
        """

>>> Cursor.dn.__doc__
        """
        Cursor Down
          'n': Number of lines
        """

>>> Cursor.getpos.__doc__
        """
        Get cursor position
            - returns tuple( [line], [column] )
        """

>>> Cursor.help.__doc__
        """ Help message """

>>> Cursor.hide.__doc__
        """ Cursor Invisible """

>>> Cursor.pos.__doc__
        """
        Set cursor position
            - PositionCursor( [line], [column] )
        """

>>> Cursor.show.__doc__
        """ Cursor Visible """

>>> Cursor.up.__doc__
        """
        Cursor Up
          'n': Number of lines
        """

return to contents

Changelog

  • 0.3.5

    • modified BBLogger and getLogger to hopefully maintain the right level across modules
    • added 'rootlogger' level option
  • 0.3.8

    • changed formatter to include filename instead of logger name
  • 0.3.9

    • fixed logging error when not in terminal
  • 0.3.91

    • fixed typo in basic logging.... maybe
  • 0.3.92

    • 2nd attempt at fixing basic logging
  • 0.3.95

    • yet another attempt to get basic log formatting right
  • 0.4.0

    • re-wrote the basic log formatter
  • 0.5.0

    • completely rebuilt bblogger, texttools, and apputils
    • full overhaul of AnsiColors
      • added types:
        • FG_Color
        • BG_Color
        • Style
        • AnsiCombo
        • Brightened
        • Dimmed
        • Inverted
        • Blended
    • added functions to apputils
    • removed bbargparser (working on new version)
    • added user colors and template
    • added a lot to documentation
  • 0.5.1

    • fixed a typo in texttools.types
  • 0.5.2

    • fixed (hopefully) AnsiCombo
      • AnsiCombo was broken when combining certain types
  • 0.5.3

    • fixed Colors.new() (texttools.newColor)
    • added functionality to Colors.get (texttools.getColor)
      • can now create color from get function
    • fixed bblogger
      • didn't set handler level with setLevel
      • sets the logger class last to avoid recursive errors
    • added TypeSet - class that acts like sets for type objects
    • changes in apputils
      • sets PROJECT_APP_NAME to project name if pyproject.toml is found
      • fix for dividing by 0 in tupleCalc
    • changes in texttools
      • changes to how __repr__ represents object names
      • separated type functions to separate file to clean code
        • AnsiCombo now derives methods from ComboFuncs
        • AnsiEscape now derives methods from EscapeFuncs
      • added __dir__ method to AnsiEscapeMeta
      • added base attribute to Dimmed and Brightened types
      • added bases attribute to Blended type
      • added methods to AnsiCombo
        • bg, fg, hasAttr, hasBG, hasBlink, hasBold, hasFG, hasItalic, hasReset, hasStrikethrough, hasUnderline
      • added hsv method and hsv_to_rgb class method to AnsiColor objects
    • changes to apputils-shell-variables script
      • tag showing if variable is found in os.environ
      • -h|--help message added
    • some fixes to bblogger

Thanks To:

Thread at Stack Overflow on python log formatting

And everyone else on forums across the web for being the sole source of my development learnings :)

return to contents

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

bb_apputils-0.5.3.tar.gz (304.9 kB view hashes)

Uploaded Source

Built Distribution

bb_apputils-0.5.3-py3-none-any.whl (293.7 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page