move documentation to be below the declaration and not above, this makes it parsable by python's help command, althought it is not as pretty, but hay, help() is awesome

Signed-off-by: Mattias Andrée <maandree@operamail.com>
This commit is contained in:
Mattias Andrée 2013-08-12 07:54:31 +02:00
parent a18804002b
commit ecdac1b6a1
12 changed files with 583 additions and 579 deletions

View file

@ -33,35 +33,38 @@ from common import *
ARGUMENTLESS = 0
''' '''
Option takes no arguments Option takes no arguments
''' '''
ARGUMENTLESS = 0
ARGUMENTED = 1
''' '''
Option takes one argument per instance Option takes one argument per instance
''' '''
ARGUMENTED = 1
VARIADIC = 2
''' '''
Option consumes all following arguments Option consumes all following arguments
''' '''
VARIADIC = 2
'''
Simple argument parser
'''
class ArgParser(): class ArgParser():
''' '''
Constructor. Simple argument parser
The short description is printed on same line as the program name
@param program:str The name of the program
@param description:str Short, single-line, description of the program
@param usage:str Formated, multi-line, usage text
@param longdescription:str Long, multi-line, description of the program, may be `None`
''' '''
def __init__(self, program, description, usage, longdescription = None): def __init__(self, program, description, usage, longdescription = None):
'''
Constructor.
The short description is printed on same line as the program name
@param program:str The name of the program
@param description:str Short, single-line, description of the program
@param usage:str Formated, multi-line, usage text
@param longdescription:str Long, multi-line, description of the program, may be `None`
'''
self.linuxvt = ('TERM' in os.environ) and (os.environ['TERM'] == 'linux') self.linuxvt = ('TERM' in os.environ) and (os.environ['TERM'] == 'linux')
self.__program = program self.__program = program
self.__description = description self.__description = description
@ -72,41 +75,41 @@ class ArgParser():
self.optmap = {} self.optmap = {}
'''
Add option that takes no arguments
@param alternatives:list<str> Option names
@param help:str Short description, use `None` to hide the option
'''
def add_argumentless(self, alternatives, help = None): def add_argumentless(self, alternatives, help = None):
'''
Add option that takes no arguments
@param alternatives:list<str> Option names
@param help:str Short description, use `None` to hide the option
'''
self.__arguments.append((ARGUMENTLESS, alternatives, None, help)) self.__arguments.append((ARGUMENTLESS, alternatives, None, help))
stdalt = alternatives[0] stdalt = alternatives[0]
self.opts[stdalt] = None self.opts[stdalt] = None
for alt in alternatives: for alt in alternatives:
self.optmap[alt] = (stdalt, ARGUMENTLESS) self.optmap[alt] = (stdalt, ARGUMENTLESS)
'''
Add option that takes one argument
@param alternatives:list<str> Option names
@param arg:str The name of the takes argument, one word
@param help:str Short description, use `None` to hide the option
'''
def add_argumented(self, alternatives, arg, help = None): def add_argumented(self, alternatives, arg, help = None):
'''
Add option that takes one argument
@param alternatives:list<str> Option names
@param arg:str The name of the takes argument, one word
@param help:str Short description, use `None` to hide the option
'''
self.__arguments.append((ARGUMENTED, alternatives, arg, help)) self.__arguments.append((ARGUMENTED, alternatives, arg, help))
stdalt = alternatives[0] stdalt = alternatives[0]
self.opts[stdalt] = None self.opts[stdalt] = None
for alt in alternatives: for alt in alternatives:
self.optmap[alt] = (stdalt, ARGUMENTED) self.optmap[alt] = (stdalt, ARGUMENTED)
'''
Add option that takes all following argument
@param alternatives:list<str> Option names
@param arg:str The name of the takes arguments, one word
@param help:str Short description, use `None` to hide the option
'''
def add_variadic(self, alternatives, arg, help = None): def add_variadic(self, alternatives, arg, help = None):
'''
Add option that takes all following argument
@param alternatives:list<str> Option names
@param arg:str The name of the takes arguments, one word
@param help:str Short description, use `None` to hide the option
'''
self.__arguments.append((VARIADIC, alternatives, arg, help)) self.__arguments.append((VARIADIC, alternatives, arg, help))
stdalt = alternatives[0] stdalt = alternatives[0]
self.opts[stdalt] = None self.opts[stdalt] = None
@ -114,13 +117,13 @@ class ArgParser():
self.optmap[alt] = (stdalt, VARIADIC) self.optmap[alt] = (stdalt, VARIADIC)
'''
Parse arguments
@param args:list<str> The command line arguments, should include the execute file at index 0, `sys.argv` is default
@return :bool Whether no unrecognised option is used
'''
def parse(self, argv = sys.argv): def parse(self, argv = sys.argv):
'''
Parse arguments
@param args:list<str> The command line arguments, should include the execute file at index 0, `sys.argv` is default
@return :bool Whether no unrecognised option is used
'''
self.argcount = len(argv) - 1 self.argcount = len(argv) - 1
self.files = [] self.files = []
@ -242,10 +245,10 @@ class ArgParser():
return self.rc return self.rc
'''
Prints a colourful help message
'''
def help(self): def help(self):
'''
Prints a colourful help message
'''
print('\033[1m%s\033[21m %s %s' % (self.__program, '-' if self.linuxvt else '', self.__description)) print('\033[1m%s\033[21m %s %s' % (self.__program, '-' if self.linuxvt else '', self.__description))
print() print()
if self.__longdescription is not None: if self.__longdescription is not None:

View file

