Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions docs/macros.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
title: User Macros
---

Users may define macros for `zti`. When defined, user macros appear in the list of available commands for `zti`. User macros are created by placing Python files in the ZTI macros directory, which defaults to the path `~/.zti-mac`. This path may be overridden with the `ZTI_MACROS_DIR` environment variables.

User macro file names must take the form `my_macro.py`, and must contain a function with the signature,

```python
def do_my_macro(zti, arg):
...
```

This function is invoked when the macro command is run from `zti`. It is passed the current `zti` instance as the first argument and any arguments to the command as the second argument.

The macro file may optionally also contain a function with the signature,

```python
def help_my_macro(zti):
...
```

This function will be registered as the help command for the main macro command.

See `examples/macros/logon.py` for an example of a user macro.

User macros which conflict with existing ZTI commands are ignored.
25 changes: 25 additions & 0 deletions examples/macros/logon.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from tnz import ati

def do_logon(zti, arg):
logon_setup()
ati.wait(lambda: ati.scrhas("Enter: "))
ati.send("app1 userid[enter]")
ati.wait(lambda: ati.scrhas("Password ===> "))
ati.send("password[enter]")

# maybe look for a status message
ati.wait(lambda: ati.scrhas("ICH70001I"))

# do something useful
ati.wait(lambda: ati.scrhas("***"))
ati.send("[enter]")
ati.wait(lambda: ati.scrhas("READY"))

def logon_setup():
ati.set("TRACE", "ALL")
ati.set("LOGDEST", "example.log")

ati.set("ONERROR", "1")
ati.set("DISPLAY", "HOST")
ati.set("SESSION_HOST", "mvs1")
ati.set("SESSION", "A")
70 changes: 70 additions & 0 deletions tnz/zti.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
import cmd
import logging
import os
import pathlib
import platform
import signal
import sys
Expand Down Expand Up @@ -173,6 +174,8 @@ def __init__(self, stdin=None, stdout=None):

self.__install_plugins()

self.__register_macros()

# Methods

def atexit(self):
Expand Down Expand Up @@ -2704,6 +2707,73 @@ def __refresh(self):

self.stdscr.refresh(_win_callback=self.__set_event_fn())

def __register_macros(self):
import importlib.util
import sys
import types

macros_dir = os.getenv("ZTI_MACROS_DIR")
if macros_dir is None:
macros_dir = os.path.expanduser("~/.zti-mac")

if not os.path.isdir(macros_dir):
_logger.error(f"{macros_dir} is not a directory")
return

for macro_file in os.listdir(macros_dir):
macro_file_path = pathlib.PurePath(macro_file)

if macro_file_path.suffix != ".py":
continue

macro_name = macro_file_path.stem

# Ignore macros with uppercase letters or
# spaces
if ' ' in macro_name or not macro_name.islower():
continue

# Ignore macros which already exist
if f"do_{macro_name}" in self.get_names():
_logger.warning(f"Function with name do_{macro_name}"
" already exists, macro registration"
" failed")
continue

macro_path = os.path.join(macros_dir, macro_file)

# Import the user macro as a module
macro_spec = importlib.util.spec_from_file_location(
f"module.{macro_name}", macro_path)
macro_module = importlib.util.module_from_spec(macro_spec)
sys.modules[f"module.{macro_name}"] = macro_module
macro_spec.loader.exec_module(macro_module)

# Find the function
if hasattr(macro_module, f"do_{macro_name}"):
def do_macro(zti, arg):
self.__bg_wait_end()
macro_func = getattr(macro_module,
f"do_{macro_name}")
macro_func(zti, arg)

# Create a new bound method for the `Zti` class for this
# function
setattr(Zti, f"do_{macro_name}",
types.MethodType(
do_macro,
self))

# Check if a corresponding help function exists
if hasattr(macro_module, f"help_{macro_name}"):
# Create a new bound method for the `Zti` class
# for this function
setattr(Zti, f"help_{macro_name}",
types.MethodType(
getattr(macro_module,
f"help_{macro_name}"),
self))

def __scale_size(self, maxrow, maxcol):
arows, acols = self.autosize
aspect1 = arows / acols
Expand Down