colpage.py - Printable Version +- Python Forum (https://python-forum.io) +-- Forum: General (https://python-forum.io/forum-1.html) +--- Forum: Code sharing (https://python-forum.io/forum-5.html) +--- Thread: colpage.py (/thread-3104.html) Pages:
1
2
|
colpage.py - Skaperen - Apr-29-2017 colpage.py: #!/usr/bin/env python3 # -*- coding: utf-8 -*- from __future__ import absolute_import, division, print_function, unicode_literals """Organize strings into columns and columns into pages using separate coroutines. file colpage.py purpose organize strings into columns and columns into pages email 10054452614123394844460370234029112340408691 The intent is that this command works correctly under both Python 2 and Python 3. Please report failures or code improvement to the author. """ __license__ = """ Copyright (C) 2017, by Phil D. Howard - all other rights reserved Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA, OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. The author may be contacted by decoding the number 10054452614123394844460370234029112340408691 (provu igi la numeron al duuma) """ import fcntl, os, struct, sys, termios def get_terminal_geometry(fd=1): """Get terminal geometry (width,heght) for a specified fd.""" import fcntl,os,struct,termios try: fd = os.open('/dev/tty',os.O_WRONLY) r = struct.unpack('4H',fcntl.ioctl(fd,termios.TIOCGWINSZ,struct.pack('4H',0,0,0,0)))[1::-1] os.close(fd) except: r = None return r class colpage: """class representing column formatted pages""" def __init__(self,gutter=None,width=None,height=None): """initialize class colpage""" self.page_num = 0 return self.geometry( gutter=gutter, width=width, height=height ) def flush(self): """flush pending columns to a completed page""" gutter = self.gutter page = ['']*self.height mc = len(self.columns)-1 for n,column in enumerate(self.columns): # add each column to the page on the right side width = self.widths[n] for m,line in enumerate(column): # append each line to the page being built page[m] += (line+' '*width)[:width] if n < mc: # append a gutter except after the last column page[m] += ' '*gutter self.pages.append(page) # add the new page to the list of completed pages self.columns = [] self.widths = [] return len(self.pages) def geometry(self,gutter=None,width=None,height=None): """set or get colpage geometry (gutter,width,height)""" if 'gutter' in dir(self) and gutter is None and width is None and height is None: self_gutter=self.gutter return (self.gutter,self.width,self.height) if width is None or height is None: term = get_terminal_geometry() if isinstance(gutter,int): self.gutter = gutter elif gutter is None: gutter = 1 if 'gutter' in dir(self): self.gutter = gutter if isinstance(width,int): self.width = width if width is None: width = term[0] if 'width' in dir(self): self.width = width if isinstance(height,int): self.height = height if height is None: height = term[1] if 'height' in dir(self): self.height = height if gutter < 0: raise ValueError if width < 2: raise ValueError if height < 2: raise ValueError self.gutter = gutter self.height = height self.width = width self.columns = [] self.lines = [] self.pages = [] self.widths = [] self.page_starter = ['']*self.height return def destruct(self): del self return def get(self): """get the next completed page (list of list of str) or None.""" if len(self.pages) > 0: return self.pages.pop(0) return None def print_ready_pages(self,**opts): """print the pages that are ready.""" while True: page = self.get() if page == None: break for line in page: print(line,**opts) return def put(self,*args): """put a new string as a line into a page column.""" if len(args) > 0: line = ' '.join(args) self.lines.append(line) # if this new line does not fill the height, then we are done if len(self.lines) < self.height: return # the working list is full or being flushed by put() with no arguments width = max(len(l) for l in self.lines) total_width = sum(this_width+self.gutter for this_width in self.widths)+width if total_width >= self.width: self.flush() self.columns.append(self.lines) self.widths.append(width) if len(args) > 0: self.lines = [] else: self.flush() return def main(args): """main: command line reads stdin, forms it into columns, outputs to stdout.""" if len(args) > 3: gutter, width, height = [int(arg) for arg in args[1:]] colz = colpage( gutter=gutter, width=width, height=height ) else: # let colpage try to get the geometry from the terminal size colz = colpage( gutter=1 ) # feed stdin to colpage, send its pages to stdout for line in sys.stdin: colz.put(line[:-1]) colz.print_ready_pages() # finish up at EOF colz.put() colz.print_ready_pages() return if __name__ == '__main__': try: result=main(sys.argv) sys.stdout.flush() except BrokenPipeError: result=99 except KeyboardInterrupt: print('') result=98 if result is 0 or result is None or result is True: exit(0) if result is 1 or result is False: exit(1) if isinstance(result,str): print(result,file=sys.stderr) exit(2) try: exit(int(result)) except ValueError: print(str(result),file=sys.stderr) exit(3) except TypeError: exit(4) # EOFthis is a script that implements a class and command to format a one-at-a-time sequence (the caller put()s each line one at a time with an empty put() call to indicate the end of the sequence of lines) into column oriented pages (the caller checks for a finished page via get() or print_ready_pages()). the command works on stdin and stdout. the geometry (3 numbers: gutter, width, height) is optional and it tries to use the controlling terminal geometry if not given. the order of forming the columns is top to bottom, on the left column first, adding each column on the right side until no more space is available for the width. my thoughts for the future: 1. a generator version. 2. a means to have it print each finished page 3. headers/footers/sidings on each page, maybe with page numbers. RE: colpage.py - volcano63 - Apr-29-2017 __init__ does not return a value!!!!It is not a constructor - as some imply, it is called by a constructor - which is __new__
RE: colpage.py - nilamo - Apr-29-2017 It returns self.geometry(), which itself doesn't return anything. So it looks like it's just doing more setup, but has return there to... confuse you? :p
RE: colpage.py - Skaperen - Apr-30-2017 what should it have? it does work, but maybe that won't be the case in a future release. RE: colpage.py - Skaperen - Apr-30-2017 it has been my understanding that return is the same as return None .
RE: colpage.py - nilamo - Apr-30-2017 That's not what you're doing, though. class colpage: def __init__(self,gutter=None,width=None,height=None): return self.geometry( gutter=gutter, width=width, height=height )It's just weird, not wrong. __init__ *can't* return anything, even if you want it to. So having return in an __init__ is just confusing to anyone reading the code, as it looks like you're trying to do something cryptic... when you're not, all you're doing is calling the function to setup some variables.
RE: colpage.py - Mekire - Apr-30-2017 Quote:It's just weird, not wrong.Honestly, I would say despite not being wrong, it is still quite wrong. __init__ can only return None and by convention we never do this explicitly for __init__. The function is "wrong" because just like this thread shows, it confuses people. Just write this and avoid confusion: class colpage: def __init__(self, gutter=None, width=None, height=None): self.geometry( gutter=gutter, width=width, height=height)Though I think changes should still be made to make attributes clearer and there is no need to use that function to both set and get these attributes. As a C programmer I would think you would hate a function with an unpredictable return type. RE: colpage.py - Skaperen - Apr-30-2017 my understanding has been that __init__ must return None. RE: colpage.py - Mekire - Apr-30-2017 Implicitly. No one ever explicitly returns None for __init__ or indeed many functions at all. I would say only explicitly return None on a function where that has meaning. If your function changes a mutable it was passed or simply is setting attributes on a class it is customary to leave it off. Functions with no explicit return statement will return None regardless. This goes for almost every return in the code you posted, but as stated specifically jumps out for the init. RE: colpage.py - Skaperen - Apr-30-2017 (Apr-30-2017, 04:42 AM)Mekire Wrote: Though I think changes should still be made to make attributes clearer and there is no need to use that function to both set and get these attributes. As a C programmer I would think you would hate a function with an unpredictable return type.even in C i have made functions that set things and return the previous value for years. and then in cases where there is nothing being set, it just gets the current setting. so it's a get and set in one. i have been doing duck typing, even in C. i called it context typing. i've seen the get/set combining in other source code, too. i've even seen cases where there being no way to not set a value, the caller set some value just to get the current value, then called again with what was returned to set it back. i think it was from my IBM mainframe assembler days. |