@ -36,25 +36,26 @@ from ucs import *
'''
Super-ultra-extreme-awesomazing replacement for cowsay
'''
class Backend(): class Backend():
''' '''
Constructor Super-ultra-extreme-awesomazing replacement for cowsay
@param message:str The message spoken by the pony
@param ponyfile:str The pony file
@param wrapcolumn:int The column at where to wrap the message, `None` for no wrapping
@param width:int The width of the screen, `None` if truncation should not be applied
@param balloon:Balloon The balloon style object, `None` if only the pony should be printed
@param hyphen:str How hyphens added by the wordwrapper should be printed
@param linkcolour:str How to colour the link character, empty string if none
@param ballooncolour:str How to colour the balloon, empty string if none
@param mode:str Mode string for the pony
@parma infolevel:int 2 if ++info is used, 1 if --info is used and 0 otherwise
''' '''
def __init__(self, message, ponyfile, wrapcolumn, width, balloon, hyphen, linkcolour, ballooncolour, mode, infolevel): def __init__(self, message, ponyfile, wrapcolumn, width, balloon, hyphen, linkcolour, ballooncolour, mode, infolevel):
'''
Constructor
@param message:str The message spoken by the pony
@param ponyfile:str The pony file
@param wrapcolumn:int The column at where to wrap the message, `None` for no wrapping
@param width:int The width of the screen, `None` if truncation should not be applied
@param balloon:Balloon The balloon style object, `None` if only the pony should be printed
@param hyphen:str How hyphens added by the wordwrapper should be printed
@param linkcolour:str How to colour the link character, empty string if none
@param ballooncolour:str How to colour the balloon, empty string if none
@param mode:str Mode string for the pony
@parma infolevel:int 2 if ++info is used, 1 if --info is used and 0 otherwise
'''
self.message = message self.message = message
self.ponyfile = ponyfile self.ponyfile = ponyfile
self.wrapcolumn = None if wrapcolumn is None else wrapcolumn - (0 if balloon is None else balloon.minwidth) self.wrapcolumn = None if wrapcolumn is None else wrapcolumn - (0 if balloon is None else balloon.minwidth)
@ -78,10 +79,10 @@ class Backend():
self.pony = None self.pony = None
'''
Process all data
'''
def parse(self): def parse(self):
'''
Process all data
'''
self.__loadFile() self.__loadFile()
if self.pony.startswith('$$$\n'): if self.pony.startswith('$$$\n'):
@ -126,14 +127,14 @@ class Backend():
self.__truncate() self.__truncate()
'''
Format metadata to be nicely printed, this include bold keys
@param info:str The metadata
@return :str The metadata nicely formated
'''
@staticmethod @staticmethod
def formatInfo(info): def formatInfo(info):
'''
Format metadata to be nicely printed, this include bold keys
@param info:str The metadata
@return :str The metadata nicely formated
'''
info = info.split('\n') info = info.split('\n')
tags = '' tags = ''
comment = '' comment = ''
@ -156,10 +157,10 @@ class Backend():
return tags + comment return tags + comment
'''
Remove padding spaces fortune cookies are padded with whitespace (damn featherbrains)
'''
def __unpadMessage(self): def __unpadMessage(self):
'''
Remove padding spaces fortune cookies are padded with whitespace (damn featherbrains)
'''
lines = self.message.split('\n') lines = self.message.split('\n')
for spaces in (128, 64, 32, 16, 8, 4, 2, 1): for spaces in (128, 64, 32, 16, 8, 4, 2, 1):
padded = True padded = True
@ -176,10 +177,10 @@ class Backend():
self.message = '\n'.join(lines) self.message = '\n'.join(lines)
'''
Converts all tabs in the message to spaces by expanding
'''
def __expandMessage(self): def __expandMessage(self):
'''
Converts all tabs in the message to spaces by expanding
'''
lines = self.message.split('\n') lines = self.message.split('\n')
buf = '' buf = ''
for line in lines: for line in lines:
@ -203,18 +204,18 @@ class Backend():
self.message = buf[:-1] self.message = buf[:-1]
'''
Loads the pony file
'''
def __loadFile(self): def __loadFile(self):
'''
Loads the pony file
'''
with open(self.ponyfile, 'rb') as ponystream: with open(self.ponyfile, 'rb') as ponystream:
self.pony = ponystream.read().decode('utf8', 'replace') self.pony = ponystream.read().decode('utf8', 'replace')
'''
Truncate output to the width of the screen
'''
def __truncate(self): def __truncate(self):
'''
Truncate output to the width of the screen
'''
if self.width is None: if self.width is None:
return return
lines = self.output.split('\n') lines = self.output.split('\n')
@ -237,10 +238,10 @@ class Backend():
self.output = self.output[:-1] self.output = self.output[:-1]
'''
Process the pony file and generate output to self.output
'''
def __processPony(self): def __processPony(self):
'''
Process the pony file and generate output to self.output
'''
self.output = '' self.output = ''
AUTO_PUSH = '\033[01010~' AUTO_PUSH = '\033[01010~'
@ -307,7 +308,7 @@ class Backend():
w -= x; w -= x;
else: else:
w = int(w) w = int(w)
balloon = self.__getballoon(w, h, x, justify, indent) balloon = self.__getBalloon(w, h, x, justify, indent)
balloon = balloon.split('\n') balloon = balloon.split('\n')
balloon = [AUTO_PUSH + self.ballooncolour + item + AUTO_POP for item in balloon] balloon = [AUTO_PUSH + self.ballooncolour + item + AUTO_POP for item in balloon]
for b in balloon[0]: for b in balloon[0]:
@ -383,15 +384,15 @@ class Backend():
self.output = '\n'.join(self.output) self.output = '\n'.join(self.output)
'''
Gets colour code att the currect offset in a buffer
@param input:str The input buffer
@param offset:int The offset at where to start reading, a escape must begin here
@return :str The escape sequence
'''
@staticmethod @staticmethod
def getColour(input, offset): def getColour(input, offset):
'''
Gets colour code att the currect offset in a buffer
@param input:str The input buffer
@param offset:int The offset at where to start reading, a escape must begin here
@return :str The escape sequence
'''
(i, n) = (offset, len(input)) (i, n) = (offset, len(input))
rc = input[i] rc = input[i]
i += 1 i += 1
@ -439,14 +440,14 @@ class Backend():
return rc return rc
'''
Calculates the number of visible characters in a text
@param input:str The input buffer
@return :int The number of visible characters
'''
@staticmethod @staticmethod
def len(input): def len(input):
'''
Calculates the number of visible characters in a text
@param input:str The input buffer
@return :int The number of visible characters
'''
(rc, i, n) = (0, 0, len(input)) (rc, i, n) = (0, 0, len(input))
while i < n: while i < n:
c = input[i] c = input[i]
@ -459,18 +460,18 @@ class Backend():
return rc return rc
''' def __getBalloon(self, width, height, innerleft, justify, left):
Generates a balloon with the message '''
Generates a balloon with the message
@param width:int The minimum width of the balloon
@param height:int The minimum height of the balloon @param width:int The minimum width of the balloon
@param innerleft:int The left column of the required span, excluding that of `left` @param height:int The minimum height of the balloon
@param justify:str Balloon placement justification, 'c' centered, @param innerleft:int The left column of the required span, excluding that of `left`
'l' left (expand to right), 'r' right (expand to left) @param justify:str Balloon placement justification, 'c' centered,
@param left:int The column where the balloon starts 'l' left (expand to right), 'r' right (expand to left)
@return :str The balloon the the message as a string @param left:int The column where the balloon starts
''' @return :str The balloon the the message as a string
def __getballoon(self, width, height, innerleft, justify, left): '''
wrap = None wrap = None
if self.wrapcolumn is not None: if self.wrapcolumn is not None:
wrap = self.wrapcolumn - left wrap = self.wrapcolumn - left
@ -508,14 +509,14 @@ class Backend():
return rc return rc
'''
Wraps the message
@param message:str The message to wrap
@param wrap:int The width at where to force wrapping
@return :str The message wrapped
'''
def __wrapMessage(self, message, wrap): def __wrapMessage(self, message, wrap):
'''
Wraps the message
@param message:str The message to wrap
@param wrap:int The width at where to force wrapping
@return :str The message wrapped
'''
wraplimit = os.environ['PONYSAY_WRAP_LIMIT'] if 'PONYSAY_WRAP_LIMIT' in os.environ else '' wraplimit = os.environ['PONYSAY_WRAP_LIMIT'] if 'PONYSAY_WRAP_LIMIT' in os.environ else ''
wraplimit = 8 if len(wraplimit) == 0 else int(wraplimit) wraplimit = 8 if len(wraplimit) == 0 else int(wraplimit)
@ -579,11 +580,11 @@ class Backend():
nbsp = b[map[mm + x]] == ' ' # nbsp nbsp = b[map[mm + x]] == ' ' # nbsp
m = map[mm + x] m = map[mm + x]
if ('­' in b[bisub : m]) and not nbsp: # sort hyphen if ('­' in b[bisub : m]) and not nbsp: # soft hyphen
hyphen = m - 1 hyphen = m - 1
while b[hyphen] != '­': # sort hyphen while b[hyphen] != '­': # soft hyphen
hyphen -= 1 hyphen -= 1
while map[mm + x] > hyphen: ## Only looking backward, if foreward is required the word is probabily not hyphenated correctly while map[mm + x] > hyphen: ## Only looking backward, if forward is required the word is probabily not hyphenated correctly
x -= 1 x -= 1
x += 1 x += 1
m = map[mm + x] m = map[mm + x]

View file

