Python development tools
Project description
BB_AppUtils
Utilities for developing python applications/scripts
README is incomplete!
Contents
-
Shell Script 'apputils' - terminal commands
-
- package of handy functions and tools
- AppData - tools for using system directories
- Classes
- Functions
- attributes
- FileLock - Threading type lock for writing/reading files
- GlobalLock - Threading lock for global use
-
- custom Logger and Formatter class for the built-in python logging module
- Shell Options - set logging options via shell environment
- Get Logger - initial call to bblogger
- BBLogger Class
- BBFormatter Class
- Logger Example
-
- collection of tools for modifying console text
- Ansi - work with escaped strings
- AnsiList
- TextTools Class - collection of classmethods
- Escape Types
- Styles
- Using Ansi Colors
- Color Functions
- Brightened
- Dimmed
- Inverted - needs work
- Blended
- AnsiCombo
- Cursor Controls
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
"""
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
"""
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
"""
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 )
Tools
apputils.tools
MISSING DOCUMENTATION
appendFile
apputils.tools.appendFile
def appendFile( path, data, *, mode = 'a', encoding = 'utf-8', **kwargs ):
attribNameFromString
apputils.tools.attribNameFromString
def attribNameFromString( string ):
filterList
apputils.tools.filterList
def filterList( _list, match, index = None, *,
not_in = False, _type = None):
filter_dir
apputils.tools.filter_dir
def filter_dir(attr, **kwargs):
getBoolFromString
apputils.tools.getBoolFromString
def getBoolFromString(s):
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 )
"""
isBinary
apputils.tools.isBinary
def isBinary(filename):
isSafeAttribName
apputils.tools.isSafeAttribName
def isSafeAttribName(name):
listDepth
apputils.tools.listDepth
def listDepth( L: list, *, tuples = False ):
listFlatten
apputils.tools.listFlatten
def listFlatten( *L, depth: int = 1, tuples = False ):
matchSimilar
apputils.tools.matchSimilar
def matchSimilar( find, _list, *,
ratio = STRING_SEQUENCE_MATCH_RATIO,
getall = False, getalldata = False ):
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
"""
moveListItem
apputils.tools.moveListItem
def moveListItem( L: list, from_index, to_index, *, items = False ):
parseDate
apputils.tools.parseDate
def parseDate(s, fmt = ''):
readFile
apputils.tools.readFile
def readFile( path, *, mode = None, encoding = 'utf-8', **kwargs ):
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.
"""
stringFilter
apputils.tools.stringFilter
def stringFilter( obj: Any, *,
func = None, start = [], end = [],
re_search: re.compile = None, re_match: re.compile = None ):
timeDeltaString
apputils.tools.timeDeltaString
def timeDeltaString(t):
"""
Convert time from timedelta object to xx:xx:xx
"""
tupleCalc
apputils.tools.tupleCalc
def tupleCalc( a, b, op, *, diff = False, round_int = False ):
uniqueName
apputils.tools.uniqueName
def uniqueName(path, *, move_existing = False):
writeFile
apputils.tools.writeFile
def writeFile( path, data, *, mode = 'w', encoding = 'utf-8', **kwargs ):
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
"""
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
"""
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
"""
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__)
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'
, yetstring[-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 attributesclean
andstring
are provided in Ansi() as nativestr
objects for more complicated uses. Another attributeescapes
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 methodjoin
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
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
"""
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
"""
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
"""
hex2rgb
texttools.TextTools.hex2rgb
@classmethod
def hex2rgb(cls, HEX):
"""
Returns an RGB tuple from a hexidecimal code
'HEX': hexidecimal value representing a color
"""
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)
"""
rgb2hex
texttools.TextTools.rgb2hex
@classmethod
def rgb2hex(cls, RGB):
"""
Returns a hexidecimal code from an rgb code
'RGB': tuple|list of 3 integers
"""
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
"""
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'
"""
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
"""
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
"""
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
"""
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
"""
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
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,)
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'
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.
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)
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) >)
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 >)'
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. EveryStyle
object can also be added as long as the 'remove' version isn't combined with the non 'removed' version. AReset
object can be used in the combination but must be the first to the list or else aTypeError
is raised. Multiple objects that conflict will raise aValueError
.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"
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
"""
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 types:
- 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
- fixed (hopefully) AnsiCombo
-
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 ifpyproject.toml
is found - fix for dividing by 0 in
tupleCalc
- sets
- 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
- AnsiCombo now derives methods from
- added
__dir__
method toAnsiEscapeMeta
- added
base
attribute toDimmed
andBrightened
types - added
bases
attribute toBlended
type - added methods to
AnsiCombo
bg
,fg
,hasAttr
,hasBG
,hasBlink
,hasBold
,hasFG
,hasItalic
,hasReset
,hasStrikethrough
,hasUnderline
- added
hsv
method andhsv_to_rgb
class method to AnsiColor objects
- changes to how
- changes to
apputils-shell-variables
script- tag showing if variable is found in
os.environ
-h
|--help
message added
- tag showing if variable is found in
- some fixes to bblogger
- fixed
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 :)
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
File details
Details for the file bb_apputils-0.5.3.tar.gz
.
File metadata
- Download URL: bb_apputils-0.5.3.tar.gz
- Upload date:
- Size: 304.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/1.7.1 CPython/3.11.7 Linux/6.7.4-zen1-1-zen
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 808e4163623d411e467328e8a2c2f314d9166f9e10a84b478f5161ae4502d4be |
|
MD5 | 02e3f3c9b7543ba9863fd7932db4dbf7 |
|
BLAKE2b-256 | 207c718ee530a74ab8dcc453b1f8081a6fadf57c104ac8914fd3ccc03ed397b4 |
File details
Details for the file bb_apputils-0.5.3-py3-none-any.whl
.
File metadata
- Download URL: bb_apputils-0.5.3-py3-none-any.whl
- Upload date:
- Size: 293.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/1.7.1 CPython/3.11.7 Linux/6.7.4-zen1-1-zen
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | f320d621031b834ca26412d757f1df7bfb2f343f0622c20da6e00bda09047de5 |
|
MD5 | cdcdc088ab1a6821c6f03c56e7e52912 |
|
BLAKE2b-256 | 23487c46d4bcb11df7e81292a3652a775b4fcdb5f9d9c62cdcdb0ecff75b215a |