Skip to main content

A lightweight CLI support package.

Project description

Tryst

CLI support package.

tryst on Github

GNU GPLv3

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
  • Configuration via config.json file and get_config* API
  • Decoupled output; easily avoid unnecessary spew to stdout or stderr
    • 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.

  1. Define and add your options and optionarguments, 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)
  1. 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 specified options and optionarguments.

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 or self.DEBUG is set to True.

Keep in mind that self.output and self.error buffer output to self.outputbuffer and self.errorbuffer respectively, which are written/flushed via self.write_stdout() and self.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 call sys.exit(), while quit() 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, use self.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 to tryst -e -2 and tryst --error --two respectively
  • 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
  • order of options, option-arguments, and arguments does not matter in usage
    • e.g. tryst.py --debug -e2 arg1, tryst.py -2e arg1 --debug, and tryst.py -e arg1 -2 --debug are equivalent

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


Download files

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

Source Distribution

tryst-0.0.2.tar.gz (28.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

tryst-0.0.2-py3-none-any.whl (28.3 kB view details)

Uploaded Python 3

File details

Details for the file tryst-0.0.2.tar.gz.

File metadata

  • Download URL: tryst-0.0.2.tar.gz
  • Upload date:
  • Size: 28.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.7.1 importlib_metadata/4.10.0 pkginfo/1.8.2 requests/2.27.0 requests-toolbelt/0.9.1 tqdm/4.62.3 CPython/3.10.0

File hashes

Hashes for tryst-0.0.2.tar.gz
Algorithm Hash digest
SHA256 2969e8591e90653e73b956ddf073f33a826137c3c2abfe357f83e5a1f3b16dbe
MD5 3ce09aae55ba9e3d495ab97d9be7b9eb
BLAKE2b-256 cb9efe6b6e5f07d09d6add80d44ac921a9c0356a95647ea710871a7725e1df02

See more details on using hashes here.

File details

Details for the file tryst-0.0.2-py3-none-any.whl.

File metadata

  • Download URL: tryst-0.0.2-py3-none-any.whl
  • Upload date:
  • Size: 28.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.7.1 importlib_metadata/4.10.0 pkginfo/1.8.2 requests/2.27.0 requests-toolbelt/0.9.1 tqdm/4.62.3 CPython/3.10.0

File hashes

Hashes for tryst-0.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 1c23a74a9d265625a75ad4ae7801069353817e0a5fe2c5cadf01a4c2be02808a
MD5 458921684a5aff98a961be8aba11358b
BLAKE2b-256 ea50ace424610c956ac88b035581789610fe40309588d7bcccf759191f7f7e51

See more details on using hashes here.

Supported by

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