@ -34,36 +34,37 @@ from ucs import *
'''
Balloon format class
'''
class Balloon(): class Balloon():
''' '''
Constructor Balloon format class
@param link:str The \-directional balloon line character
@param linkmirror:str The /-directional balloon line character
@param linkcross:str The /-directional balloon crossing a \-directional ballonon line character
@param ww:str See the info manual
@param ee:str See the info manual
@param nw:list<str> See the info manual
@param nnw:list<str> See the info manual
@param n:list<str> See the info manual
@param nne:list<str> See the info manual
@param ne:list<str> See the info manual
@param nee:str See the info manual
@param e:str See the info manual
@param see:str See the info manual
@param se:list<str> See the info manual
@param sse:list<str> See the info manual
@param s:list<str> See the info manual
@param ssw:list<str> See the info manual
@param sw:list<str> See the info manual
@param sww:str See the info manual
@param w:str See the info manual
@param nww:str See the info manual
''' '''
def __init__(self, link, linkmirror, linkcross, ww, ee, nw, nnw, n, nne, ne, nee, e, see, se, sse, s, ssw, sw, sww, w, nww): def __init__(self, link, linkmirror, linkcross, ww, ee, nw, nnw, n, nne, ne, nee, e, see, se, sse, s, ssw, sw, sww, w, nww):
'''
Constructor
@param link:str The \-directional balloon line character
@param linkmirror:str The /-directional balloon line character
@param linkcross:str The /-directional balloon crossing a \-directional ballonon line character
@param ww:str See the info manual
@param ee:str See the info manual
@param nw:list<str> See the info manual
@param nnw:list<str> See the info manual
@param n:list<str> See the info manual
@param nne:list<str> See the info manual
@param ne:list<str> See the info manual
@param nee:str See the info manual
@param e:str See the info manual
@param see:str See the info manual
@param se:list<str> See the info manual
@param sse:list<str> See the info manual
@param s:list<str> See the info manual
@param ssw:list<str> See the info manual
@param sw:list<str> See the info manual
@param sww:str See the info manual
@param w:str See the info manual
@param nww:str See the info manual
'''
(self.link, self.linkmirror, self.linkcross) = (link, linkmirror, linkcross) (self.link, self.linkmirror, self.linkcross) = (link, linkmirror, linkcross)
(self.ww, self.ee) = (ww, ee) (self.ww, self.ee) = (ww, ee)
(self.nw, self.ne, self.se, self.sw) = (nw, ne, se, sw) (self.nw, self.ne, self.se, self.sw) = (nw, ne, se, sw)
@ -86,16 +87,16 @@ class Balloon():
self.minheight = minN + minS self.minheight = minN + minS
'''
Generates a balloon with a message
@param minw:int The minimum number of columns of the balloon
@param minh:int The minimum number of lines of the balloon
@param lines:list<str> The text lines to display
@param lencalc:int(str) Function used to compute the length of a text line
@return :str The balloon as a formated string
'''
def get(self, minw, minh, lines, lencalc): def get(self, minw, minh, lines, lencalc):
'''
Generates a balloon with a message
@param minw:int The minimum number of columns of the balloon
@param minh:int The minimum number of lines of the balloon
@param lines:list<str> The text lines to display
@param lencalc:int(str) Function used to compute the length of a text line
@return :str The balloon as a formated string
'''
## Get dimension ## Get dimension
h = self.minheight + len(lines) h = self.minheight + len(lines)
w = self.minwidth + lencalc(max(lines, key = lencalc)) w = self.minwidth + lencalc(max(lines, key = lencalc))
@ -138,15 +139,15 @@ class Balloon():
return '\n'.join(rc) return '\n'.join(rc)
'''
Creates the balloon style object
@param balloonfile:str The file with the balloon style, may be `None`
@param isthink:bool Whether the ponythink command is used
@return :Balloon Instance describing the balloon's style
'''
@staticmethod @staticmethod
def fromFile(balloonfile, isthink): def fromFile(balloonfile, isthink):
'''
Creates the balloon style object
@param balloonfile:str The file with the balloon style, may be `None`
@param isthink:bool Whether the ponythink command is used
@return :Balloon Instance describing the balloon's style
'''
## Use default balloon if none is specified ## Use default balloon if none is specified
if balloonfile is None: if balloonfile is None:
if isthink: if isthink:

View file

@ -33,19 +33,20 @@ from common import *
'''
ANSI colour stack
This is used to make layers with independent coloursations
'''
class ColourStack(): class ColourStack():
''' '''
Constructor ANSI colour stack
@param autopush:str String that, when used, will create a new independently colourised layer This is used to make layers with independent coloursations
@param autopop:str String that, when used, will end the current layer and continue of the previous layer
''' '''
def __init__(self, autopush, autopop): def __init__(self, autopush, autopop):
'''
Constructor
@param autopush:str String that, when used, will create a new independently colourised layer
@param autopop:str String that, when used, will end the current layer and continue of the previous layer
'''
self.autopush = autopush self.autopush = autopush
self.autopop = autopop self.autopop = autopop
self.lenpush = len(autopush) self.lenpush = len(autopush)
@ -56,24 +57,24 @@ class ColourStack():
self.seq = None self.seq = None
'''
Create a new independently colourised layer
@return :str String that should be inserted into your buffer
'''
def push(self): def push(self):
'''
Create a new independently colourised layer
@return :str String that should be inserted into your buffer
'''
self.stack.insert(0, [self.bufproto, None, None, [False] * 9]) self.stack.insert(0, [self.bufproto, None, None, [False] * 9])
if len(self.stack) == 1: if len(self.stack) == 1:
return None return None
return '\033[0m' return '\033[0m'
'''
End the current layer and continue of the previous layer
@return :str String that should be inserted into your buffer
'''
def pop(self): def pop(self):
'''
End the current layer and continue of the previous layer
@return :str String that should be inserted into your buffer
'''
old = self.stack.pop(0) old = self.stack.pop(0)
rc = '\033[0;' rc = '\033[0;'
if len(self.stack) == 0: # last resort in case something made it pop too mush if len(self.stack) == 0: # last resort in case something made it pop too mush
@ -87,14 +88,14 @@ class ColourStack():
return rc[:-1] + 'm' return rc[:-1] + 'm'
'''
Use this, in sequence, for which character in your buffer that contains yor autopush and autopop
string, the automatically get push and pop string to insert after each character
@param :chr One character in your buffer
@return :str The text to insert after the input character
'''
def feed(self, char): def feed(self, char):
'''
Use this, in sequence, for which character in your buffer that contains yor autopush and autopop
string, the automatically get push and pop string to insert after each character
@param :chr One character in your buffer
@return :str The text to insert after the input character
'''
if self.seq is not None: if self.seq is not None:
self.seq += char self.seq += char
if (char == '~') or (('a' <= char) and (char <= 'z')) or (('A' <= char) and (char <= 'Z')): if (char == '~') or (('a' <= char) and (char <= 'z')) or (('A' <= char) and (char <= 'Z')):

View file

@ -38,40 +38,40 @@ from subprocess import Popen, PIPE
VERSION = 'dev' # this line should not be edited, it is fixed by the build system
''' '''
The version of ponysay The version of ponysay
''' '''
VERSION = 'dev' # this line should not be edited, it is fixed by the build system
'''
Hack to enforce UTF-8 in output (in the future, if you see anypony not using utf-8 in
programs by default, report them to Princess Celestia so she can banish them to the moon)
@param text:str The text to print (empty string is default)
@param end:str The appendix to the text to print (line breaking is default)
'''
def print(text = '', end = '\n'): def print(text = '', end = '\n'):
'''
Hack to enforce UTF-8 in output (in the future, if you see anypony not using utf-8 in
programs by default, report them to Princess Celestia so she can banish them to the moon)
@param text:str The text to print (empty string is default)
@param end:str The appendix to the text to print (line breaking is default)
'''
sys.stdout.buffer.write((str(text) + end).encode('utf-8')) sys.stdout.buffer.write((str(text) + end).encode('utf-8'))
'''
stderr equivalent to print()
@param text:str The text to print (empty string is default)
@param end:str The appendix to the text to print (line breaking is default)
'''
def printerr(text = '', end = '\n'): def printerr(text = '', end = '\n'):
'''
stderr equivalent to print()
@param text:str The text to print (empty string is default)
@param end:str The appendix to the text to print (line breaking is default)
'''
sys.stderr.buffer.write((str(text) + end).encode('utf-8')) sys.stderr.buffer.write((str(text) + end).encode('utf-8'))
fd3 = None fd3 = None
'''
/proc/self/fd/3 equivalent to print()
@param text:str The text to print (empty string is default)
@param end:str The appendix to the text to print (line breaking is default)
'''
def printinfo(text = '', end = '\n'): def printinfo(text = '', end = '\n'):
'''
/proc/self/fd/3 equivalent to print()
@param text:str The text to print (empty string is default)
@param end:str The appendix to the text to print (line breaking is default)
'''
global fd3 global fd3
if os.path.exists('/proc/self/fd/3') and not os.path.isdir(os.path.realpath('/proc/self/fd/3')): if os.path.exists('/proc/self/fd/3') and not os.path.isdir(os.path.realpath('/proc/self/fd/3')):
if fd3 is None: if fd3 is None:
@ -80,23 +80,23 @@ def printinfo(text = '', end = '\n'):
fd3.write(str(text) + end) fd3.write(str(text) + end)
'''
Checks whether a text ends with a specific text, but has more
@param text:str The text to test
@param ending:str The desired end of the text
@return :bool The result of the test
'''
def endswith(text, ending): def endswith(text, ending):
'''
Checks whether a text ends with a specific text, but has more
@param text:str The text to test
@param ending:str The desired end of the text
@return :bool The result of the test
'''
return text.endswith(ending) and not (text == ending) return text.endswith(ending) and not (text == ending)
'''
Gets the size of the terminal in (rows, columns)
@return (rows, columns):(int, int) The number or lines and the number of columns in the terminal's display area
'''
def gettermsize(): def gettermsize():
'''
Gets the size of the terminal in (rows, columns)
@return (rows, columns):(int, int) The number or lines and the number of columns in the terminal's display area
'''
## Call `stty` to determine the size of the terminal, this way is better than using python's ncurses ## Call `stty` to determine the size of the terminal, this way is better than using python's ncurses
for channel in (sys.stderr, sys.stdout, sys.stdin): for channel in (sys.stderr, sys.stdout, sys.stdin):
termsize = Popen(['stty', 'size'], stdout=PIPE, stdin=channel, stderr=PIPE).communicate()[0] termsize = Popen(['stty', 'size'], stdout=PIPE, stdin=channel, stderr=PIPE).communicate()[0]

