diff options
author | noah <noah@656d521f-e311-0410-88e0-e7920216d269> | 2007-03-07 03:38:52 +0000 |
---|---|---|
committer | noah <noah@656d521f-e311-0410-88e0-e7920216d269> | 2007-03-07 03:38:52 +0000 |
commit | 3b83f590704fb158bc8a0df0b681f06f82ff31ac (patch) | |
tree | 8836fb2948afb5712bc45f20d7555bc5e60e4911 | |
parent | f06d6c64d591cefa4bcc72f304f80178a2834990 (diff) | |
download | pexpect-3b83f590704fb158bc8a0df0b681f06f82ff31ac.tar.gz |
Updated docs.
git-svn-id: http://pexpect.svn.sourceforge.net/svnroot/pexpect/trunk@462 656d521f-e311-0410-88e0-e7920216d269
-rw-r--r--[-rwxr-xr-x] | pexpect/ANSI.py | 307 | ||||
-rw-r--r-- | pexpect/FSM.py | 239 | ||||
-rw-r--r-- | pexpect/pexpect.py | 6 | ||||
-rw-r--r-- | pexpect/psh.py | 7 | ||||
-rw-r--r-- | pexpect/pxssh.py | 4 | ||||
-rw-r--r--[-rwxr-xr-x] | pexpect/screen.py | 201 |
6 files changed, 469 insertions, 295 deletions
diff --git a/pexpect/ANSI.py b/pexpect/ANSI.py index 030d3f1..9162389 100755..100644 --- a/pexpect/ANSI.py +++ b/pexpect/ANSI.py @@ -1,134 +1,177 @@ -''' -$Revision: 374 $ -$Date: 2006-03-06 18:47:28 -0800 (Mon, 06 Mar 2006) $ +"""This implements an ANSI terminal emulator as a subclass of screen. -''' +$Id$ +""" # references: -# http://www.retards.org/terminals/vt102.html -# http://vt100.net/docs/vt102-ug/contents.html -# http://vt100.net/docs/vt220-rm/ -# http://www.termsys.demon.co.uk/vtansi.htm -# +# http://www.retards.org/terminals/vt102.html +# http://vt100.net/docs/vt102-ug/contents.html +# http://vt100.net/docs/vt220-rm/ +# http://www.termsys.demon.co.uk/vtansi.htm import screen import FSM import copy import string +def Emit (fsm): + screen = fsm.something[0] + screen.write_ch(fsm.input_symbol) -def Emit (fsm): - screen = fsm.something[0] - screen.write_ch(fsm.input_symbol) def StartNumber (fsm): - fsm.something.append (fsm.input_symbol) + + fsm.something.append (fsm.input_symbol) + def BuildNumber (fsm): - ns = fsm.something.pop() - ns = ns + fsm.input_symbol - fsm.something.append (ns) + + ns = fsm.something.pop() + ns = ns + fsm.input_symbol + fsm.something.append (ns) + def DoBackOne (fsm): - screen = fsm.something[0] - screen.cursor_back () + + screen = fsm.something[0] + screen.cursor_back () + def DoBack (fsm): - count = int(fsm.something.pop()) - screen = fsm.something[0] - screen.cursor_back (count) + + count = int(fsm.something.pop()) + screen = fsm.something[0] + screen.cursor_back (count) + def DoDownOne (fsm): - screen = fsm.something[0] - screen.cursor_down () + + screen = fsm.something[0] + screen.cursor_down () + def DoDown (fsm): - count = int(fsm.something.pop()) - screen = fsm.something[0] - screen.cursor_down (count) + + count = int(fsm.something.pop()) + screen = fsm.something[0] + screen.cursor_down (count) + def DoForwardOne (fsm): - screen = fsm.something[0] - screen.cursor_forward () + + screen = fsm.something[0] + screen.cursor_forward () + def DoForward (fsm): - count = int(fsm.something.pop()) - screen = fsm.something[0] - screen.cursor_forward (count) + + count = int(fsm.something.pop()) + screen = fsm.something[0] + screen.cursor_forward (count) + def DoUpReverse (fsm): - screen = fsm.something[0] - screen.cursor_up_reverse() + + screen = fsm.something[0] + screen.cursor_up_reverse() + def DoUpOne (fsm): - screen = fsm.something[0] - screen.cursor_up () + + screen = fsm.something[0] + screen.cursor_up () + def DoUp (fsm): - count = int(fsm.something.pop()) - screen = fsm.something[0] - screen.cursor_up (count) + + count = int(fsm.something.pop()) + screen = fsm.something[0] + screen.cursor_up (count) + def DoHome (fsm): - c = int(fsm.something.pop()) - r = int(fsm.something.pop()) - screen = fsm.something[0] - screen.cursor_home (r,c) + + c = int(fsm.something.pop()) + r = int(fsm.something.pop()) + screen = fsm.something[0] + screen.cursor_home (r,c) + def DoHomeOrigin (fsm): - c = 1 - r = 1 - screen = fsm.something[0] - screen.cursor_home (r,c) + + c = 1 + r = 1 + screen = fsm.something[0] + screen.cursor_home (r,c) + def DoEraseDown (fsm): - screen = fsm.something[0] - screen.erase_down() + + screen = fsm.something[0] + screen.erase_down() + def DoErase (fsm): - arg = int(fsm.something.pop()) - screen = fsm.something[0] - if arg == 0: - screen.erase_down() - elif arg == 1: - screen.erase_up() - elif arg == 2: - screen.erase_screen() + + arg = int(fsm.something.pop()) + screen = fsm.something[0] + if arg == 0: + screen.erase_down() + elif arg == 1: + screen.erase_up() + elif arg == 2: + screen.erase_screen() + def DoEraseEndOfLine (fsm): - screen = fsm.something[0] - screen.erase_end_of_line() + + screen = fsm.something[0] + screen.erase_end_of_line() + def DoEraseLine (fsm): - screen = fsm.something[0] - if arg == 0: - screen.end_of_line() - elif arg == 1: - screen.start_of_line() - elif arg == 2: - screen.erase_line() + + screen = fsm.something[0] + if arg == 0: + screen.end_of_line() + elif arg == 1: + screen.start_of_line() + elif arg == 2: + screen.erase_line() + def DoEnableScroll (fsm): - screen = fsm.something[0] - screen.scroll_screen() + + screen = fsm.something[0] + screen.scroll_screen() + def DoCursorSave (fsm): - screen = fsm.something[0] - screen.cursor_save_attrs() + + screen = fsm.something[0] + screen.cursor_save_attrs() + def DoCursorRestore (fsm): - screen = fsm.something[0] - screen.cursor_restore_attrs() + + screen = fsm.something[0] + screen.cursor_restore_attrs() + def DoScrollRegion (fsm): - screen = fsm.something[0] - r2 = int(fsm.something.pop()) - r1 = int(fsm.something.pop()) - screen.scroll_screen_rows (r1,r2) + + screen = fsm.something[0] + r2 = int(fsm.something.pop()) + r1 = int(fsm.something.pop()) + screen.scroll_screen_rows (r1,r2) def DoMode (fsm): - screen = fsm.something[0] - mode = fsm.something.pop() # Should be 4 - # screen.setReplaceMode () + + screen = fsm.something[0] + mode = fsm.something.pop() # Should be 4 + # screen.setReplaceMode () + def Log (fsm): - screen = fsm.something[0] - fsm.something = [screen] - fout = open ('log', 'a') - fout.write (fsm.input_symbol + ',' + fsm.current_state + '\n') - fout.close() + + screen = fsm.something[0] + fsm.something = [screen] + fout = open ('log', 'a') + fout.write (fsm.input_symbol + ',' + fsm.current_state + '\n') + fout.close() class term (screen.screen): - '''This is a placeholder. + """This is a placeholder. In theory I might want to add other terminal types. - ''' + """ def __init__ (self, r=24, c=80): screen.screen.__init__(self, r,c) class ANSI (term): - '''This class encapsulates a generic terminal. - It filters a stream and maintains the state of - a screen object. - ''' + + """This class encapsulates a generic terminal. It filters a stream and + maintains the state of a screen object. """ + def __init__ (self, r=24,c=80): + term.__init__(self,r,c) #self.screen = screen (24,80) @@ -199,21 +242,30 @@ class ANSI (term): self.state.add_transition ('m', 'NUMBER_2', None, 'INIT') ### LED control. Same problem as 'm' code. self.state.add_transition ('q', 'NUMBER_2', None, 'INIT') + def process (self, c): + self.state.process(c) + def process_list (self, l): + self.write(l) + def write (self, s): + for c in s: self.process(c) + def flush (self): + pass + def write_ch (self, ch): - '''Puts a character at the current cursor position. - cursor position if moved forward with wrap-around, but - no scrolling is done if the cursor hits the lower-right corner - of the screen. - ''' + + """This puts a character at the current cursor position. cursor + position if moved forward with wrap-around, but no scrolling is done if + the cursor hits the lower-right corner of the screen. """ + #\r and \n both produce a call to crlf(). ch = ch[0] @@ -246,36 +298,37 @@ class ANSI (term): self.cursor_home (self.cur_r, 1) self.erase_line() - def test (self): - import sys - write_text = 'I\'ve got a ferret sticking up my nose.\n' + \ - '(He\'s got a ferret sticking up his nose.)\n' + \ - 'How it got there I can\'t tell\n' + \ - 'But now it\'s there it hurts like hell\n' + \ - 'And what is more it radically affects my sense of smell.\n' + \ - '(His sense of smell.)\n' + \ - 'I can see a bare-bottomed mandril.\n' + \ - '(Slyly eyeing his other nostril.)\n' + \ - 'If it jumps inside there too I really don\'t know what to do\n' + \ - 'I\'ll be the proud posessor of a kind of nasal zoo.\n' + \ - '(A nasal zoo.)\n' + \ - 'I\'ve got a ferret sticking up my nose.\n' + \ - '(And what is worst of all it constantly explodes.)\n' + \ - '"Ferrets don\'t explode," you say\n' + \ - 'But it happened nine times yesterday\n' + \ - 'And I should know for each time I was standing in the way.\n' + \ - 'I\'ve got a ferret sticking up my nose.\n' + \ - '(He\'s got a ferret sticking up his nose.)\n' + \ - 'How it got there I can\'t tell\n' + \ - 'But now it\'s there it hurts like hell\n' + \ - 'And what is more it radically affects my sense of smell.\n' + \ - '(His sense of smell.)' - self.fill('.') - self.cursor_home() - for c in write_text: - self.write_ch (c) - print str(self) - -if __name__ == '__main__': - t = ANSI(6,65) - t.test() +# def test (self): +# +# import sys +# write_text = 'I\'ve got a ferret sticking up my nose.\n' + \ +# '(He\'s got a ferret sticking up his nose.)\n' + \ +# 'How it got there I can\'t tell\n' + \ +# 'But now it\'s there it hurts like hell\n' + \ +# 'And what is more it radically affects my sense of smell.\n' + \ +# '(His sense of smell.)\n' + \ +# 'I can see a bare-bottomed mandril.\n' + \ +# '(Slyly eyeing his other nostril.)\n' + \ +# 'If it jumps inside there too I really don\'t know what to do\n' + \ +# 'I\'ll be the proud posessor of a kind of nasal zoo.\n' + \ +# '(A nasal zoo.)\n' + \ +# 'I\'ve got a ferret sticking up my nose.\n' + \ +# '(And what is worst of all it constantly explodes.)\n' + \ +# '"Ferrets don\'t explode," you say\n' + \ +# 'But it happened nine times yesterday\n' + \ +# 'And I should know for each time I was standing in the way.\n' + \ +# 'I\'ve got a ferret sticking up my nose.\n' + \ +# '(He\'s got a ferret sticking up his nose.)\n' + \ +# 'How it got there I can\'t tell\n' + \ +# 'But now it\'s there it hurts like hell\n' + \ +# 'And what is more it radically affects my sense of smell.\n' + \ +# '(His sense of smell.)' +# self.fill('.') +# self.cursor_home() +# for c in write_text: +# self.write_ch (c) +# print str(self) +# +#if __name__ == '__main__': +# t = ANSI(6,65) +# t.test() diff --git a/pexpect/FSM.py b/pexpect/FSM.py index b327f82..d1ff58a 100644 --- a/pexpect/FSM.py +++ b/pexpect/FSM.py @@ -1,62 +1,66 @@ -'''This module implements a Finite State Machine (FSM). -In addition to state this FSM also maintains a user defined "something". -This "something" is effectively memory, so this FSM could be considered -a Push-down Automata (PDA) since a PDA is a FSM + memory. - -The following describes how the FSM works, but you will probably also need -to see the example function to understand how the FSM is used in practice. - -You define an FSM by building tables of transitions. -For a given input symbol the process() method uses these tables -to decide what action to call and what the next state will be. -The FSM has a table of transitions that associate: +''' This module implements a Finite State Machine (FSM). In addition to state +this FSM also maintains a user defined "something". This "something" is +effectively memory, so this FSM could be considered a Push-down Automata (PDA) +since a PDA is a FSM + memory. + +The following describes how the FSM works, but you will probably also need to +see the example function to understand how the FSM is used in practice. + +You define an FSM by building tables of transitions. For a given input symbol +the process() method uses these tables to decide what action to call and what +the next state will be. The FSM has a table of transitions that associate:: + (input_symbol, current_state) --> (action, next_state) -where "action" is a function you define. The symbols and states -can be any objects. You use the add_transition() and add_transition_list() -methods to add to the transition table. The FSM also has a table -of transitions that associate: + +where "action" is a function you define. The symbols and states can be any +objects. You use the add_transition() and add_transition_list() methods to add +to the transition table. The FSM also has a table of transitions that +associate:: + (current_state) --> (action, next_state) -You use the add_transition_any() method to add to this transition table. -The FSM also has one default transition that is not associated -with any specific input_symbol or state. You use the -set_default_transition() method to set the default transition. - -When an action function is called it is passed a reference to the FSM. -The action function may then access attributes of the FSM such as -input_symbol, current_state, or "something". The "something" attribute -can be any object that you want to pass along to the action functions. -It is not used by the FSM. For parsing you would typically pass a list -to be used as a stack. - -The processing sequence is as follows. -The process() method is given an input_symbol to process. -The FSM will search the table of transitions that associate: + +You use the add_transition_any() method to add to this transition table. The +FSM also has one default transition that is not associated with any specific +input_symbol or state. You use the set_default_transition() method to set the +default transition. + +When an action function is called it is passed a reference to the FSM. The +action function may then access attributes of the FSM such as input_symbol, +current_state, or "something". The "something" attribute can be any object that +you want to pass along to the action functions. It is not used by the FSM. For +parsing you would typically pass a list to be used as a stack. + +The processing sequence is as follows. The process() method is given an +input_symbol to process. The FSM will search the table of transitions that +associate:: + (input_symbol, current_state) --> (action, next_state) -If the pair (input_symbol, current_state) is found then -process() will call the associated action function and then set the -current state to the next_state. -If the FSM cannot find a match for (input_symbol, current_state) -it will then search the table of transitions that associate: +If the pair (input_symbol, current_state) is found then process() will call the +associated action function and then set the current state to the next_state. + +If the FSM cannot find a match for (input_symbol, current_state) it will then +search the table of transitions that associate:: + (current_state) --> (action, next_state) -If the current_state is found then the process() method will call -the associated action function and then set the current state to -the next_state. Notice that this table lacks an input_symbol. -It lets you define transitions for a current_state and ANY input_symbol. -Hence, it is called the "any" table. Remember, it is always checked -after first searching the table for a specific (input_symbol, current_state). - -For the case where the FSM did not match either of the previous two cases -the FSM will try to use the default transition. If the default transition -is defined then the process() method will call the associated action function -and then set the current state to the next_state. This lets you define -a default transition as a catch-all case. You can think of it as an -exception handler. There can be only one default transition. - -Finally, if none of the previous cases are defined for an input_symbol -and current_state then the FSM will raise an exception. -This may be desirable, but you can always prevent this just by -defining a default transition. + +If the current_state is found then the process() method will call the +associated action function and then set the current state to the next_state. +Notice that this table lacks an input_symbol. It lets you define transitions +for a current_state and ANY input_symbol. Hence, it is called the "any" table. +Remember, it is always checked after first searching the table for a specific +(input_symbol, current_state). + +For the case where the FSM did not match either of the previous two cases the +FSM will try to use the default transition. If the default transition is +defined then the process() method will call the associated action function and +then set the current state to the next_state. This lets you define a default +transition as a catch-all case. You can think of it as an exception handler. +There can be only one default transition. + +Finally, if none of the previous cases are defined for an input_symbol and +current_state then the FSM will raise an exception. This may be desirable, but +you can always prevent this just by defining a default transition. Noah Spurrier @@ -64,23 +68,25 @@ $Id$ ''' class ExceptionFSM(Exception): + '''This is the FSM Exception class.''' + def __init__(self, value): self.value = value def __str__(self): return `self.value` class FSM: - '''This is a Finite State Machine (FSM). - ''' + + '''This is a Finite State Machine (FSM). ''' def __init__(self, initial_state, something): - '''This creates the FSM. - You set the initial state here. The "something" attribute is any - object that you want to pass along to the action functions. - It is not used by the FSM. For parsing you would typically pass - a list to be used as a stack. - ''' + + '''This creates the FSM. You set the initial state here. The + "something" attribute is any object that you want to pass along to the + action functions. It is not used by the FSM. For parsing you would + typically pass a list to be used as a stack. ''' + # Map (input_symbol, current_state) --> (action, next_state). self.state_transitions = {} # Map (current_state) --> (action, next_state). @@ -93,69 +99,85 @@ class FSM: self.something = something def reset (self): - '''This sets the current_state to the initial_state and - sets input_symbol to None. - The initial state was set by the constructor __init__(). - ''' + + '''This sets the current_state to the initial_state and sets + input_symbol to None. The initial state was set by the constructor + __init__(). ''' + self.current_state = self.initial_state self.input_symbol = None def add_transition (self, input_symbol, state, action, next_state): - '''This adds a transition that associates + + '''This adds a transition that associates:: + (input_symbol, current_state) --> (action, next_state) - The action may be set to None in which case the process() method - will ignore the action and only set the next_state. + + The action may be set to None in which case the process() method will + ignore the action and only set the next_state. You can also set transitions for a list of symbols by using - add_transition_list(). - ''' + add_transition_list(). ''' + self.state_transitions[(input_symbol, state)] = (action, next_state) def add_transition_list (self, list_input_symbols, state, action, next_state): + '''This adds the same transition for lots of different input symbols. You can pass a list or a string. Note that it is handy to use string.digits, string.whitespace, string.letters, etc. to add - transitions that match character classes. - ''' + transitions that match character classes. ''' + for input_symbol in list_input_symbols: self.add_transition (input_symbol, state, action, next_state) def add_transition_any (self, state, action, next_state): - '''This adds a transition that associates + + '''This adds a transition that associates:: + (current_state) --> (action, next_state) - The process() method checks these associations if it cannot - first find a match of an (input_symbol, current_state). - ''' + + The process() method checks these associations if it cannot first find + a match of an (input_symbol, current_state). ''' + self.state_transitions_any [state] = (action, next_state) def set_default_transition (self, action, next_state): - '''This sets the default transition. - This defines an action and next_state if the FSM cannot find the - input symbol and the current state in the transition list and - if the FSM cannot find the current_state in the transition_any list. - This is useful for catching errors and undefined states. + + '''This sets the default transition. This defines an action and + next_state if the FSM cannot find the input symbol and the current + state in the transition list and if the FSM cannot find the + current_state in the transition_any list. This is useful for catching + errors and undefined states. The default transition can be removed by setting the attribute - default_transition to None. - ''' + default_transition to None. ''' + self.default_transition = (action, next_state) def get_transition (self, input_symbol, state): + '''This returns (action, next state) given an input_symbol and state. - This leaves the FSM unchanged. This does not update the current state - nor does it trigger the output action. Normally you do not call - this method. It is called by process(). + This leaves the FSM unchanged. This does not update the current state + nor does it trigger the output action. Normally you do not call this + method. It is called by process(). - The sequence of steps to check for a defined transition goes from - the most specific to the least specific. - 1. Check state_transitions[] that match (input_symbol, state) - 2. Check state_transitions_any[] that match (state) - In other words, match a specific state and ANY input_symbol. - 3. Check if the default_transition is defined. - This catches any input_symbol and any state. - This is a handler for errors, undefined states, or defaults. - 4. No transition was defined. If we get here then raise an exception. + The sequence of steps to check for a defined transition goes from the + most specific to the least specific:: + + 1. Check state_transitions[] that match (input_symbol, state) + + 2. Check state_transitions_any[] that match (state) In other words, + match a specific state and ANY input_symbol. + + 3. Check if the default_transition is defined. This catches any + input_symbol and any state. This is a handler for errors, undefined + states, or defaults. + + 4. No transition was defined. If we get here then raise an exception. + ''' + if self.state_transitions.has_key((input_symbol, self.current_state)): return self.state_transitions[(input_symbol, self.current_state)] elif self.state_transitions_any.has_key (self.current_state): @@ -167,15 +189,15 @@ class FSM: (str(input_symbol), str(self.current_state)) ) def process (self, input_symbol): - '''This is the main method that you call to process input. - This may cause the FSM to change state and call an action. - This method calls get_transition() to find the action and next_state - associated with the input_symbol and current_state. - If the action is None then the action is not called and - only the current state is changed. - This method processes one input symbol. You can process a list of - symbols (or a string) by calling process_list(). - ''' + + '''This is the main method that you call to process input. This may + cause the FSM to change state and call an action. This method calls + get_transition() to find the action and next_state associated with the + input_symbol and current_state. If the action is None then the action + is not called and only the current state is changed. This method + processes one input symbol. You can process a list of symbols (or a + string) by calling process_list(). ''' + self.input_symbol = input_symbol (action, next_state) = self.get_transition (self.input_symbol, self.current_state) if action != None: @@ -183,9 +205,10 @@ class FSM: self.current_state = next_state def process_list (self, s): - '''This takes a list and sends each element to process(). - The list may be a string. - ''' + + '''This takes a list and sends each element to process(). The list may + be a string. ''' + for c in s: self.process (c) diff --git a/pexpect/pexpect.py b/pexpect/pexpect.py index b378958..a2bacc2 100644 --- a/pexpect/pexpect.py +++ b/pexpect/pexpect.py @@ -167,7 +167,8 @@ def run (command, timeout=-1, withexitstatus=False, events=None, extra_args=None from pexpect import * run ('scp foo myname@host.example.com:.', events={'(?i)password': mypassword}) - == Examples == + Examples + ======== Start the apache daemon on the local machine:: @@ -184,7 +185,8 @@ def run (command, timeout=-1, withexitstatus=False, events=None, extra_args=None from pexpect import * (command_output, exitstatus) = run ('ls -l /bin', withexitstatus=1) - === Tricky Examples === + Tricky Examples + =============== The following will run SSH and execute 'ls -l' on the remote machine. The password 'secret' will be sent if the '(?i)password' pattern is ever seen:: diff --git a/pexpect/psh.py b/pexpect/psh.py index 916c8c5..6737c90 100644 --- a/pexpect/psh.py +++ b/pexpect/psh.py @@ -1,8 +1,9 @@ -"""This is a utility class to make shell scripting easier in Python. -It combines Pexpect and wraps many Standard Python Library functions. -The goal is to make Python an attractive alternative to Sh scripting. +"""This is a utility class to make shell scripting easier in Python. It +combines Pexpect and wraps many Standard Python Library functions. + $Id$ """ + import pexpect, os, sys class psh (object): diff --git a/pexpect/pxssh.py b/pexpect/pxssh.py index c12e871..5bf64e6 100644 --- a/pexpect/pxssh.py +++ b/pexpect/pxssh.py @@ -27,7 +27,7 @@ class pxssh (spawn): unique than just $ or #. This should work on most Borne/Bash or Csh style shells. - Example that runs a few commands on a remote server and prints the result: + Example that runs a few commands on a remote server and prints the result:: import pxssh import getpass @@ -58,7 +58,7 @@ class pxssh (spawn): off any key agents during testing. The 'force_password' attribute will turn off public key authentication. This will only work if the remote SSH server is configured to allow password logins. Example of using 'force_password' - attirbute: + attribute:: s = pxssh.pxssh() s.force_password = True diff --git a/pexpect/screen.py b/pexpect/screen.py index 1c1f217..6bcc3e4 100755..100644 --- a/pexpect/screen.py +++ b/pexpect/screen.py @@ -1,6 +1,7 @@ -''' +"""This implements a virtual screen. + $Id$ -''' +""" import copy #import string @@ -25,8 +26,9 @@ DEL = 127 # Fill character; ignored on input. SPACE = chr(32) # Space or blank character. def constrain (n, min, max): - """This returns n constrained to the min and max bounds. - """ + + """This returns n constrained to the min and max bounds. """ + if n < min: return min if n > max: @@ -34,14 +36,13 @@ def constrain (n, min, max): return n class screen: - """This maintains the state of a virtual text screen. - This maintains a cursor position and handles - scrolling as characters are added. - This supports most of the methods needed by an - ANSI text screen. - Row and column indexes are 1-based (not zero-based, - like arrays). - """ + + """This object maintains the state of a virtual text screen as a + rectangluar array. This maintains a virtual cursor position and handles + scrolling as characters are added. This supports most of the methods needed + by an ANSI text screen. Row and column indexes are 1-based (not zero-based, + like arrays). """ + def __init__ (self, r=24,c=80): self.rows = r self.cols = c @@ -57,16 +58,18 @@ class screen: return '\n'.join ([ ''.join(c) for c in self.w ]) def dump (self): - """This returns a copy of the screen as a string. - This is similar to __str__ except that lines - are not terminated with line feeds. - """ + + """This returns a copy of the screen as a string. This is similar to + __str__ except that lines are not terminated with line feeds. """ + return ''.join ([ ''.join(c) for c in self.w ]) def fill (self, ch=SPACE): + self.fill_region (1,1,self.rows,self.cols, ch) def fill_region (self, rs,cs, re,ce, ch=SPACE): + rs = constrain (rs, 1, self.rows) re = constrain (re, 1, self.rows) cs = constrain (cs, 1, self.cols) @@ -80,12 +83,17 @@ class screen: self.put_abs (r,c,ch) def cr (self): - '''This moves the cursor to the beginning (col 1) of the current row. - ''' + + """This moves the cursor to the beginning (col 1) of the current row. + """ + self.cursor_home (self.cur_r, 1) + def lf (self): - '''This moves the cursor down with scrolling. - ''' + + """This moves the cursor down with scrolling. + """ + old_r = self.cur_r self.cursor_down() if old_r == self.cur_r: @@ -93,51 +101,69 @@ class screen: self.erase_line() def crlf (self): - '''This advances the cursor with CRLF properties. + + """This advances the cursor with CRLF properties. The cursor will line wrap and the screen may scroll. - ''' + """ + self.cr () self.lf () def newline (self): + """This is an alias for crlf(). """ + self.crlf() def put_abs (self, r, c, ch): - '''Screen array starts at 1 index.''' + + """Screen array starts at 1 index.""" + r = constrain (r, 1, self.rows) c = constrain (c, 1, self.cols) ch = str(ch)[0] self.w[r-1][c-1] = ch + def put (self, ch): + """This puts a characters at the current cursor position. """ + self.put_abs (self.cur_r, self.cur_c, ch) def insert_abs (self, r, c, ch): - '''This inserts a character at (r,c). Everything under + + """This inserts a character at (r,c). Everything under and to the right is shifted right one character. The last character of the line is lost. - ''' + """ + r = constrain (r, 1, self.rows) c = constrain (c, 1, self.cols) for ci in range (self.cols, c, -1): self.put_abs (r,ci, self.get_abs(r,ci-1)) self.put_abs (r,c,ch) + def insert (self, ch): + self.insert_abs (self.cur_r, self.cur_c, ch) def get_abs (self, r, c): + r = constrain (r, 1, self.rows) c = constrain (c, 1, self.cols) return self.w[r-1][c-1] + def get (self): + self.get_abs (self.cur_r, self.cur_c) def get_region (self, rs,cs, re,ce): - '''This returns a list of lines representing the region. - ''' + + """This returns a list of lines representing the region. + """ + rs = constrain (rs, 1, self.rows) re = constrain (re, 1, self.rows) cs = constrain (cs, 1, self.cols) @@ -156,107 +182,177 @@ class screen: return sc def cursor_constrain (self): - '''This keeps the cursor within the screen area. - ''' + + """This keeps the cursor within the screen area. + """ + self.cur_r = constrain (self.cur_r, 1, self.rows) self.cur_c = constrain (self.cur_c, 1, self.cols) + def cursor_home (self, r=1, c=1): # <ESC>[{ROW};{COLUMN}H + self.cur_r = r self.cur_c = c self.cursor_constrain () + def cursor_back (self,count=1): # <ESC>[{COUNT}D (not confused with down) + self.cur_c = self.cur_c - count self.cursor_constrain () + def cursor_down (self,count=1): # <ESC>[{COUNT}B (not confused with back) + self.cur_r = self.cur_r + count self.cursor_constrain () + def cursor_forward (self,count=1): # <ESC>[{COUNT}C + self.cur_c = self.cur_c + count self.cursor_constrain () + def cursor_up (self,count=1): # <ESC>[{COUNT}A + self.cur_r = self.cur_r - count self.cursor_constrain () + def cursor_up_reverse (self): # <ESC> M (called RI -- Reverse Index) + old_r = self.cur_r self.cursor_up() if old_r == self.cur_r: self.scroll_up() + def cursor_force_position (self, r, c): # <ESC>[{ROW};{COLUMN}f - '''Identical to Cursor Home.''' + + """Identical to Cursor Home.""" + self.cursor_home (r, c) + def cursor_save (self): # <ESC>[s - '''Save current cursor position.''' + + """Save current cursor position.""" + self.cursor_save_attrs() + def cursor_unsave (self): # <ESC>[u - '''Restores cursor position after a Save Cursor.''' + + """Restores cursor position after a Save Cursor.""" + self.cursor_restore_attrs() + def cursor_save_attrs (self): # <ESC>7 - '''Save current cursor position.''' + + """Save current cursor position.""" + self.cur_saved_r = self.cur_r self.cur_saved_c = self.cur_c + def cursor_restore_attrs (self): # <ESC>8 - '''Restores cursor position after a Save Cursor.''' + + """Restores cursor position after a Save Cursor.""" + self.cursor_home (self.cur_saved_r, self.cur_saved_c) + def scroll_constrain (self): - '''This keeps the scroll region within the screen region.''' + + """This keeps the scroll region within the screen region.""" + if self.scroll_row_start <= 0: self.scroll_row_start = 1 if self.scroll_row_end > self.rows: self.scroll_row_end = self.rows + def scroll_screen (self): # <ESC>[r - '''Enable scrolling for entire display.''' + + """Enable scrolling for entire display.""" + self.scroll_row_start = 1 self.scroll_row_end = self.rows + def scroll_screen_rows (self, rs, re): # <ESC>[{start};{end}r - '''Enable scrolling from row {start} to row {end}.''' + + """Enable scrolling from row {start} to row {end}.""" + self.scroll_row_start = rs self.scroll_row_end = re self.scroll_constrain() + def scroll_down (self): # <ESC>D - '''Scroll display down one line.''' + + """Scroll display down one line.""" + # Screen is indexed from 1, but arrays are indexed from 0. s = self.scroll_row_start - 1 e = self.scroll_row_end - 1 self.w[s+1:e+1] = copy.deepcopy(self.w[s:e]) + def scroll_up (self): # <ESC>M - '''Scroll display up one line.''' + + """Scroll display up one line.""" + # Screen is indexed from 1, but arrays are indexed from 0. s = self.scroll_row_start - 1 e = self.scroll_row_end - 1 self.w[s:e] = copy.deepcopy(self.w[s+1:e+1]) + def erase_end_of_line (self): # <ESC>[0K -or- <ESC>[K - '''Erases from the current cursor position to - the end of the current line.''' + + """Erases from the current cursor position to the end of the current + line.""" + self.fill_region (self.cur_r, self.cur_c, self.cur_r, self.cols) + def erase_start_of_line (self): # <ESC>[1K - '''Erases from the current cursor position to - the start of the current line.''' + + """Erases from the current cursor position to the start of the current + line.""" + self.fill_region (self.cur_r, 1, self.cur_r, self.cur_c) + def erase_line (self): # <ESC>[2K - '''Erases the entire current line.''' + + """Erases the entire current line.""" + self.fill_region (self.cur_r, 1, self.cur_r, self.cols) + def erase_down (self): # <ESC>[0J -or- <ESC>[J - '''Erases the screen from the current line down to - the bottom of the screen.''' + + """Erases the screen from the current line down to the bottom of the + screen.""" + self.erase_end_of_line () self.fill_region (self.cur_r + 1, 1, self.rows, self.cols) + def erase_up (self): # <ESC>[1J - '''Erases the screen from the current line up to - the top of the screen.''' + + """Erases the screen from the current line up to the top of the + screen.""" + self.erase_start_of_line () self.fill_region (self.cur_r-1, 1, 1, self.cols) + def erase_screen (self): # <ESC>[2J - '''Erases the screen with the background color.''' + + """Erases the screen with the background color.""" + self.fill () def set_tab (self): # <ESC>H - '''Sets a tab at the current position.''' + + """Sets a tab at the current position.""" + pass + def clear_tab (self): # <ESC>[g - '''Clears tab at the current position.''' + + """Clears tab at the current position.""" + pass + def clear_all_tabs (self): # <ESC>[3g - '''Clears all tabs.''' + + """Clears all tabs.""" + pass # Insert line Esc [ Pn L @@ -264,4 +360,3 @@ class screen: # Delete character Esc [ Pn P # Scrolling region Esc [ Pn(top);Pn(bot) r - |