Package: thelittlethings
thelittlethingsa library full of small utilities for you to use in your codecontentsgeneral⭳installationmodules, functions and classes⟳auto_reload➜ usage⛭ technical details🗎debug.Log➜ usage⛭ technical details⧖debug.Timer➜ usage⛭ technical details≡extended_list➜ usage⛭ technical details🗁files.load_file➜ usage⛭ technical details✎mutable.Mutable➜ usage⛭ technical details∞linked_values➜ usage⛭ technical details═progress_bar➜ usage⛭ technical details!assertion➜ usage⛭ technical details✓testing.test➜ usage⛭ technical detailsⒶto_string➜ usage⛭ technical detailsπconstants➜ usage⛭ technical detailsᗑvariables.get_all_subclasses➜ usage⛭ technical detailsᎥᎥᎥvariables.get_instances➜ usage⛭ technical details佟variables.get_names➜ usage⛭ technical details『』variables.get_types➜ usage⛭ technical details∩variables.get_first_shared_inheritance➜ usage⛭ technical detailsinstallationmake sure that you havepython3andpipinstalled and run the following command:pip install thelittlethingsif that doesn't work, try the following command:pip3 install thelittlethingsto update, run the following command:pip install --upgrade thelittlethingsto uninstall, run the following command:pip uninstall thelittlethingsauto_reload➜ usageinrun.py:fromthelittlethingsimportauto_reloadauto_reload.run("file_to_auto_reload.py",refresh_interval=0.1)infile_to_auto_reload.py:whileTrue:print("hello world")time.sleep(1)now, when you runrun.py,"hello world"will be printed every second. if you modifyfile_to_auto_reload.pyand save it, you will see the changes reflected in the output.⛭ technical detailsthe script checks for changes in the file in set intervals. you can change the interval by modifying therefresh_intervalvariable. The refresh interval is in seconds and defaults to0.1.if an error occurs, auto reload will not terminate.if your script (file_to_auto_reload.py) stops without errors, auto reload will terminate.taking inputs currently doesn't work as multiprocessing throws anend of fileerror.debug.Log➜ usageimport:fromthelittlethingsimportLogfromthelittlethings.debugimportLogLogis used to both print to the console and log to a file:Log("hello world")By default it will only print to the console. To load a file, do:Log.load_file("log.txt")from now on, allLogstatements will be printed to the console and also to the file.only one file can be loaded at a time.to close the file, do:Log.close_file()to use a different file for one statement, do:Log("hello world",file_path="other_log.txt")to disable printing to the console, do:Log.print=Falseor, to disable it for only one statement, do:Log("hello world",print=False)Loguses custom formatting for color and style. To change the text color, do:Log("[Text: Red]hello world")to change the background color, do:Log("[Background: Red]hello world")to change the text style, do:Log("[Style: Bright]hello world")color, background and style can be combined:Log("[Text: Red, Background: Green, Style: Bright]hello world")to access the color code parser, do:fromthelittlethingsimporttranslate_color_codestranslate_color_codes("your_text_here",console=True)where the console argument says whether to apply the codes or ignore them.by default,Logseperates the values with a space and appends a newline. to change this, do:Log.sep="my_seperator"Log.end="my_end_of_line"to change it for only one statement, do:Log("hello world",sep="my_seperator",end="my_end_of_line")⛭ technical detailsLogusessys.stdout.writeinstead of print and will flush the buffer after each statement.when the script exits, the file is closed automatically and all styles are reset.To make the parser ignore an opening square bracket, follow it with a backslash.debug.Timer➜ usageimport:fromthelittlethingsimportTimerfromthelittlethings.debugimportTimerTimeris used to time the execution of a block of code:Timer.start("timer 1")# do somethingTimer.stop("timer 1")Timer("timer 2")# do somethingTimer.stop("timer 2")timer_3=Timer()# do somethingtimer_3.stop()timer_4=Timer()# do somethingTimer.stop("timer_4")when a timer is stopped, the elapsed time is logged:to manually log the elapsed time, do:Timer.log("timer 1")timer_3.log()to get the elapsed time, do:Timer.get("timer 1")timer_3.get()⛭ technical detailsa timer is not automatically restarted after it is stopped.TimerusesLogfor logging. color codes in timer names will therefore be applied.extended_list➜ usageimport:fromthelittlethingsimportEListfromthelittlethings.extended_listimportEListEListadds some useful funcionality to thelistclass.it allows you to merge two lists, ignoring duplicates:list_1=EList([1,2,3,4])list_2=EList([3,4,5,6])list1.merge(list_2)list_1is now[1, 2, 3, 4, 5, 6]. the same effect can be achieved by using the bitwise or operator|.The operators&and^are also implemented and behave like they do on sets.you can remove all instances of one or multiple values from a list:my_list=EList([1,2,2,3,4,4,5,6])my_list.clear_from(2,4)my_listis now[1, 3, 5, 6].Using unpack operations this can be used to remove all values in an iterable from the list:my_list=EList([1,2,2,3,4,4,5,6])my_list.clear_from(*[2,4])this can also be achieved using the-operator:my_list=EList([1,2,2,3,4,4,5,6])my_list-=[2,4]you can clear anEListof all duplicates usingclear_duplicates:my_list=EList([1,2,2,3,4,4,5,6])my_list.clear_duplicates()my_listis now[1, 2, 3, 4, 5, 6].EListalso implements methods that allow you to get a new list with some operation applied. these methods are:withoutforclear_frommergedformergewithout_duplicatesforclear_duplicatesextendedforextendmany methods that mutate the list are modified to return self to allow method chaining.iteration over anEListis slightly different than with a normallist. theEListensures that the iterator iterates over every single item in the list, even if some items are removed in the process.e_list=EList([1,2,3,4,5,6])builtin_list=list(e_list)foritemine_list:ifitem%3!=0:e_list.remove(item)foriteminbuiltin_list:ifitem%3!=0:builtin_list.remove(item)e_listis now[3, 6]whilebuiltin_listis[2, 3, 5, 6].as the builtinenumeratefunction will give incorrect indices,EListprovides anenumeratefunction that will return a proper iterator.another feature is joining the elements of anEListinto a string with a separator.e_list=EList([1,"hello",[2,3]])joined_string=e_list.join("-")joined_stringis"1-hello-[2, 3]".the seperator defaults to a space if no other separator is specified.⛭ technical detailsEListinherits fromlist.the way iteration is handled means that theEListclass needs to keep track of all iterators that are currently active and update their indices whenever a modification is made. this can be costly when doing large operations.files.load_file➜ usageimport:fromthelittlethingsimportload_filefromthelittlethings.filesimportload_filetheload_filefunction will create a file if it does not exist or load the file if it does.my_file=load_file("my_file.txt")whether the file existed before or not, it now does and is loaded in themy_filevariable.if the input is not a string or the file could not be loaded, the function will throw an error unless given adefaultvalue.⛭ technical detailsthe file is loaded in thewritemode.mutable.Mutable➜ usageimport:fromthelittlethingsimportMutablefromthelittlethings.mutableimportMutableMutableis a class to make any object mutable. it redirects most magic methods to the value and implements the__setattr__and__getattr__methods.my_mutable=Mutable(1)other_variable=my_mutablemy_mutable+=1other_variableis now2.⛭ technical detailsthe__i.*__magic methods (__iadd__,__isub__etc.) use the value's__.*__(__add__,__sub__etc.) methods if it does not implement the__i.*__method itself. this way they can work on immutable objects as well.linked_values➜ usageimport:fromthelittlethingsimportlinked_valueslinked_valuesis a class that allows you to link values together using operators:fromthelittlethingsimportValuea=Value(1)b=Value(2)c=a+ba,bandcare now1,2and3respectively. continuing the example:a+=1a,bandcare now2,2and4respectively.b-=2a,bandcare now2,0and2respectively.c+=3a,bandcare now5,0and5respectively.basically, wheneveraorbis changed,cwill be updated. ifcis changed,awill be updated so thatcwill be equal toa + b. to havebbe updated instead ofa, switch theaandbvalues in the expression. for positional operators (-,/,**etc.),linked_valuesprovides backwards links that will update the second value in the expression. all operators are subclasses ofLink.the most basicLinks areVar,AttrandFunc.Varsimply holds an object,Attrrefers to an object's attribute andFuncholds getter and setter functions that will be called when the value is accessed or modified.VarandAttrcan be marked as immutable and can then only be modified by passing a function to thesetmethod.available general operators are:Eq(a, b)⟺a == bGt(a, b)⟺a > bGe(a, b)⟺a >= bLt(a, b)⟺a < bLe(a, b)⟺a <= bavailable number operators are:Add(a, b)⟺a + bSub(a, b⟺a - bRSub(a, b)⟺b - aMul(a, b)⟺a * bDiv(a, b)⟺a / bRDiv(a, b)⟺b / aPow(a, b)⟺a ** bRPow(a, b)⟺b ** aRoot(a, b)⟺a ** (1 / b)RRoot(a, b)⟺b ** (1 / a)Mod(a, b)⟺a % bAbs(a)⟺abs(a)Ln(a)LogB(a, b)RLogB(a, b)available boolean operators are:And(a, b)⟺a & bOr(a, b)⟺a | bXor(a, b)⟺a ^ bNot(a)⟺~athe equivalence is only true ifais already aLinkand (for reverse operators) ifbis not.some additional info onAttr:you can input strings in the format of"a.b.c"to access attributes of attributes of attributes etc. forLinks,Attrwill get thevalue's attribute.⛭ technical detailscustom operations can be added by inheriting fromlinked_values.Operatorand implementing the class methods_evaland optionally_eval_reverse. thehas_reversemethod should work without any changes but if it does not, override it.Operatorhas the subclassesNumberOperatorandBooleanOperatorthat the corresponding operators inherit from.backwards operators are only implemented for operators for which the position of the arguments is relevant and for which the_eval_reversemethod is implemented.XorOperatorandNotOperatorare the onlyBooleanOperators that support_eval_reversefor all values. all other BooleanOperators have cases in which one of the input values is irrelevant. in these cases the input is not modified.inplace operators do not createLinkobjects but modify the value directly.progress_bar➜ usageimport:fromthelittlethingsimportProgressBarProgressBaris a class that allows you to easily create a progress bar.importtimeprogress_bar=ProgressBar()foriinrange(100):progress_bar.progress=itime.sleep(0.1)progress_bar.finish()the progress bar will be shown in the console. it will automatically provide a heuristic for the time it will take to finish the operation.optional arguments:max_value: the maximum value of the progress bar. defaults to100.width: the width of the progress bar. defaults to20.log_interval: the interval in seconds between each log. defaults to0.1.display_percentage: whether to display the percentage of the progress. defaults toTrue.display_time_passed: whether to display the time that has passed since the progress bar was created. defaults toTrue.display_time_remaining: whether to display an estimate for time that is remaining until the progress bar is finished. defaults toTrue.draw_function: the function that draws the progress bar. defaults toProgressBar.draw_bar.⛭ technical detailstheProgressBarsupportswithstatements.theProgressBarusesmultiprocessingfor printing and estimating the time remaining.the time remaining heuristic currently assumes a linear progression.assertion➜ usagetheassertionmodule helps in testing and checking if arguments are of the right type. it provides the following functions:assert_closeassert_equalassert_falseassert_greaterassert_greater_equalassert_isassert_lessassert_less_equalassert_not_equalassert_trueassert_typeassert_typesassert_typeandassert_typesare intended to be used in type checking for functions:fromthelittlethingsimportassert_typesdefmy_func(string:str,integer:int,m_list:list)assert_types((string,integer,m_list),(str,int,list))with allassertfunctions you can pass theerror_message_appendixkeyword argument. the given string will be shown if an error is thrown.⛭ technical detailsallassertfunctions will throw anAssertionErrorif they fail and otherwise returnTrue.testing➜ usageimport:fromthelittlethingsimporttestingthetestingmodule helps you to test your code.fromthelittlethingsimportassert_equalfromrandomimportrandomdeffunction_to_test(a,b):returna+bdeftest_addition(function_to_test):a=random()*100-50b=random()*100-50assert_equal(function_to_test(a,b),a+b)returnf"Passed on{a}+{b}"testing.test([function_to_test],test_addition,iterations=1)the first argument is the list of functions / classes / etc. to test, all other positional arguments will be used to test them.testwill print the results of each individual test as well as a summary of the results per object to test.if you have multiple seperate tests to run you can use themulti_testfunction:fromthelittlethingsimportassert_equalfromrandomimportrandomdeffunction_to_test(a,b):returna+bdeftest_addition(function_to_test):a=random()*100-50b=random()*100-50assert_equal(function_to_test(a,b),a+b)returnf"Passed on{a}+{b}"defother_function_to_test(a,b):returna-bdeftest_subtraction(function_to_test):a=random()*100-50b=random()*100-50assert_equal(function_to_test(a,b),a+b)returnf"Passed on{a}+{b}"testing.multi_test(([function_to_test],test_addition,{'iterations':1}),([other_function_to_test],test_subtraction))another way to run tests is to use thetest_from_classfunction:fromthelittlethingsimportassert_equalclassTestFunctions:deftest_something():assert_equal(1,1)testing.test_from_class(TestFunctions,iterations=1)this will test all the functions in the class that start withtest_.⛭ technical detailstestusesLogfor outputting the results. therefore, color codes can be used in the testing functions' feedback.iterationsdefaults to1.testandtest_from_classreturnTestSummaryobjects.multi_testreturns a list ofTestSummaryobjects.to_string➜ usageimport:fromthelittlethingsimportto_stringcurrently,to_stringonly has a function to convert a list to a string in a more human like way.fromthelittlethingsimportto_stringlist_string=to_string.list([1,2,3],final_sep="and",empty_str="nothing")list_stringis now"1, 2 and 3". final_sep and empty_str are optional and default to the values shown in the example.⛭ technical detailsto_stringconverts every element of the list to a string. this way, any type of object will work as long as it has astrrepresentation.constants➜ usageimport:fromthelittlethingsimportconstantsconstantsis a module that contains some useful constants, some of which are used in the other modules.currently,constantscontains anUNDEFINEDconstant , thecolorsubmodule for color constants and themathsubmodule for math constants.⛭ technical detailsUNDEFINEDis part of a custom singletonNoneTypeclass that will always returnUNDEFINED. it supportsstrandboolevaluation.
``
all colors in thecolorsubmodule are tuples of integers from 0 to 255.themathsubmodule uses constants from the builtinmathmodule.variables.get_all_subclasses➜ usageimport:fromthelittlethingsimportget_all_subclassesget_all_subclassesis a function that returns all subclasses of a given class including subclasses of subclasses.fromthelittlethingsimportget_all_subclasses,Operatoroperator_subclasses=get_all_subclasses(Operator)operation_subclasses is now a list of all subclasses of Operation includingNumberOperatorandBooleanOperator.⛭ technical detailsget_all_subclassesworks by recursively searching through the inheritance tree of the given class. it supports multiple inheritance.variables.get_instances➜ usageimport:fromthelittlethingsimportget_instancesget_instancesis a function that returns all instances of a given class.fromthelittlethingsimportget_instancesclassA:def__init__(self,value):self.value=valuedef__repr__(self):returnf"A({self.value})"a1=A(1)a2=A(2)a3=A(3)all_instances=get_instances(A)all_instancesis now[A(1), A(2), A(3)].⛭ technical detailsget_instancesususgcto search all objects in the memory. therefore, it is quite slow and it is recommended to handle instances in your class itself like this:classA:instances=[]def__init__(self):type(self).instances.append(self)this will be much faster and is therefore recommended in situations where performance is an issue.variables.get_names➜ usageimport:fromthelittlethingsimportget_namesget_namesis a function that returns all names of a given object.a=list()b=ac=anames_of_a=get_names(a)names_of_ais now["a", "b", "c"].if you want to get the object's names in a different frame, use theframekeyword argument and pass aninspect.frameobject.⛭ technical detailsget_namesusesinspectand will only scan the frame'slocalsandglobalsfor names. if a name has been assigned in a different frame, it will not be returned.variables.get_types➜ usageimport:fromthelittlethingsimportget_typesget_typesis a function that returns all types of a given objects.a=list()b=set()types=get_types(a,b)typesis now(list, set).⛭ technical detailsget_typesreturns a tuple of the type returned by thetypefunction for each object.variables.get_first_shared_inheritance➜ usageimport:fromthelittlethingsimportget_first_shared_inheritanceget_first_shared_inheritanceis a function that returns the first class that all given objects inherit from.fromthelittlethingsimportget_first_shared_inheritanceclassA:passclassB(A):passclassC(A):passfirst_shared_inheritance=get_first_shared_inheritance(B(),C())first_shared_inheritanceis nowA.⛭ technical detailsget_first_shared_inheritancerecursively scans all objects from the inheritance tree. it supports multiple inheritance.