View file

@ -33,18 +33,19 @@ from common import *
'''
KMS support utilisation
'''
class KMS(): class KMS():
''' '''
Identifies whether KMS support is utilised KMS support utilisation
@param linuxvt:bool Whether Linux VT is used
@return :bool Whether KMS support is utilised
''' '''
@staticmethod @staticmethod
def usingkms(linuxvt): def usingkms(linuxvt):
'''
Identifies whether KMS support is utilised
@param linuxvt:bool Whether Linux VT is used
@return :bool Whether KMS support is utilised
'''
## KMS is not utilised if Linux VT is not used ## KMS is not utilised if Linux VT is not used
if not linuxvt: if not linuxvt:
return False return False
@ -64,16 +65,16 @@ class KMS():
return env_kms != '' return env_kms != ''
'''
Returns the file name of the input pony converted to a KMS pony, or if KMS is not used, the input pony itself
@param pony:str Choosen pony file
@param home:str The home directory
@param linuxvt:bool Whether Linux VT is used
@return :str Pony file to display
'''
@staticmethod @staticmethod
def kms(pony, home, linuxvt): def kms(pony, home, linuxvt):
'''
Returns the file name of the input pony converted to a KMS pony, or if KMS is not used, the input pony itself
@param pony:str Choosen pony file
@param home:str The home directory
@param linuxvt:bool Whether Linux VT is used
@return :str Pony file to display
'''
## If not in Linux VT, return the pony as is ## If not in Linux VT, return the pony as is
if not linuxvt: if not linuxvt:
return pony return pony

View file

@ -34,17 +34,18 @@ from ucs import *
'''
File listing functions
'''
class List(): class List():
''' '''
Columnise a list and prints it File listing functions
@param ponies:list<(str, str)> All items to list, each item should have to elements: unformated name, formated name
''' '''
@staticmethod @staticmethod
def __columnise(ponies): def __columnise(ponies):
'''
Columnise a list and prints it
@param ponies:list<(str, str)> All items to list, each item should have to elements: unformated name, formated name
'''
## Get terminal width, and a 2 which is the space between columns ## Get terminal width, and a 2 which is the space between columns
termwidth = gettermsize()[1] + 2 termwidth = gettermsize()[1] + 2
## Sort the ponies, and get the cells' widths, and the largest width + 2 ## Sort the ponies, and get the cells' widths, and the largest width + 2
@ -90,17 +91,17 @@ class List():
## Print the matrix, with one extra blank row ## Print the matrix, with one extra blank row
print('\n'.join([''.join(line)[:-2] for line in lines])) print('\n'.join([''.join(line)[:-2] for line in lines]))
print() print()
'''
Lists the available ponies
@param ponydirs:itr<str> The pony directories to use
@param quoters:__in__(str)bool Set of ponies that of quotes
@param ucsiser:(list<str>)?void Function used to UCS:ise names
'''
@staticmethod @staticmethod
def simplelist(ponydirs, quoters = [], ucsiser = None): def simplelist(ponydirs, quoters = [], ucsiser = None):
'''
Lists the available ponies
@param ponydirs:itr<str> The pony directories to use
@param quoters:__in__(str)bool Set of ponies that of quotes
@param ucsiser:(list<str>)?void Function used to UCS:ise names
'''
for ponydir in ponydirs: # Loop ponydirs for ponydir in ponydirs: # Loop ponydirs
## Get all ponies in the directory ## Get all ponies in the directory
_ponies = os.listdir(ponydir) _ponies = os.listdir(ponydir)
@ -122,15 +123,15 @@ class List():
List.__columnise([(pony, '\033[1m' + pony + '\033[21m' if pony in quoters else pony) for pony in ponies]) List.__columnise([(pony, '\033[1m' + pony + '\033[21m' if pony in quoters else pony) for pony in ponies])
'''
Lists the available ponies with alternatives inside brackets
@param ponydirs:itr<str> The pony directories to use
@param quoters:__in__(str)bool Set of ponies that of quotes
@param ucsiser:(list<str>, map<str, str>)?void Function used to UCS:ise names
'''
@staticmethod @staticmethod
def linklist(ponydirs = None, quoters = [], ucsiser = None): def linklist(ponydirs = None, quoters = [], ucsiser = None):
'''
Lists the available ponies with alternatives inside brackets
@param ponydirs:itr<str> The pony directories to use
@param quoters:__in__(str)bool Set of ponies that of quotes
@param ucsiser:(list<str>, map<str, str>)?void Function used to UCS:ise names
'''
## Get the size of the terminal ## Get the size of the terminal
termsize = gettermsize() termsize = gettermsize()
@ -200,15 +201,15 @@ class List():
List.__columnise(list(ponies)) List.__columnise(list(ponies))
'''
Lists the available ponies on one column without anything bold or otherwise formated
@param standard:itr<str>? Include standard ponies
@param extra:itr<str>? Include extra ponies
@param ucsiser:(list<str>)?void Function used to UCS:ise names
'''
@staticmethod @staticmethod
def onelist(standarddirs, extradirs = None, ucsiser = None): def onelist(standarddirs, extradirs = None, ucsiser = None):
'''
Lists the available ponies on one column without anything bold or otherwise formated
@param standard:itr<str>? Include standard ponies
@param extra:itr<str>? Include extra ponies
@param ucsiser:(list<str>)?void Function used to UCS:ise names
'''
## Get all pony files ## Get all pony files
_ponies = [] _ponies = []
if standarddirs is not None: if standarddirs is not None:
@ -237,14 +238,14 @@ class List():
print(pony) print(pony)
'''
Prints a list of all balloons
@param balloondirs:itr<str> The balloon directories to use
@param isthink:bool Whether the ponythink command is used
'''
@staticmethod @staticmethod
def balloonlist(balloondirs, isthink): def balloonlist(balloondirs, isthink):
'''
Prints a list of all balloons
@param balloondirs:itr<str> The balloon directories to use
@param isthink:bool Whether the ponythink command is used
'''
## Get the size of the terminal ## Get the size of the terminal
termsize = gettermsize() termsize = gettermsize()

View file

@ -33,18 +33,19 @@ from common import *
'''
Metadata functions
'''
class Metadata(): class Metadata():
''' '''
Make restriction test logic function Metadata functions
@param restriction:list<string> Metadata based restrictions
@return :dict<str, str>bool Test function
''' '''
@staticmethod @staticmethod
def makeRestrictionLogic(restriction): def makeRestrictionLogic(restriction):
'''
Make restriction test logic function
@param restriction:list<string> Metadata based restrictions
@return :dict<str, str>bool Test function
'''
def get_test(cell): def get_test(cell):
strict = cell[0][-1] != '?' strict = cell[0][-1] != '?'
key = cell[0] key = cell[0]
@ -109,15 +110,15 @@ class Metadata():
return Logic(table) return Logic(table)
'''
Get ponies that pass restriction
@param ponydir:str Pony directory, must end with `os.sep`
@param logic:(str)bool Restriction test functor
@return :list<str> Passed ponies
'''
@staticmethod @staticmethod
def restrictedPonies(ponydir, logic): def restrictedPonies(ponydir, logic):
'''
Get ponies that pass restriction
@param ponydir:str Pony directory, must end with `os.sep`
@param logic:(str)bool Restriction test functor
@return :list<str> Passed ponies
'''
import pickle import pickle
passed = [] passed = []
if os.path.exists(ponydir + 'metadata'): if os.path.exists(ponydir + 'metadata'):
@ -131,15 +132,15 @@ class Metadata():
return passed return passed
'''
Get ponies that fit the terminal
@param fitting:add(str)void The set to fill
@param requirement:int The maximum allowed value
@param file:istream The file with all data
'''
@staticmethod @staticmethod
def getfitting(fitting, requirement, file): def getfitting(fitting, requirement, file):
'''
Get ponies that fit the terminal
@param fitting:add(str)void The set to fill
@param requirement:int The maximum allowed value
@param file:istream The file with all data
'''
data = file.read() # not too much data, can load everything at once data = file.read() # not too much data, can load everything at once
ptr = 0 ptr = 0
while data[ptr] != 47: # 47 == ord('/') while data[ptr] != 47: # 47 == ord('/')

View file

