Package: tryst
TrystCLI support package.tryst on GithubGNU GPLv3One's body is inviolable, subject to one's own will alone.What Tryst is:A lightweight interface and context package for basic cli features intended for rapid atomic problem-solving. Built to support code-time chaining between apps for efficient code-reuse and 'build-outward' problem-solving methodologies.Tryst is lightweight but means to compy with SOLID design principles and is a learning experience in action to build a better understanding of Python, CLI development, unit testing, deployment, support, and more.What Tryst is not:Tryst is not fancy or comprehensive. It is not intended to be flawless, nor to replace more fully-functional and well-established CLI support packages such asargparseorgetopt. It is a project for learning and for rapid development of personal workflow enhancements and automation.This is evidenced by very little validation; the burden of understanding is left to the implementing developer. For example, options and option-arguments are not duplicate-checked; undefined behavior will occur if you define more than one option object with the same brief or verbose tokens. Additionally, most of Tryst's methods are public rather than private; this is by design, to provide maximum flexibility to the implementer.FeaturesOptions and Option-Arguments specifiable via verbose (e.g.--debug) and brief (e.g.-d) tokensBrief tokens for options can be stacked e.g.-abcConfiguration viaconfig.jsonfile andget_config*APIDecoupled output; easily avoid unnecessary spew tostdoutorstderrwrite*api allows easyto-filefunctionalityProcedural usage instructionsSecrets (e.g. credentials) Support viaget_secretAPIFuture development may include credential encryption or similar security featuresUsageTryst has recently been upgraded. To implement your own Tryst-based app, inherit Tryst and implementmain(). Note theinputsargument which allows you or another developer to call your Tryst'smainmethod with specifiable arguments at code-time, greatly expediting code reuse.fromtrystimportTryst# Tryst class defines your app and its mainfromtrystimportOption# Option defines options and option-arguments e.g. --debugclassMyTrystApp(Tryst):defmain(self,inputs=None):self.appname="my-tryst-app"# ...1. define and add your options and option-arguments hereself.consort(inputs)# ...2. implement your app's behavior here based on self.userargs, self.useroptions, and self.useroptionargumentsself.finish()# Wrap-up and outputAdditional Implementation DetailsOptions, or switches, allow users to toggle functionality in your app. Option-Arguments allow for value-based user-input.Define and add youroptionsandoptionarguments, establishing the rules of engagement for your tryst:# Option("verbose-token", "description", "optional-brief-token")# This option can be used with --my-option or -mself.myoption=Option("my-option","switches behavior in my app","m")# Add the option to your Trystself.add_option(self.myoption)# This option-argument can be used with --my-option-argument="some value" or -a="some value"self.myoptionargument=Option("my-option-argument","lets a user specify a value for my app","a")# Add the option-argument to your trystself.add_option_argument(self.myoptionargument)Govern your app behavior based on the arguments, options, and option-arguments the user specified:ifself.myoptioninself.useroptions:# Act on --my-optionmyoptargval=self.useroptionarguments.get(self.myoptionargument)ifmyoptargval:# Act on --my-option-argument="some value"Provide Usage Instructions to Usersmytryst.show_usage()Note: this may be appropriate in your app due to various criteria, such as if the user specified no arguments, or no options; because every app is different, the burden of making the call to provide this usage is on the developer. Theshow_usage()method creates procedural instructions based on your Tryst's specifiedoptionsandoptionarguments.Decouple & Buffer Your OutputUseself.output(message)for result output andself.error(message)for error output.If you are working to diagnose your app while developing, useself.debug(message)to only display output when--debugis specified orself.DEBUGis set toTrue.Keep in mind thatself.outputandself.errorbuffer output toself.outputbufferandself.errorbufferrespectively, which are written/flushed viaself.write_stdout()andself.write_stderr().Write Your OutputTo write output and errors to stdout and stderr respectively and simply, callself.finish().This indicates that your Tryst has concluded and its outputs are ready.finish()does not callsys.exit(), whilequit()does.sys.exit()interferes with app chaining because it ends the current python session completely. To build your app to be easily callable from another and to support app chaining, useself.finish()or call the appropriate write methods manually:self.write_stdout()self.write_stderr()Tryst usesJSONHelperinternally to cleanly format Pythondictorlistobjects that may be in your Tryst'soutputbufferwhen writing. Expect dictionary and list objects you've passed toself.outputto be written in an alphabetically sorted, readable, 4-space indented multiline string.CLI Conventionsshort options can be stackede.g.tryst -e2is equivalent totryst -e -2andtryst --error --tworespectivelyoption-arguments only allow=, not spaces; complex values should be quoted at the shelle.g.--debug=true, not--debug truee.g.--name="Wholesome Necromancer", not--name=Wholesome Necromancerorder of options, option-arguments, and arguments does not matter in usagee.g.tryst.py --debug -e2 arg1,tryst.py -2e arg1 --debug, andtryst.py -e arg1 -2 --debugare equivalentBest PracticesGet configuration dataafterconsort()is called to ensure the correct configuration file and path are loaded.ExamplesTrivial example of app chaining:#! trystchild.pyfromtrystimportTrystfromtrystimportOptionclassTrystCapitalize(Tryst):defmain(self,inputs=None):# self.DEBUG = Trueself.appname="tryst-capitalize"self.authors="wholesomenecromancer"self.summary="Progeny of Tryst. Capitalizes strings."self.version="0.x.y"self.consort(inputs)forargginself.userargs:self.output(argg.upper())self.finish()#================================================================================classTrystChild(Tryst):defmain(self,inputs=None):# self.DEBUG = Trueself.appname="trystchild"self.authors="wholesomenecromancer"self.summary="Descends from Tryst. Echoes strings."self.version="0.0.12a"# Optionsself.capitalize_option=Option("capitalize-inputs","Capitalizes arguments before echoing using tryst-chaining.","C")self.add_option(self.capitalize_option)# consortself.consort(inputs)self.debug("Example debug statement.")# use the tryst to govern app behavioriflen(self.userargs)<1:self.debug("No args given.")self.show_usage()self.quit()# Capitalize by using TrystCapitalizeifself.capitalize_optioninself.useroptions:captryst=TrystCapitalize()# Constructcaptryst.silence()# Silence console outputcaptryst.main(self.userargs)# Pass the user's argumentsifcaptryst.outputbuffer:# Get TrystCapitalize's output directlyself.userargs=captryst.outputbuffer# Replace our userargs before outputtingforarrginself.userargs:self.output(arrg)self.finish()#================================================================================#----------------------------------------if__name__=="__main__":TrystChild().main()#----------------------------------------TestsTests can be run fromsrc/tryst/viapython -m unittest.DocumentationDocumentation is intended for use withpdoc:pdoc tryst.pypdoc -o <destdir> tryst.pySupportTryst intends to be platform-agnostic but has only been tested in Windows 10 environments with PowerShell 5.x and WSL 2.0's Ubuntu 20.x bash.