├── README.md └── cat.py /README.md: -------------------------------------------------------------------------------- 1 | # IllustratedPy3 2 | 3 | If you have questions or concerns, click on Issues above. Please search if there is a similar issue. If not, please file an issue. 4 | -------------------------------------------------------------------------------- /cat.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | r"""A simple implementation of the unix ``cat`` 4 | command. It only implements the ``--number`` 5 | option. It is useful for illustrating file 6 | layout and best practices in Python. 7 | 8 | This is a triple quoted docstring for the whole 9 | module (this file). If you import this module 10 | somewhere else and run ``help(cat)``, you will 11 | see this. 12 | 13 | This docstring also contains a ``doctest`` which 14 | serves as an example of programmatically using 15 | the code. It also functions as a doctest. The 16 | ``doctest`` module can execute this docstring 17 | and validate it by checking any output. 18 | 19 | >>> import io 20 | >>> fin = io.StringIO(\ 21 | ... 'hello\nworld\n') 22 | >>> fout = io.StringIO() 23 | >>> cat = Catter([fin], 24 | ... show_numbers=True) 25 | >>> cat.run(fout) 26 | >>> print(fout.getvalue()) 27 | 1 hello 28 | 2 world 29 | 30 | """ 31 | 32 | import argparse 33 | import logging 34 | import sys 35 | 36 | 37 | __version__ = '0.0.1' 38 | 39 | 40 | logging.basicConfig( 41 | level=logging.DEBUG) 42 | 43 | 44 | class Catter(object): 45 | """ 46 | A class to concatenate files to 47 | standard out 48 | 49 | This is a class docstring, 50 | ``help(cat.Catter)`` would show 51 | this. 52 | """ 53 | 54 | def __init__(self, files, 55 | show_numbers=False): 56 | self.files = files 57 | self.show_numbers = show_numbers 58 | 59 | def run(self, fout): 60 | # use 6 spaces for numbers and right align 61 | fmt = '{0:>6} {1}' 62 | count = 1 63 | for fin in self.files: 64 | logging.debug('catting {0}'.format(fin)) 65 | for line in fin.readlines(): 66 | if self.show_numbers: 67 | fout.write(fmt.format( 68 | count, line)) 69 | count += 1 70 | else: 71 | fout.write(line) 72 | 73 | 74 | def main(args): 75 | """ 76 | Logic to run a cat with arguments 77 | """ 78 | parser = argparse.ArgumentParser( 79 | description='Concatenate FILE(s), or ' 80 | 'standard input, to standard output') 81 | parser.add_argument('--version', 82 | action='version', version=__version__) 83 | parser.add_argument('-n', '--number', 84 | action='store_true', 85 | help='number all output lines') 86 | parser.add_argument('files', nargs='*', 87 | type=argparse.FileType('r'), 88 | default=[sys.stdin], metavar='FILE') 89 | parser.add_argument('--run-tests', 90 | action='store_true', 91 | help='run module tests') 92 | args = parser.parse_args(args) 93 | 94 | if args.run_tests: 95 | import doctest 96 | doctest.testmod() 97 | else: 98 | cat = Catter(args.files, args.number) 99 | cat.run(sys.stdout) 100 | logging.debug('done catting') 101 | 102 | if __name__ == '__main__': 103 | main(sys.argv[1:]) 104 | --------------------------------------------------------------------------------