@ -3,13 +3,31 @@
''' '''
ponysay - Ponysay, cowsay reimplementation for ponies ponysay - Ponysay, cowsay reimplementation for ponies
Copyright (C) 2012, 2013 Erkin Batu Altunbaş et al. Copyright (C) 2012, 2013 Erkin Batu Altunbaş et al.
This program is free software. It comes without any warranty, to
the extent permitted by applicable law. You can redistribute it This program is free software: you can redistribute it and/or modify
and/or modify it under the terms of the Do What The Fuck You Want it under the terms of the GNU General Public License as published by
To Public License, Version 2, as published by Sam Hocevar. See the Free Software Foundation, either version 3 of the License, or
http://sam.zoy.org/wtfpl/COPYING for more details. (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
If you intend to redistribute ponysay or a fork of it commercially,
it contains aggregated images, some of which may not be commercially
redistribute, you would be required to remove those. To determine
whether or not you may commercially redistribute an image make use
that line FREE: yes, is included inside the image between two $$$
lines and the FREE is and upper case and directly followed by
the colon.
''' '''
from common import * from common import *
from backend import * from backend import *
@ -22,29 +40,29 @@ from metadata import *
'''
This is the mane class of ponysay
'''
class Ponysay(): class Ponysay():
''' '''
Constructor This is the mane class of ponysay
''' '''
def __init__(self): def __init__(self):
''' '''
The user's home directory Constructor
''' '''
# The user's home directory
self.HOME = os.environ['HOME'] if 'HOME' in os.environ else '' self.HOME = os.environ['HOME'] if 'HOME' in os.environ else ''
if len(self.HOME) == 0: if len(self.HOME) == 0:
os.environ['HOME'] = self.HOME = os.path.expanduser('~') os.environ['HOME'] = self.HOME = os.path.expanduser('~')
'''
Parse a file name encoded with environment variables
@param file The encoded file name
@return The target file name, None if the environment variables are not declared
'''
def parsefile(file): def parsefile(file):
'''
Parse a file name encoded with environment variables
@param file The encoded file name
@return The target file name, None if the environment variables are not declared
'''
if '$' in file: if '$' in file:
buf = '' buf = ''
esc = False esc = False
@ -88,21 +106,15 @@ class Ponysay():
os.environ['HOME'] = self.HOME = os.path.expanduser('~') os.environ['HOME'] = self.HOME = os.path.expanduser('~')
''' # Whether any unrecognised options was parsed, this should be set by the invoker before run()
Whether any unrecognised options was parsed, this should be set by the invoker before run()
'''
self.unrecognised = False self.unrecognised = False
''' # Whether the program is execute in Linux VT (TTY)
Whether the program is execute in Linux VT (TTY)
'''
self.linuxvt = ('TERM' in os.environ) and (os.environ['TERM'] == 'linux') self.linuxvt = ('TERM' in os.environ) and (os.environ['TERM'] == 'linux')
''' # Whether the script is executed as ponythink
Whether the script is executed as ponythink
'''
self.isthink = sys.argv[0] self.isthink = sys.argv[0]
if os.sep in self.isthink: if os.sep in self.isthink:
self.isthink = self.isthink[self.isthink.rfind(os.sep) + 1:] self.isthink = self.isthink[self.isthink.rfind(os.sep) + 1:]
@ -111,31 +123,21 @@ class Ponysay():
self.isthink = self.isthink.endswith('think') self.isthink = self.isthink.endswith('think')
''' # Whether stdin is piped
Whether stdin is piped
'''
self.pipelinein = not sys.stdin.isatty() self.pipelinein = not sys.stdin.isatty()
''' # Whether stdout is piped
Whether stdout is piped
'''
self.pipelineout = not sys.stdout.isatty() self.pipelineout = not sys.stdout.isatty()
''' # Whether stderr is piped
Whether stderr is piped
'''
self.pipelineerr = not sys.stderr.isatty() self.pipelineerr = not sys.stderr.isatty()
''' # Whether KMS is used
Whether KMS is used
'''
self.usekms = KMS.usingkms(self.linuxvt) self.usekms = KMS.usingkms(self.linuxvt)
''' # Mode string that modifies or adds $ variables in the pony image
Mode string that modifies or adds $ variables in the pony image
'''
self.mode = '' self.mode = ''
@ -152,9 +154,7 @@ class Ponysay():
]] ]]
''' # The directories where pony files are stored, ttyponies/ are used if the terminal is Linux VT (also known as TTY) and not with KMS
The directories where pony files are stored, ttyponies/ are used if the terminal is Linux VT (also known as TTY) and not with KMS
'''
appendset = set() appendset = set()
self.xponydirs = [] self.xponydirs = []
_ponydirs = share('ponies/') _ponydirs = share('ponies/')
@ -171,9 +171,7 @@ class Ponysay():
appendset.add(ponydir) appendset.add(ponydir)
''' # The directories where pony files are stored, extrattyponies/ are used if the terminal is Linux VT (also known as TTY) and not with KMS
The directories where pony files are stored, extrattyponies/ are used if the terminal is Linux VT (also known as TTY) and not with KMS
'''
appendset = set() appendset = set()
self.extraxponydirs = [] self.extraxponydirs = []
_extraponydirs = share('extraponies/') _extraponydirs = share('extraponies/')
@ -190,9 +188,7 @@ class Ponysay():
appendset.add(extraponydir) appendset.add(extraponydir)
''' # The directories where quotes files are stored
The directories where quotes files are stored
'''
appendset = set() appendset = set()
self.quotedirs = [] self.quotedirs = []
_quotedirs = share('quotes/') _quotedirs = share('quotes/')
@ -202,9 +198,7 @@ class Ponysay():
appendset.add(quotedir) appendset.add(quotedir)
''' # The directories where balloon style files are stored
The directories where balloon style files are stored
'''
appendset = set() appendset = set()
self.balloondirs = [] self.balloondirs = []
_balloondirs = share('balloons/') _balloondirs = share('balloons/')
@ -214,9 +208,7 @@ class Ponysay():
appendset.add(balloondir) appendset.add(balloondir)
''' # ucsmap files
ucsmap files
'''
appendset = set() appendset = set()
self.ucsmaps = [] self.ucsmaps = []
_ucsmaps = share('ucsmap/') _ucsmaps = share('ucsmap/')
@ -227,24 +219,24 @@ class Ponysay():
'''
Starts the part of the program the arguments indicate
@param args:ArgParser Parsed command line arguments
'''
def run(self, args): def run(self, args):
'''
Starts the part of the program the arguments indicate
@param args:ArgParser Parsed command line arguments
'''
if (args.argcount == 0) and not self.pipelinein: if (args.argcount == 0) and not self.pipelinein:
args.help() args.help()
exit(254) exit(254)
return return
'''
Test arguments written in negation-free disjunctive normal form
@param keys:*str|itr<str> A list of keys and set of keys, any of which must exists, a set of keys only passes if all of those exists
@return :bool Whether the check passed
'''
def test(*keys): def test(*keys):
'''
Test arguments written in negation-free disjunctive normal form
@param keys:*str|itr<str> A list of keys and set of keys, any of which must exists, a set of keys only passes if all of those exists
@return :bool Whether the check passed
'''
for key in keys: for key in keys:
if isinstance(key, str): if isinstance(key, str):
if args.opts[key] is not None: if args.opts[key] is not None:
@ -328,21 +320,21 @@ class Ponysay():
## Methods that run before the mane methods ## ## Methods that run before the mane methods ##
############################################## ##############################################
'''
Use extra ponies
'''
def __extraponies(self): def __extraponies(self):
'''
Use extra ponies
'''
## Change ponydir to extraponydir ## Change ponydir to extraponydir
self.ponydirs[:] = self.extraponydirs self.ponydirs[:] = self.extraponydirs
self.quotedirs[:] = [] ## TODO +q self.quotedirs[:] = [] ## TODO +q
'''
Use best.pony if nothing else is set
@param args:ArgParser Parsed command line arguments
'''
def __bestpony(self, args): def __bestpony(self, args):
'''
Use best.pony if nothing else is set
@param args:ArgParser Parsed command line arguments
'''
## Set best.pony as the pony to display if none is selected ## Set best.pony as the pony to display if none is selected
def test(keys, strict): def test(keys, strict):
if strict: if strict:
@ -368,12 +360,12 @@ class Ponysay():
break break
'''
Apply pony name remapping to args according to UCS settings
@param args:ArgParser Parsed command line arguments
'''
def __ucsremap(self, args): def __ucsremap(self, args):
'''
Apply pony name remapping to args according to UCS settings
@param args:ArgParser Parsed command line arguments
'''
## Read UCS configurations ## Read UCS configurations
env_ucs = os.environ['PONYSAY_UCS_ME'] if 'PONYSAY_UCS_ME' in os.environ else '' env_ucs = os.environ['PONYSAY_UCS_ME'] if 'PONYSAY_UCS_ME' in os.environ else ''
ucs_conf = 0 ucs_conf = 0
@ -413,13 +405,13 @@ class Ponysay():
## Auxiliary methods ## ## Auxiliary methods ##
####################### #######################
'''
Apply UCS:ise pony names according to UCS settings
@param ponies:list<str> List of all ponies (of interrest)
@param links:map<str, str>? Map to fill with simulated symlink ponies, may be `None`
'''
def __ucsise(self, ponies, links = None): def __ucsise(self, ponies, links = None):
'''
Apply UCS:ise pony names according to UCS settings
@param ponies:list<str> List of all ponies (of interrest)
@param links:map<str, str>? Map to fill with simulated symlink ponies, may be `None`
'''
## Read UCS configurations ## Read UCS configurations
env_ucs = os.environ['PONYSAY_UCS_ME'] if 'PONYSAY_UCS_ME' in os.environ else '' env_ucs = os.environ['PONYSAY_UCS_ME'] if 'PONYSAY_UCS_ME' in os.environ else ''
ucs_conf = 0 ucs_conf = 0
@ -460,18 +452,18 @@ class Ponysay():
ponies[j] = map[ponies[j]] ponies[j] = map[ponies[j]]
'''
Returns one file with full path and ponyquote that should be used, names is filter for names, also accepts filepaths
@param selection:(name:str, dirs:itr<str>, quote:bool)? Parsed command line arguments as namedirectoriesquoting tubles:
name: The pony name
dirfiles: Files, with the directory, in the pony directories
quote: Whether to use ponyquotes
@param args:ArgParser Parsed command line arguments
@param alt:bool For method internal use...
@return (path, quote):(str, str?) The file name of a pony, and the ponyquote that should be used if any
'''
def __getpony(self, selection, args, alt = False): def __getpony(self, selection, args, alt = False):
'''
Returns one file with full path and ponyquote that should be used, names is filter for names, also accepts filepaths
@param selection:(name:str, dirs:itr<str>, quote:bool)? Parsed command line arguments as namedirectoriesquoting tubles:
name: The pony name
dirfiles: Files, with the directory, in the pony directories
quote: Whether to use ponyquotes
@param args:ArgParser Parsed command line arguments
@param alt:bool For method internal use...
@return (path, quote):(str, str?) The file name of a pony, and the ponyquote that should be used if any
'''
## If there is no selected ponies, choose all of them ## If there is no selected ponies, choose all of them
if (selection is None) or (len(selection) == 0): if (selection is None) or (len(selection) == 0):
quote = args.opts['-q'] is not None ## TODO +q -Q quote = args.opts['-q'] is not None ## TODO +q -Q
@ -570,14 +562,14 @@ class Ponysay():
return (file, self.__getquote(pony[0], file) if pony[2] else None) return (file, self.__getquote(pony[0], file) if pony[2] else None)
'''
Select a quote for a pony
@param pony:str The pony name
@param file:str The pony's file name
@return :str A quote from the pony, with a failure fall back message
'''
def __getquote(self, pony, file): def __getquote(self, pony, file):
'''
Select a quote for a pony
@param pony:str The pony name
@param file:str The pony's file name
@return :str A quote from the pony, with a failure fall back message
'''
quote = [] quote = []
if (os.path.dirname(file) + os.sep).replace(os.sep + os.sep, os.sep) in self.ponydirs: if (os.path.dirname(file) + os.sep).replace(os.sep + os.sep, os.sep) in self.ponydirs:
realpony = pony realpony = pony
@ -596,14 +588,14 @@ class Ponysay():
return quote return quote
'''
Returns a set with all ponies that have quotes and are displayable
@param ponydirs:itr<str>? The pony directories to use
@param quotedirs:itr<str>? The quote directories to use
@return :set<str> All ponies that have quotes and are displayable
'''
def __quoters(self, ponydirs = None, quotedirs = None): def __quoters(self, ponydirs = None, quotedirs = None):
'''
Returns a set with all ponies that have quotes and are displayable
@param ponydirs:itr<str>? The pony directories to use
@param quotedirs:itr<str>? The quote directories to use
@return :set<str> All ponies that have quotes and are displayable
'''
if ponydirs is None: ponydirs = self.ponydirs if ponydirs is None: ponydirs = self.ponydirs
if quotedirs is None: quotedirs = self.quotedirs if quotedirs is None: quotedirs = self.quotedirs
@ -633,15 +625,15 @@ class Ponysay():
return ponies return ponies
'''
Returns a list with all (pony, quote file) pairs
@param ponydirs:itr<str>? The pony directories to use
@param quotedirs:itr<str>? The quote directories to use
@param ponies:itr<str>? The ponies to use
@return (pony, quote):(str, str) All poniesquote file-pairs
'''
def __quotes(self, ponydirs = None, quotedirs = None, ponies = None): def __quotes(self, ponydirs = None, quotedirs = None, ponies = None):
'''
Returns a list with all (pony, quote file) pairs
@param ponydirs:itr<str>? The pony directories to use
@param quotedirs:itr<str>? The quote directories to use
@param ponies:itr<str>? The ponies to use
@return (pony, quote):(str, str) All poniesquote file-pairs
'''
if ponydirs is None: ponydirs = self.ponydirs if ponydirs is None: ponydirs = self.ponydirs
if quotedirs is None: quotedirs = self.quotedirs if quotedirs is None: quotedirs = self.quotedirs
@ -678,45 +670,45 @@ class Ponysay():
## Listing methods ## ## Listing methods ##
##################### #####################
'''
Lists the available ponies
@param ponydirs:itr<str>? The pony directories to use
'''
def list(self, ponydirs = None): def list(self, ponydirs = None):
'''
Lists the available ponies
@param ponydirs:itr<str>? The pony directories to use
'''
List.simplelist(self.ponydirs if ponydirs is None else ponydirs, List.simplelist(self.ponydirs if ponydirs is None else ponydirs,
self.__quoters(), lambda x : self.__ucsise(x)) self.__quoters(), lambda x : self.__ucsise(x))
'''
Lists the available ponies with alternatives inside brackets
@param ponydirs:itr<str> The pony directories to use
'''
def linklist(self, ponydirs = None): def linklist(self, ponydirs = None):
'''
Lists the available ponies with alternatives inside brackets
@param ponydirs:itr<str> The pony directories to use
'''
List.linklist(self.ponydirs if ponydirs is None else ponydirs, List.linklist(self.ponydirs if ponydirs is None else ponydirs,
self.__quoters(), lambda x, y : self.__ucsise(x, y)) self.__quoters(), lambda x, y : self.__ucsise(x, y))
'''
Lists the available ponies on one column without anything bold or otherwise formated
@param standard:bool Include standard ponies
@param extra:bool Include extra ponies
'''
def onelist(self, standard = True, extra = False): def onelist(self, standard = True, extra = False):
'''
Lists the available ponies on one column without anything bold or otherwise formated
@param standard:bool Include standard ponies
@param extra:bool Include extra ponies
'''
List.onelist(self.ponydirs if standard else None, List.onelist(self.ponydirs if standard else None,
self.extraponydirs if extra else None, self.extraponydirs if extra else None,
lambda x : self.__ucsise(x)) lambda x : self.__ucsise(x))
'''
Lists with all ponies that have quotes and are displayable, on one column without anything bold or otherwise formated
@param standard:bool Include standard ponies
@param extra:bool Include extra ponies
'''
def quoters(self, standard = True, extra = False): def quoters(self, standard = True, extra = False):
'''
Lists with all ponies that have quotes and are displayable, on one column without anything bold or otherwise formated
@param standard:bool Include standard ponies
@param extra:bool Include extra ponies
'''
## Get all quoters ## Get all quoters
ponies = list(self.__quoters()) if standard else [] ponies = list(self.__quoters()) if standard else []
@ -742,21 +734,21 @@ class Ponysay():
## Balloon methods ## ## Balloon methods ##
##################### #####################
'''
Prints a list of all balloons
'''
def balloonlist(self): def balloonlist(self):
'''
Prints a list of all balloons
'''
List.balloonlist(self.balloondirs, self.isthink) List.balloonlist(self.balloondirs, self.isthink)
'''
Returns one file with full path, names is filter for style names, also accepts filepaths
@param names:list<str> Balloons to choose from, may be `None`
@param alt:bool For method internal use
@param :str The file name of the balloon, will be `None` iff `names` is `None`
'''
def __getballoonpath(self, names, alt = False): def __getballoonpath(self, names, alt = False):
'''
Returns one file with full path, names is filter for style names, also accepts filepaths
@param names:list<str> Balloons to choose from, may be `None`
@param alt:bool For method internal use
@param :str The file name of the balloon, will be `None` iff `names` is `None`
'''
## Stop if their is no choosen balloon ## Stop if their is no choosen balloon
if names is None: if names is None:
return None return None
@ -799,13 +791,13 @@ class Ponysay():
return balloons[balloon] return balloons[balloon]
'''
Creates the balloon style object
@param balloonfile:str The file with the balloon style, may be `None`
@return :Balloon Instance describing the balloon's style
'''
def __getballoon(self, balloonfile): def __getballoon(self, balloonfile):
'''
Creates the balloon style object
@param balloonfile:str The file with the balloon style, may be `None`
@return :Balloon Instance describing the balloon's style
'''
return Balloon.fromFile(balloonfile, self.isthink) return Balloon.fromFile(balloonfile, self.isthink)
@ -814,20 +806,20 @@ class Ponysay():
## Displaying methods ## ## Displaying methods ##
######################## ########################
'''
Prints the name of the program and the version of the program
'''
def version(self): def version(self):
'''
Prints the name of the program and the version of the program
'''
## Prints the "ponysay $VERSION", if this is modified, ./dev/dist.sh must be modified accordingly ## Prints the "ponysay $VERSION", if this is modified, ./dev/dist.sh must be modified accordingly
print('%s %s' % ('ponysay', VERSION)) print('%s %s' % ('ponysay', VERSION))
'''
Print the pony with a speech or though bubble. message, pony and wrap from args are used.
@param args:ArgParser Parsed command line arguments
'''
def print_pony(self, args): def print_pony(self, args):
'''
Print the pony with a speech or though bubble. message, pony and wrap from args are used.
@param args:ArgParser Parsed command line arguments
'''
## Get the pony ## Get the pony
(selection, standard, extra) = ([], [], []) (selection, standard, extra) = ([], [], [])
for ponydir in self.ponydirs: for ponydir in self.ponydirs:

