A lightweight CLI support package.
Project description
Tryst
CLI support package.
One'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 as argparse or getopt. 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.
Features
- Options and Option-Arguments specifiable via verbose (e.g.
--debug
) and brief (e.g.-d
) tokens- Brief tokens for options can be stacked e.g.
-abc
- Brief tokens for options can be stacked e.g.
- Configuration via
config.json
file andget_config*
API - Decoupled output; easily avoid unnecessary spew to
stdout
orstderr
write*
api allows easy to-file functionality
- Procedural usage instructions
- Secrets (e.g. credentials) Support via
get_secret
API- Future development may include credential encryption or similar security features
Usage
Tryst has recently been upgraded. To implement your own Tryst-based app, inherit Tryst and implement main()
. Note the inputs
argument which allows you or another developer to call your Tryst's main
method with specifiable arguments at code-time, greatly expediting code reuse.
from tryst import Tryst # Tryst class defines your app and its main
from tryst import Option # Option defines options and option-arguments e.g. --debug
class MyTrystApp(Tryst):
def main(self, inputs = None):
self.appname = "my-tryst-app"
# ...1. define and add your options and option-arguments here
self.consort(inputs)
# ...2. implement your app's behavior here based on self.userargs, self.useroptions, and self.useroptionarguments
self.finish() # Wrap-up and output
Additional Implementation Details
Options, or switches, allow users to toggle functionality in your app. Option-Arguments allow for value-based user-input.
- Define and add your
options
andoptionarguments
, establishing the rules of engagement for your tryst:
# Option("verbose-token", "description", "optional-brief-token")
# This option can be used with --my-option or -m
self.myoption = Option("my-option", "switches behavior in my app", "m")
# Add the option to your Tryst
self.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 tryst
self.add_option_argument(self.myoptionargument)
- Govern your app behavior based on the arguments, options, and option-arguments the user specified:
if self.myoption in self.useroptions:
# Act on --my-option
myoptargval = self.useroptionarguments.get(self.myoptionargument)
if myoptargval:
# Act on --my-option-argument="some value"
Provide Usage Instructions to Users
mytryst.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. The
show_usage()
method creates procedural instructions based on your Tryst's specifiedoptions
andoptionarguments
.
Decouple & Buffer Your Output
Use self.output(message)
for result output and self.error(message)
for error output.
If you are working to diagnose your app while developing, use
self.debug(message)
to only display output when--debug
is specified orself.DEBUG
is set toTrue
.
Keep in mind that
self.output
andself.error
buffer output toself.outputbuffer
andself.errorbuffer
respectively, which are written/flushed viaself.write_stdout()
andself.write_stderr()
.
Write Your Output
To write output and errors to stdout and stderr respectively and simply, call self.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 uses JSONHelper
internally to cleanly format Python dict
or list
objects that may be in your Tryst's outputbuffer
when writing. Expect dictionary and list objects you've passed to self.output
to be written in an alphabetically sorted, readable, 4-space indented multiline string.
CLI Conventions
- short options can be stacked
- e.g.
tryst -e2
is equivalent totryst -e -2
andtryst --error --two
respectively
- e.g.
- option-arguments only allow
=
, not spaces; complex values should be quoted at the shell- e.g.
--debug=true
, not--debug true
- e.g.
--name="Wholesome Necromancer"
, not--name=Wholesome Necromancer
- e.g.
- order of options, option-arguments, and arguments does not matter in usage
- e.g.
tryst.py --debug -e2 arg1
,tryst.py -2e arg1 --debug
, andtryst.py -e arg1 -2 --debug
are equivalent
- e.g.
Best Practices
- Get configuration data after
consort()
is called to ensure the correct configuration file and path are loaded.
Examples
Trivial example of app chaining:
#! trystchild.py
from tryst import Tryst
from tryst import Option
class TrystCapitalize(Tryst):
def main(self, inputs = None):
# self.DEBUG = True
self.appname = "tryst-capitalize"
self.authors = "wholesomenecromancer"
self.summary = "Progeny of Tryst. Capitalizes strings."
self.version = "0.x.y"
self.consort(inputs)
for argg in self.userargs:
self.output(argg.upper())
self.finish()
#================================================================================
class TrystChild(Tryst):
def main(self, inputs = None):
# self.DEBUG = True
self.appname = "trystchild"
self.authors = "wholesomenecromancer"
self.summary = "Descends from Tryst. Echoes strings."
self.version = "0.0.12a"
# Options
self.capitalize_option = Option("capitalize-inputs", "Capitalizes arguments before echoing using tryst-chaining.", "C")
self.add_option(self.capitalize_option)
# consort
self.consort(inputs)
self.debug("Example debug statement.")
# use the tryst to govern app behavior
if len(self.userargs) < 1:
self.debug("No args given.")
self.show_usage()
self.quit()
# Capitalize by using TrystCapitalize
if self.capitalize_option in self.useroptions:
captryst = TrystCapitalize() # Construct
captryst.silence() # Silence console output
captryst.main(self.userargs) # Pass the user's arguments
if captryst.outputbuffer: # Get TrystCapitalize's output directly
self.userargs = captryst.outputbuffer # Replace our userargs before outputting
for arrg in self.userargs:
self.output(arrg)
self.finish()
#================================================================================
#----------------------------------------
if __name__ == "__main__":
TrystChild().main()
#----------------------------------------
Tests
Tests can be run from src/tryst/
via python -m unittest
.
Documentation
Documentation is intended for use with pdoc
:
pdoc tryst.py
pdoc -o <destdir> tryst.py
Support
Tryst 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.
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.