Python library to build simple user-friendly CLI API for your Python scripts.
Allows mapping commands to classes, class methods and functions.
Supports both def
and async def
.
Let's assume we have script dumb_ci
with next class:
from asyncio import sleep
class Computer:
def __init__(self, computer_name: str = "Default"):
self.computer_name = computer_name
async def compile_code(self, project_name: str = "TestProject"):
await sleep(2) # build something
print(f"Built finished for {self.computer_name} {project_name}")
And next function:
from asyncio import sleep
async def deploy_code(project_name: str = "TestProject"):
await sleep(1)
print(f"Deployed {project_name}")
With this library you are able to create simple API for CLI.
./dumb_ci computer compile_code
Library allows passing parameters.
./dumb_ci computer '"Build server #2"' compile_code '"MegaProject"'
You can order commands using basic shell tools
./dumb_ci computer compile_code && ./dumb_ci deploy_code
./dumb_ci computer compile_code '"Project1"' & ./dumb_ci computer compile_code '"Project2"'
Additionally, you can use operators and
(asynchronous), then
(synchronous) and parentheses.
./dumb_ci computer compile_code then deploy_code
./dumb_ci (computer compile_code then deploy_code) and (computer '"2"' compile_code '"Project2"' then deploy_code '"Project2"')
To list all available commands there is a help
operator.
./dumb_ci help
> computer
> computer compile_code
> deploy_code
To obtain help for a specific command just add help
before command.
If no help text provided in command definition, the one received with Python help
command will be used.
./dumb_ci deploy_code
> Deploys code
./dumb_ci computer compile_code
> Compiles code
Assuming we have class Computer
and function deploy_code
from example above entire integration would look like this:
import sys
from moxom import Moxom, Routine, create_routines
routines = create_routines( # Utility function to create Dict[str, Routine]
Routine( # Class representation of a command in CLI
"deploy_code", # Command name
deploy_code, # Function to call
help="Deploys code to AWS" # Text for help
),
Routine( # Class representation of a command in CLI
"computer", # Command name
Computer, # Constructor of class Computer and it is a function
subroutines=create_routines( # As we want to invoke methods of Computer, we can specify subroutines
Routine( # which are represented by Computer class methods
"compile_code", # Subroutine name. "computer compile_code"
Computer.compile_code, # Method of Computer to be called,
help="Compiles code" # which will receive instance created by "computer" as `self`
)
)
)
)
if __name__ == '__main__':
moxom = Moxom(routines)
arguments = sys.argv[1:]
moxom.execute(" ".join(arguments))
To increase comfortability you may want to have simple interactive mode
if __name__ == '__main__':
moxom = Moxom(routines)
arguments = sys.argv[1:]
if len(arguments) == 0:
# interactive mode
print("Interactive mode")
while True:
moxom.execute(input("> "))
else:
# single line mode
moxom.execute(" ".join(arguments))
Example of usage and integration is here.
To integrate library into existing Python project, just import as in next line:
from moxom import Moxom, Routine, create_routines
Moxom
- library entry-point.
Expects to receive Dict[str, Routine]
on construction.
Optionally you can pass environment file path.
Call execute(line)
to process a single line of input.
Routine
- class representation of a single command.
It must have a name and a function to call.
Optionally you may set subroutines and help
text.
create_routines
- Utility function to create Dict[str, Routine]
.
Allows running commands asynchronously, &
in Shell, maps to asyncio.gather
.
# Starts computers simultaneously
computer "1" start and computer "2" start
Allows running commands consequently, &&
in Shell, maps to consequent code with await
.
# Starts computer 1, waits for it to start, and then starts computer 2
computer "1" start then computer "2" start
To specify higher precedence of operation, you can use parentheses.
# Start computer 1 and computer 2 simultaneously.
# Wait for both to start.
# Then shut down both computers 1 and 2 simultaneously.
(computer "1" start and computer "2" start) then (computer "1" shutdown and computer "2" shutdown)
To list all available commands there is a help
operator.
./dumb_ci help
> computer
> computer compile_code
> deploy_code
To obtain help for a specific command just add help
before command.
If no help text provided in routine definition, the one received with Python help
command will be used.
./dumb_ci deploy_code
> Deploys code
./dumb_ci computer compile_code
> Compiles code
Commands and variables can be declared with =
operator.
It can be a declaration of an argument.
> mega_project = "MegaProject"
# returns "MegaProject"
> mega_project
Or of a function.
> comp2_mega_full_ci = computer "Comp2" compile_code "MegaProject" then deploy_code "MegaProject"
# runs code
> comp2_mega_full_ci
Additionally, you can provide arguments for a function.
> full_ci comp proj = computer comp compile_code proj then deploy_code proj
# runs full ci on Comp2 for MegaProject
> full_ci "Comp2" "MegaProject"
# declares a function
> comp2_mega_full_ci = full_ci "Comp2" "MegaProject"
# runs full ci on Comp2 for MegaProject
> comp2_mega_full_ci
When strings are passed from shell to python, shell removes quotes.
./dumb_ci computer "Build server #2" compile_code
is parsed as computer Build server #2 compile_code
and leads to exception.
Using from Shell you have to wrap strings twice with double quotation marks, or with quotes and escaping or by using both single quotes and double quotes:
./dumb_ci computer '"Build server #2"' compile_code
If you want to create simple scenarios with Moxom syntax or just to specify variables on script startup, you can create a file with commands, which will be executed on Moxom initialization.
env_file = os.path.join(os.path.dirname(__file__), "example_mox_env.ini")
moxom = Moxom(routines, moxom_env=env_file)
You can find example of environment file in example_mox_env.ini and integration in example module.