View file

@ -39,44 +39,45 @@ from ponysay import *
from metadata import * from metadata import *
VERSION = 'dev' # this line should not be edited, it is fixed by the build system
''' '''
The version of ponysay The version of ponysay
''' '''
VERSION = 'dev' # this line should not be edited, it is fixed by the build system
'''
Hack to enforce UTF-8 in output (in the future, if you see anypony not using utf-8 in
programs by default, report them to Princess Celestia so she can banish them to the moon)
@param text:str The text to print (empty string is default)
@param end:str The appendix to the text to print (line breaking is default)
'''
def print(text = '', end = '\n'): def print(text = '', end = '\n'):
'''
Hack to enforce UTF-8 in output (in the future, if you see anypony not using utf-8 in
programs by default, report them to Princess Celestia so she can banish them to the moon)
@param text:str The text to print (empty string is default)
@param end:str The appendix to the text to print (line breaking is default)
'''
sys.stdout.buffer.write((str(text) + end).encode('utf-8')) sys.stdout.buffer.write((str(text) + end).encode('utf-8'))
'''
stderr equivalent to print()
@param text:str The text to print (empty string is default)
@param end:str The appendix to the text to print (line breaking is default)
'''
def printerr(text = '', end = '\n'): def printerr(text = '', end = '\n'):
'''
stderr equivalent to print()
@param text:str The text to print (empty string is default)
@param end:str The appendix to the text to print (line breaking is default)
'''
sys.stderr.buffer.write((str(text) + end).encode('utf-8')) sys.stderr.buffer.write((str(text) + end).encode('utf-8'))
'''
This is the mane class of ponysay-tool
'''
class PonysayTool(): class PonysayTool():
''' '''
Starts the part of the program the arguments indicate This is the mane class of ponysay-tool
@param args:ArgParser Parsed command line arguments
''' '''
def __init__(self, args): def __init__(self, args):
'''
Starts the part of the program the arguments indicate
@param args:ArgParser Parsed command line arguments
'''
if args.argcount == 0: if args.argcount == 0:
args.help() args.help()
exit(255) exit(255)
@ -194,13 +195,13 @@ class PonysayTool():
exit(253) exit(253)
'''
Execute ponysay!
@param args Arguments
@param message Message
'''
def execPonysay(self, args, message = ''): def execPonysay(self, args, message = ''):
'''
Execute ponysay!
@param args Arguments
@param message Message
'''
class PhonyArgParser(): class PhonyArgParser():
def __init__(self, args, message): def __init__(self, args, message):
self.argcount = len(args) + (0 if message is None else 1) self.argcount = len(args) + (0 if message is None else 1)
@ -239,13 +240,13 @@ class PonysayTool():
return out return out
'''
Browse ponies
@param ponydir:str The pony directory to browse
@param restriction:list<str> Restrictions on listed ponies, may be None
'''
def browse(self, ponydir, restriction): def browse(self, ponydir, restriction):
'''
Browse ponies
@param ponydir:str The pony directory to browse
@param restriction:list<str> Restrictions on listed ponies, may be None
'''
## Call `stty` to determine the size of the terminal, this way is better than using python's ncurses ## Call `stty` to determine the size of the terminal, this way is better than using python's ncurses
termsize = None termsize = None
for channel in (sys.stdout, sys.stdin, sys.stderr): for channel in (sys.stdout, sys.stdin, sys.stderr):
@ -431,10 +432,10 @@ class PonysayTool():
(x, y) = (0, 0) (x, y) = (0, 0)
'''
Generate all kmsponies for the current TTY palette
'''
def generateKMS(self): def generateKMS(self):
'''
Generate all kmsponies for the current TTY palette
'''
class PhonyArgParser(): class PhonyArgParser():
def __init__(self, key, value): def __init__(self, key, value):
self.argcount = 3 self.argcount = 3
@ -495,13 +496,13 @@ class PonysayTool():
sys.stdout = stdout sys.stdout = stdout
'''
Generate pony dimension file for a directory
@param ponydir:str The directory
@param ponies:itr<str>? Ponies to which to limit
'''
def generateDimensions(self, ponydir, ponies = None): def generateDimensions(self, ponydir, ponies = None):
'''
Generate pony dimension file for a directory
@param ponydir:str The directory
@param ponies:itr<str>? Ponies to which to limit
'''
dimensions = [] dimensions = []
ponyset = None if (ponies is None) or (len(ponies) == 0) else set(ponies) ponyset = None if (ponies is None) or (len(ponies) == 0) else set(ponies)
for ponyfile in os.listdir(ponydir): for ponyfile in os.listdir(ponydir):
@ -581,13 +582,13 @@ class PonysayTool():
file.flush() file.flush()
'''
Generate pony metadata collection file for a directory
@param ponydir:str The directory
@param ponies:itr<str>? Ponies to which to limit
'''
def generateMetadata(self, ponydir, ponies = None): def generateMetadata(self, ponydir, ponies = None):
'''
Generate pony metadata collection file for a directory
@param ponydir:str The directory
@param ponies:itr<str>? Ponies to which to limit
'''
if not ponydir.endswith('/'): if not ponydir.endswith('/'):
ponydir += '/' ponydir += '/'
def makeset(value): def makeset(value):
@ -663,12 +664,12 @@ class PonysayTool():
file.flush() file.flush()
'''
Edit a pony file's metadata
@param ponyfile:str A pony file to edit
'''
def editmeta(self, ponyfile): def editmeta(self, ponyfile):
'''
Edit a pony file's metadata
@param ponyfile:str A pony file to edit
'''
(data, meta, image) = 3 * [None] (data, meta, image) = 3 * [None]
with open(ponyfile, 'rb') as file: with open(ponyfile, 'rb') as file:
@ -838,32 +839,32 @@ class PonysayTool():
''' class TextArea(): # TODO support small screens (This is being work on in GNU-Pony/featherweight)
GNU Emacs alike text area
'''
class TextArea(): # TODO support small screens
''' '''
Constructor GNU Emacs alike text area
@param fields:list<str> Field names
@param datamap:dist<str,str> Data map
@param left:int Left position of the component
@param top:int Top position of the component
@param width:int Width of the component
@param height:int Height of the component
@param termsize:(int,int) The height and width of the terminal
''' '''
def __init__(self, fields, datamap, left, top, width, height, termsize): def __init__(self, fields, datamap, left, top, width, height, termsize):
'''
Constructor
@param fields:list<str> Field names
@param datamap:dist<str,str> Data map
@param left:int Left position of the component
@param top:int Top position of the component
@param width:int Width of the component
@param height:int Height of the component
@param termsize:(int,int) The height and width of the terminal
'''
(self.fields, self.datamap, self.left, self.top, self.width, self.height, self.termsize) \ (self.fields, self.datamap, self.left, self.top, self.width, self.height, self.termsize) \
= (fields, datamap, left, top, width - 1, height, termsize) = (fields, datamap, left, top, width - 1, height, termsize)
'''
Execute text reading
@param saver Save method
'''
def run(self, saver): def run(self, saver):
'''
Execute text reading
@param saver Save method
'''
innerleft = UCS.dispLen(max(self.fields, key = UCS.dispLen)) + self.left + 3 innerleft = UCS.dispLen(max(self.fields, key = UCS.dispLen)) + self.left + 3
leftlines = [] leftlines = []
@ -1183,25 +1184,25 @@ class TextArea(): # TODO support small screens
HOME = os.environ['HOME'] if 'HOME' in os.environ else os.path.expanduser('~')
''' '''
The user's home directory The user's home directory
''' '''
HOME = os.environ['HOME'] if 'HOME' in os.environ else os.path.expanduser('~')
pipelinein = not sys.stdin.isatty()
''' '''
Whether stdin is piped Whether stdin is piped
''' '''
pipelinein = not sys.stdin.isatty()
pipelineout = not sys.stdout.isatty()
''' '''
Whether stdout is piped Whether stdout is piped
''' '''
pipelineout = not sys.stdout.isatty()
pipelineerr = not sys.stderr.isatty()
''' '''
Whether stderr is piped Whether stderr is piped
''' '''
pipelineerr = not sys.stderr.isatty()
usage_program = '\033[34;1mponysay-tool\033[21;39m' usage_program = '\033[34;1mponysay-tool\033[21;39m'
@ -1241,10 +1242,10 @@ opts.add_argumented( ['--edit-rm'], arg = 'PONY-FILE', help = 'Remove
opts.add_argumented( ['--edit-apply'], arg = 'PONY-FILE', help = 'Apply metadata from stdin to a pony file') opts.add_argumented( ['--edit-apply'], arg = 'PONY-FILE', help = 'Apply metadata from stdin to a pony file')
opts.add_argumented( ['--edit-stash'], arg = 'PONY-FILE', help = 'Print applyable metadata from a pony file') opts.add_argumented( ['--edit-stash'], arg = 'PONY-FILE', help = 'Print applyable metadata from a pony file')
unrecognised = not opts.parse()
''' '''
Whether at least one unrecognised option was used Whether at least one unrecognised option was used
''' '''
unrecognised = not opts.parse()
PonysayTool(args = opts) PonysayTool(args = opts)

View file

@ -33,24 +33,25 @@ from common import *
'''
Class used for correcting spellos and typos,
Note that this implementation will not find that correctly spelled word are correct faster than it corrects words.
It is also limited to words of size 0 to 127 (inclusive)
'''
class SpelloCorrecter(): # Naïvely and quickly ported and adapted from optimised Java, may not be the nicest, or even fast, Python code class SpelloCorrecter(): # Naïvely and quickly ported and adapted from optimised Java, may not be the nicest, or even fast, Python code
''' '''
Constructor Class used for correcting spellos and typos,
@param directories:list<str> List of directories that contains the file names with the correct spelling Note that this implementation will not find that correctly spelled word are correct faster than it corrects words.
@param ending:str The file name ending of the correctly spelled file names, this is removed for the name It is also limited to words of size 0 to 127 (inclusive)
-- OR -- (emulated overloading [overloading is absent in Python])
@param directories:list<str> The file names with the correct spelling
''' '''
def __init__(self, directories, ending = None): def __init__(self, directories, ending = None):
'''
Constructor
@param directories:list<str> List of directories that contains the file names with the correct spelling
@param ending:str The file name ending of the correctly spelled file names, this is removed for the name
-- OR -- (emulated overloading [overloading is absent in Python])
@param directories:list<str> The file names with the correct spelling
'''
self.weights = {'k' : {'c' : 0.25, 'g' : 0.75, 'q' : 0.125}, self.weights = {'k' : {'c' : 0.25, 'g' : 0.75, 'q' : 0.125},
'c' : {'k' : 0.25, 'g' : 0.75, 's' : 0.5, 'z' : 0.5, 'q' : 0.125}, 'c' : {'k' : 0.25, 'g' : 0.75, 's' : 0.5, 'z' : 0.5, 'q' : 0.125},
's' : {'z' : 0.25, 'c' : 0.5}, 's' : {'z' : 0.25, 'c' : 0.5},
@ -145,13 +146,13 @@ class SpelloCorrecter(): # Naïvely and quickly ported and adapted from optimise
# index -= 1; # index -= 1;
'''
Finds the closests correct spelled word
@param used:str The word to correct
@return (words, distance):(list<string>, int) A list the closest spellings and the weighted distance
'''
def correct(self, used): def correct(self, used):
'''
Finds the closests correct spelled word
@param used:str The word to correct
@return (words, distance):(list<string>, int) A list the closest spellings and the weighted distance
'''
if len(used) > 127: if len(used) > 127:
return ([used], 0) return ([used], 0)
@ -159,12 +160,12 @@ class SpelloCorrecter(): # Naïvely and quickly ported and adapted from optimise
return (self.corrections, self.closestDistance) return (self.corrections, self.closestDistance)
'''
Finds the closests correct spelled word
@param used:str The word to correct, it must satisfy all restrictions
'''
def __correct(self, used): def __correct(self, used):
'''
Finds the closests correct spelled word
@param used:str The word to correct, it must satisfy all restrictions
'''
self.closestDistance = 0x7FFFFFFF self.closestDistance = 0x7FFFFFFF
previous = self.dictionary[-1] previous = self.dictionary[-1]
prevLen = 0 prevLen = 0
@ -217,18 +218,18 @@ class SpelloCorrecter(): # Naïvely and quickly ported and adapted from optimise
prevLen = len(proper) prevLen = len(proper)
'''
Calculate the distance between a correct word and a incorrect word
@param proper:str The correct word
@param y0:int The offset for `proper`
@param yn:int The length, before applying `y0`, of `proper`
@param used:str The incorrect word
@param x0:int The offset for `used`
@param xn:int The length, before applying `x0`, of `used`
@return :float The distance between the words
'''
def __distance(self, proper, y0, yn, used, x0, xn): def __distance(self, proper, y0, yn, used, x0, xn):
'''
Calculate the distance between a correct word and a incorrect word
@param proper:str The correct word
@param y0:int The offset for `proper`
@param yn:int The length, before applying `y0`, of `proper`
@param used:str The incorrect word
@param x0:int The offset for `used`
@param xn:int The length, before applying `x0`, of `used`
@return :float The distance between the words
'''
my = self.M[y0] my = self.M[y0]
for y in range(y0, yn): for y in range(y0, yn):
best = 0x7FFFFFFF best = 0x7FFFFFFF

View file

@ -33,18 +33,19 @@ from common import *
'''
UCS utility class
'''
class UCS(): class UCS():
''' '''
Checks whether a character is a combining character UCS utility class
@param char:chr The character to test
@return :bool Whether the character is a combining character
''' '''
@staticmethod @staticmethod
def isCombining(char): def isCombining(char):
'''
Checks whether a character is a combining character
@param char:chr The character to test
@return :bool Whether the character is a combining character
'''
o = ord(char) o = ord(char)
if (0x0300 <= o) and (o <= 0x036F): return True if (0x0300 <= o) and (o <= 0x036F): return True
if (0x20D0 <= o) and (o <= 0x20FF): return True if (0x20D0 <= o) and (o <= 0x20FF): return True
@ -53,14 +54,14 @@ class UCS():
return False return False
'''
Gets the number of combining characters in a string
@param string:str A text to count combining characters in
@return :int The number of combining characters in the string
'''
@staticmethod @staticmethod
def countCombining(string): def countCombining(string):
'''
Gets the number of combining characters in a string
@param string:str A text to count combining characters in
@return :int The number of combining characters in the string
'''
rc = 0 rc = 0
for char in string: for char in string:
if UCS.isCombining(char): if UCS.isCombining(char):
@ -68,13 +69,13 @@ class UCS():
return rc return rc
'''
Gets length of a string not counting combining characters
@param string:str The text of which to determine the monospaced width
@return The determine the monospaced width of the text, provided it does not have escape sequnces
'''
@staticmethod @staticmethod
def dispLen(string): def dispLen(string):
'''
Gets length of a string not counting combining characters
@param string:str The text of which to determine the monospaced width
@return The determine the monospaced width of the text, provided it does not have escape sequnces
'''
return len(string) - UCS.countCombining(string) return len(string) - UCS.countCombining(string)