From 3d6408e723536d2452aa3e57550a1c55dfe86f15 Mon Sep 17 00:00:00 2001 From: Gabriel Soares Martins Date: Fri, 20 Dec 2024 18:31:23 -0300 Subject: [PATCH 01/12] Add gitignore --- .gitignore | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/.gitignore b/.gitignore index 68ca39690..8430880fe 100644 --- a/.gitignore +++ b/.gitignore @@ -11,23 +11,24 @@ tex2pdf* .coverage .idea .vscode -02_crowsnest/crowsnest.py -03_picnic/picnic.py -04_jump_the_five/jump.py -05_howler/howler.py -06_wc/wc.py -07_gashlycrumb/gashlycrumb.py -08_apples_and_bananas/apples.py -09_abuse/abuse.py -10_telephone/telephone.py -11_bottles_of_beer/bottles.py -12_ransom/ransom.py -13_twelve_days/twelve_days.py -14_rhymer/rhymer.py -15_kentucky_friar/friar.py -16_scrambler/scrambler.py -17_mad_libs/mad.py -18_gematria/gematria.py -19_wod/wod.py -20_password/password.py -21_tictactoe/tictactoe.py +**/environ +#02_crowsnest/crowsnest.py +#03_picnic/picnic.py +#04_jump_the_five/jump.py +#05_howler/howler.py +#06_wc/wc.py +#07_gashlycrumb/gashlycrumb.py +#08_apples_and_bananas/apples.py +#09_abuse/abuse.py +#10_telephone/telephone.py +#11_bottles_of_beer/bottles.py +#12_ransom/ransom.py +#13_twelve_days/twelve_days.py +#14_rhymer/rhymer.py +#15_kentucky_friar/friar.py +#16_scrambler/scrambler.py +#17_mad_libs/mad.py +#18_gematria/gematria.py +#19_wod/wod.py +#20_password/password.py +#21_tictactoe/tictactoe.py From 9932caa0aece54324f2c9b33d942a384c32ce5c3 Mon Sep 17 00:00:00 2001 From: Gabriel Soares Martins Date: Fri, 20 Dec 2024 18:21:11 -0300 Subject: [PATCH 02/12] Finished Chapter one --- 01_hello/hello.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100755 01_hello/hello.py diff --git a/01_hello/hello.py b/01_hello/hello.py new file mode 100755 index 000000000..cfb190009 --- /dev/null +++ b/01_hello/hello.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 +""" +Author: grogfx +Purpose: Say hello +""" + +import argparse + + +def get_args(): + """Get the command-line arguments""" + parser = argparse.ArgumentParser(description='Say Hello') + parser.add_argument('-n', + '--name', + metavar='name', + default='World', + help='Name to greet') + return parser.parse_args() + + +def main(): + """Make a jazz noise here""" + args = get_args() + print('Hello, ' + args.name + '!') + + +if __name__ == '__main__': + main() From 9e51610f99282b62fdaf3cb826d18cdb303047c6 Mon Sep 17 00:00:00 2001 From: Gabriel Soares Martins Date: Fri, 20 Dec 2024 18:21:24 -0300 Subject: [PATCH 03/12] Finished chapter two --- 02_crowsnest/crowsnest.py | 48 +++++++++++++++++++++++++++++++++++++++ 02_crowsnest/test.py | 43 ++++++++++++++++++++++++++++------- 2 files changed, 83 insertions(+), 8 deletions(-) create mode 100755 02_crowsnest/crowsnest.py diff --git a/02_crowsnest/crowsnest.py b/02_crowsnest/crowsnest.py new file mode 100755 index 000000000..e88d0f0c7 --- /dev/null +++ b/02_crowsnest/crowsnest.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +""" +Author : gmartins +Date : 2024-12-17 +Purpose: Rock the Casbah +""" + +import argparse + + +# -------------------------------------------------- +def get_args(): + """Get command-line arguments""" + + parser = argparse.ArgumentParser( + description="Crow's Nest -- choose the correct article", + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + + parser.add_argument('word', metavar='word', help='A word') + + parser.add_argument('-s', + '--side', + metavar='side', + help='larboard or starboard', + default='larboard') + + return parser.parse_args() + + +# -------------------------------------------------- +def main(): + """Make a jazz noise here""" + + args = get_args() + word = args.word + + if word.isalpha(): + article = 'an' if word[0].lower() in 'aeiou' else 'a' + article = article.title() if word[0].isupper() else article + side = 'starboard' if args.side == 'right' else 'larboard' + print(f'Ahoy, Captain, {article} {word} off the {side} bow!') + else: + print('Invalid word') + + +# -------------------------------------------------- +if __name__ == '__main__': + main() diff --git a/02_crowsnest/test.py b/02_crowsnest/test.py index d4e25c6c7..56c74424b 100755 --- a/02_crowsnest/test.py +++ b/02_crowsnest/test.py @@ -12,7 +12,7 @@ 'zebrafish' ] vowel_words = ['aviso', 'eel', 'iceberg', 'octopus', 'upbound'] -template = 'Ahoy, Captain, {} {} off the larboard bow!' +template = 'Ahoy, Captain, {} {} off the {} bow!' # -------------------------------------------------- @@ -38,16 +38,16 @@ def test_consonant(): for word in consonant_words: out = getoutput(f'{prg} {word}') - assert out.strip() == template.format('a', word) + assert out.strip() == template.format('a', word, 'larboard') # -------------------------------------------------- def test_consonant_upper(): - """brigantine -> a Brigatine""" + """brigantine -> A Brigantine""" for word in consonant_words: out = getoutput(f'{prg} {word.title()}') - assert out.strip() == template.format('a', word.title()) + assert out.strip() == template.format('A', word.title(), 'larboard') # -------------------------------------------------- @@ -56,13 +56,40 @@ def test_vowel(): for word in vowel_words: out = getoutput(f'{prg} {word}') - assert out.strip() == template.format('an', word) + assert out.strip() == template.format('an', word, 'larboard') # -------------------------------------------------- def test_vowel_upper(): - """octopus -> an Octopus""" + """octopus -> An Octopus""" for word in vowel_words: - out = getoutput(f'{prg} {word.upper()}') - assert out.strip() == template.format('an', word.upper()) + out = getoutput(f'{prg} {word.title()}') + assert out.strip() == template.format('An', word.title(), 'larboard') + + +# -------------------------------------------------- +def test_side(): + """--side any/right -> larboard/starboard""" + + word = 'narwhal' + out = getoutput(f'{prg} {word}') + assert out.strip() == template.format('a', word, 'larboard') + + out = getoutput(f'{prg} {word} -s blah') + assert out.strip() == template.format('a', word, 'larboard') + + out = getoutput(f'{prg} {word} -s right') + assert out.strip() == template.format('a', word, 'starboard') + + out = getoutput(f'{prg} {word} --side left') + assert out.strip() == template.format('a', word, 'larboard') + + +# -------------------------------------------------- +def test_alpha(): + """test for alpha strings""" + + for word in ['!narwhal', 'narwha1', '1narwhal', 'narwhal!']: + out = getoutput(f'{prg} {word}') + assert out.strip() == 'Invalid word' From 61df97d9ad8c470847ec17b8bfa0ff329d6479d3 Mon Sep 17 00:00:00 2001 From: Gabriel Soares Martins Date: Fri, 20 Dec 2024 18:21:36 -0300 Subject: [PATCH 04/12] Finished Chapter three. --- 03_picnic/picnic.py | 79 +++++++++++++++++++++++++++++++++++++++++++++ 03_picnic/test.py | 21 ++++++++++++ 2 files changed, 100 insertions(+) create mode 100755 03_picnic/picnic.py diff --git a/03_picnic/picnic.py b/03_picnic/picnic.py new file mode 100755 index 000000000..efec83b33 --- /dev/null +++ b/03_picnic/picnic.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python3 +""" +Author : gmartins +Date : 2024-12-18 +Purpose: Rock the Casbah +""" + +import argparse + +TEMPLATE = 'You are bringing {}.' + + +# -------------------------------------------------- +def get_args(): + """Get command-line arguments""" + + parser = argparse.ArgumentParser( + description='Picnic game', + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + + parser.add_argument('positional', + metavar='str', + nargs='+', + help='Item(s) to bring') + + parser.add_argument('-s', + '--sorted', + help='Sort the items', + action='store_true') + + parser.add_argument('-n', + '--not-oxford', + help='Do not print Oxford comma', + action='store_true') + + parser.add_argument('-r', + '--sep', + metavar='separator', + default=',', + help='Separator') + + return parser.parse_args() + + +# -------------------------------------------------- +def main(): + """Make a jazz noise here""" + + args = get_args() + sorted_arg = args.sorted + + pos_arg = args.positional + oxford_arg = args.not_oxford + sep_arg = args.sep + ' ' + + if sep_arg != ', ': + oxford_arg = True + + if sorted_arg: + pos_arg.sort() + + if len(pos_arg) < 1: + print('Error') + elif len(pos_arg) == 1: + print(TEMPLATE.format(pos_arg[0])) + else: + if len(pos_arg) == 2: + s = ' and '.join(pos_arg) + else: + pos_arg[-1] = 'and ' + pos_arg[-1] + s = sep_arg.join(pos_arg) + if oxford_arg: + s = s.replace(sep_arg + 'and', ' and') + print(TEMPLATE.format(s)) + + +# -------------------------------------------------- +if __name__ == '__main__': + main() diff --git a/03_picnic/test.py b/03_picnic/test.py index 281fab552..ae0600bbe 100755 --- a/03_picnic/test.py +++ b/03_picnic/test.py @@ -66,3 +66,24 @@ def test_more_than_two_sorted(): out = getoutput(f'{prg} {arg} --sorted') expected = ('You are bringing apples, bananas, cherries, and dates.') assert out.strip() == expected + + +# -------------------------------------------------- +def test_more_than_two_not_oxford(): + """more than two items that do not contain oxford comma""" + + arg = '"potato chips" coleslaw cupcakes "French silk pie" -n' + out = getoutput(f'{prg} {arg}') + expected = ('You are bringing potato chips, coleslaw, ' + 'cupcakes and French silk pie.') + assert out.strip() == expected + + +def test_more_than_two_sep(): + """more than two items with custom separator""" + + arg = '"potato chips" coleslaw cupcakes "French silk pie" -r ";"' + out = getoutput(f'{prg} {arg}') + expected = ('You are bringing potato chips; coleslaw; ' + 'cupcakes and French silk pie.') + assert out.strip() == expected From f461d1430fd4fcec4049dea64078f5b93ef56f25 Mon Sep 17 00:00:00 2001 From: Gabriel Soares Martins Date: Mon, 30 Dec 2024 18:56:19 -0300 Subject: [PATCH 05/12] Finished chapter four. --- 04_jump_the_five/jump.py | 49 ++++++++++++++++++++++++++++++++++ 04_jump_the_five/jump0.py | 55 +++++++++++++++++++++++++++++++++++++++ 04_jump_the_five/test.py | 10 +++++++ 3 files changed, 114 insertions(+) create mode 100755 04_jump_the_five/jump.py create mode 100755 04_jump_the_five/jump0.py diff --git a/04_jump_the_five/jump.py b/04_jump_the_five/jump.py new file mode 100755 index 000000000..a0584ba7e --- /dev/null +++ b/04_jump_the_five/jump.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +""" +Author : gmartins +Date : 2024-12-30 +Purpose: Rock the Casbah +""" + +import argparse + + +# -------------------------------------------------- +def get_args(): + """Get command-line arguments""" + + parser = argparse.ArgumentParser( + description='Jump the five', + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + + parser.add_argument('positional', metavar='str', help='Input text') + + return parser.parse_args() + + +# -------------------------------------------------- +def main(): + """Make a jazz noise here""" + + args = get_args() + pos_arg = args.positional + + jumper = { + '1': '9', + '2': '8', + '3': '7', + '4': '6', + '5': '0', + '6': '4', + '7': '3', + '8': '2', + '9': '1', + '0': '5' + } + + print(''.join([jumper.get(char, char) for char in pos_arg])) + + +# -------------------------------------------------- +if __name__ == '__main__': + main() diff --git a/04_jump_the_five/jump0.py b/04_jump_the_five/jump0.py new file mode 100755 index 000000000..457491878 --- /dev/null +++ b/04_jump_the_five/jump0.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 +""" +Author : gmartins +Date : 2024-12-30 +Purpose: Rock the Casbah +""" + +import argparse + + +# -------------------------------------------------- +def get_args(): + """Get command-line arguments""" + + parser = argparse.ArgumentParser( + description='Jump the five', + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + + parser.add_argument('positional', metavar='str', help='Input text') + + return parser.parse_args() + + +# -------------------------------------------------- +def main(): + """Make a jazz noise here""" + + args = get_args() + pos_arg = args.positional + + jumper = { + '1': 'one', + '2': 'two', + '3': 'three', + '4': 'four', + '5': 'five', + '6': 'six', + '7': 'seven', + '8': 'eight', + '9': 'nine', + '0': 'zero' + } + + output = '' + for char in pos_arg: + output += jumper[char] + '-' if char in jumper else char + output = output.replace('--', '-') + output = output.replace('-.', '.') + output = output.replace('- ', '.') + + print(output) + +# -------------------------------------------------- +if __name__ == '__main__': + main() diff --git a/04_jump_the_five/test.py b/04_jump_the_five/test.py index 5dc89e311..be3799a81 100755 --- a/04_jump_the_five/test.py +++ b/04_jump_the_five/test.py @@ -5,6 +5,7 @@ from subprocess import getstatusoutput prg = './jump.py' +prg0 = './jump0.py' # -------------------------------------------------- @@ -12,6 +13,7 @@ def test_exists(): """exists""" assert os.path.isfile(prg) + assert os.path.isfile(prg0) # -------------------------------------------------- @@ -40,3 +42,11 @@ def test_02(): rv, out = getstatusoutput(f'{prg} "That number to call is 098-765-4321."') assert rv == 0 assert out.rstrip() == 'That number to call is 512-340-6789.' + +# -------------------------------------------------- +def test_03(): + """test""" + + rv, out = getstatusoutput(f'{prg0} "That number to call is 098-765-4321."') + assert rv == 0 + assert out.rstrip() == 'That number to call is zero-nine-eight-seven-six-five-four-three-two-one.' From 71de553fc9cad97946091aee401210f6749504ca Mon Sep 17 00:00:00 2001 From: Gabriel Soares Martins Date: Thu, 2 Jan 2025 17:51:31 -0300 Subject: [PATCH 06/12] Finished chapter five --- 05_howler/howler.py | 101 +++++++++++++++++++++++ 05_howler/test-outs-lower/preamble.txt | 6 ++ 05_howler/test-outs-lower/sonnet-29.txt | 17 ++++ 05_howler/test-outs-lower/the-bustle.txt | 9 ++ 05_howler/test.py | 71 ++++++++++++++++ 5 files changed, 204 insertions(+) create mode 100755 05_howler/howler.py create mode 100644 05_howler/test-outs-lower/preamble.txt create mode 100644 05_howler/test-outs-lower/sonnet-29.txt create mode 100644 05_howler/test-outs-lower/the-bustle.txt diff --git a/05_howler/howler.py b/05_howler/howler.py new file mode 100755 index 000000000..f5c1b06be --- /dev/null +++ b/05_howler/howler.py @@ -0,0 +1,101 @@ +#!/usr/bin/env python3 +""" +Author : gmartins +Date : 2025-01-02 +Purpose: Rock the Casbah +""" + +import argparse +import os +import sys +import io +import pathlib + + +# -------------------------------------------------- +def get_args(): + """Get command-line arguments""" + + parser = argparse.ArgumentParser( + description='Howler (upper case inputs)', + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + + parser.add_argument('text', + metavar='text', + nargs='+', + type=str, + help='Input string or file') + + parser.add_argument('-o', + '--outfile', + help='Output filename', + metavar='str', + type=str, + default='') + + parser.add_argument('-e', '--ee', help='Lower case?', action='store_true') + + parser.add_argument('-d', + '--dir', + help='Output directory', + metavar='str', + default='output', + type=str) + + return parser.parse_args() + + +def action(string, lower=False): + """Choose operation to transform input""" + return string.lower() if lower else string.upper() + + +def exec_one_file(text, outfile_in, lower, multiple_files, dir_in): + p = os.path.join(pathlib.Path.cwd(), dir_in) + if not os.path.isdir(p): + p = pathlib.Path(p) + p.mkdir(exist_ok=True) + + if os.path.isfile(text) and multiple_files: + outfile_in = os.path.join(os.path.abspath(dir_in), + os.path.basename(text)) + elif (not os.path.isfile(text)) and multiple_files: + return True + + outfile = open(outfile_in, 'wt', + encoding='utf-8') if outfile_in else sys.stdout + + with open(text, 'rt', encoding='utf-8') if os.path.isfile( + text) else io.StringIO(text + '\n') as infile: + for line in infile: + outfile.write(action(line, lower)) + + if outfile_in: + outfile.close() + + return True + + +# -------------------------------------------------- +def main(): + """Make a jazz noise here""" + + args = get_args() + text_arg = args.text + outfile_arg = args.outfile + lower_arg = args.ee + dir_in = args.dir + + multiple_files = False + if len(text_arg) > 1: + multiple_files = True + + for file_in in text_arg: + exec_one_file(file_in, outfile_arg, lower_arg, multiple_files, dir_in) + + return 0 + + +# -------------------------------------------------- +if __name__ == '__main__': + main() diff --git a/05_howler/test-outs-lower/preamble.txt b/05_howler/test-outs-lower/preamble.txt new file mode 100644 index 000000000..e63fcc70e --- /dev/null +++ b/05_howler/test-outs-lower/preamble.txt @@ -0,0 +1,6 @@ +when, in the course of human events, it becomes necessary for one people to +dissolve the political bands which have connected them with another, and to +assume among the powers of the earth, the separate and equal station to +which the laws of nature and of nature's god entitle them, a decent respect +to the opinions of mankind requires that they should declare the causes +which impel them to the separation. diff --git a/05_howler/test-outs-lower/sonnet-29.txt b/05_howler/test-outs-lower/sonnet-29.txt new file mode 100644 index 000000000..68d4eca38 --- /dev/null +++ b/05_howler/test-outs-lower/sonnet-29.txt @@ -0,0 +1,17 @@ +sonnet 29 +william shakespeare + +when, in disgrace with fortune and men's eyes, +i all alone beweep my outcast state, +and trouble deaf heaven with my bootless cries, +and look upon myself and curse my fate, +wishing me like to one more rich in hope, +featured like him, like him with friends possessed, +desiring this man's art and that man's scope, +with what i most enjoy contented least; +yet in these thoughts myself almost despising, +haply i think on thee, and then my state, +(like to the lark at break of day arising +from sullen earth) sings hymns at heaven's gate; +for thy sweet love remembered such wealth brings +that then i scorn to change my state with kings. diff --git a/05_howler/test-outs-lower/the-bustle.txt b/05_howler/test-outs-lower/the-bustle.txt new file mode 100644 index 000000000..a94ffd4a7 --- /dev/null +++ b/05_howler/test-outs-lower/the-bustle.txt @@ -0,0 +1,9 @@ +the bustle in a house +the morning after death +is solemnest of industries +enacted upon earth,-- + +the sweeping up the heart, +and putting love away +we shall not want to use again +until eternity. diff --git a/05_howler/test.py b/05_howler/test.py index 1c04934c8..9139b769c 100755 --- a/05_howler/test.py +++ b/05_howler/test.py @@ -90,3 +90,74 @@ def test_file(): finally: if os.path.isfile(out_file): os.remove(out_file) + + +# -------------------------------------------------- +def test_text_stdout_lower(): + """Test STDIN/STDOUT lower""" + + out = getoutput(f'{prg} "FOO BAR BAZ" -e') + assert out.strip() == 'foo bar baz' + + +# -------------------------------------------------- +def test_text_outfile_lower(): + """Test STDIN/outfile lower""" + + out_file = random_string() + if os.path.isfile(out_file): + os.remove(out_file) + + try: + out = getoutput(f'{prg} {out_flag()} {out_file} "FOO BAR BAZ" --ee') + assert out.strip() == '' + assert os.path.isfile(out_file) + text = open(out_file).read().rstrip() + assert text == 'foo bar baz' + finally: + if os.path.isfile(out_file): + os.remove(out_file) + + +# -------------------------------------------------- +def test_file_lower(): + """Test file in/out""" + + for expected_file in os.listdir('test-outs-lower'): + try: + out_file = random_string() + if os.path.isfile(out_file): + os.remove(out_file) + + basename = os.path.basename(expected_file) + in_file = os.path.join('../inputs', basename) + out = getoutput(f'{prg} {out_flag()} {out_file} {in_file}') + assert out.strip() == '' + produced = open(out_file).read().rstrip() + expected = open(os.path.join('test-outs', + expected_file)).read().strip() + assert expected == produced + finally: + if os.path.isfile(out_file): + os.remove(out_file) + + +# -------------------------------------------------- +def test_multiple_files(): + """Test file in/out""" + + getoutput( + f'{prg} test-outs-lower/preamble.txt test-outs-lower/sonnet-29.txt test-outs-lower/the-bustle.txt' + ) + + for expected_file in os.listdir('test-outs-lower'): + produced = open(os.path.join('output', expected_file)).read().strip() + expected = open(os.path.join('test-outs', + expected_file)).read().strip() + assert expected == produced + + for root, dirs, files in os.walk('output', topdown=False): + for name in files: + os.remove(os.path.join(root, name)) + for name in dirs: + os.rmdir(os.path.join(root, name)) From 309700cb0f93f507ba985757ec6b84ce5baaea37 Mon Sep 17 00:00:00 2001 From: Gabriel Soares Martins Date: Mon, 13 Jan 2025 16:20:47 -0300 Subject: [PATCH 07/12] Finished chapter six --- 06_wc/cat.py | 43 ++++++++++++++++++++ 06_wc/head.py | 54 +++++++++++++++++++++++++ 06_wc/tac.py | 43 ++++++++++++++++++++ 06_wc/tail.py | 59 ++++++++++++++++++++++++++++ 06_wc/test.py | 88 +++++++++++++++++++++++++++++++++++++++++ 06_wc/wc.py | 106 ++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 393 insertions(+) create mode 100755 06_wc/cat.py create mode 100755 06_wc/head.py create mode 100755 06_wc/tac.py create mode 100755 06_wc/tail.py create mode 100755 06_wc/wc.py diff --git a/06_wc/cat.py b/06_wc/cat.py new file mode 100755 index 000000000..c3a621f16 --- /dev/null +++ b/06_wc/cat.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +""" +Author : gmartins +Date : 2025-01-13 +Purpose: Rock the Casbah +""" + +import argparse +import sys + + +# -------------------------------------------------- +def get_args(): + """Get command-line arguments""" + + parser = argparse.ArgumentParser( + description='Emulate cat', + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + + parser.add_argument('file', + metavar='FILE', + nargs='*', + default=[sys.stdin], + help='Input file(s)', + type=argparse.FileType('rt')) + + return parser.parse_args() + + +# -------------------------------------------------- +def main(): + """Make a jazz noise here""" + + args = get_args() + + for fh in args.file: + for line in fh: + print(f'{line}', end='') + + +# -------------------------------------------------- +if __name__ == '__main__': + main() diff --git a/06_wc/head.py b/06_wc/head.py new file mode 100755 index 000000000..62e527a16 --- /dev/null +++ b/06_wc/head.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 +""" +Author : gmartins +Date : 2025-01-13 +Purpose: Rock the Casbah +""" + +import argparse +import sys + + +# -------------------------------------------------- +def get_args(): + """Get command-line arguments""" + + parser = argparse.ArgumentParser( + description='Emulate head', + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + + parser.add_argument('file', + metavar='FILE', + nargs='*', + default=[sys.stdin], + help='Input file(s)', + type=argparse.FileType('rt')) + + parser.add_argument('-n', + '--num-lines', + metavar='num_lines', + default=10, + help='Number of line(s)', + type=int) + + return parser.parse_args() + + +# -------------------------------------------------- +def main(): + """Make a jazz noise here""" + + args = get_args() + + for fh in args.file: + n_line = 0 + for line in fh: + n_line += 1 + if n_line > int(args.num_lines): + break + print(f'{line}', end='') + + +# -------------------------------------------------- +if __name__ == '__main__': + main() diff --git a/06_wc/tac.py b/06_wc/tac.py new file mode 100755 index 000000000..1507b8463 --- /dev/null +++ b/06_wc/tac.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +""" +Author : gmartins +Date : 2025-01-13 +Purpose: Rock the Casbah +""" + +import argparse +import sys + + +# -------------------------------------------------- +def get_args(): + """Get command-line arguments""" + + parser = argparse.ArgumentParser( + description='Emulate tac', + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + + parser.add_argument('file', + metavar='FILE', + nargs='*', + default=[sys.stdin], + help='Input file(s)', + type=argparse.FileType('rt')) + + return parser.parse_args() + + +# -------------------------------------------------- +def main(): + """Make a jazz noise here""" + + args = get_args() + + for fh in args.file: + for line in reversed(list(fh)): + print(f'{line.rstrip()}') + + +# -------------------------------------------------- +if __name__ == '__main__': + main() diff --git a/06_wc/tail.py b/06_wc/tail.py new file mode 100755 index 000000000..7b95b4511 --- /dev/null +++ b/06_wc/tail.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 +""" +Author : gmartins +Date : 2025-01-13 +Purpose: Rock the Casbah +""" + +import argparse +import sys + + +# -------------------------------------------------- +def get_args(): + """Get command-line arguments""" + + parser = argparse.ArgumentParser( + description='Emulate head', + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + + parser.add_argument('file', + metavar='FILE', + nargs='*', + default=[sys.stdin], + help='Input file(s)', + type=argparse.FileType('rt')) + + parser.add_argument('-n', + '--num-lines', + metavar='num_lines', + default=10, + help='Number of line(s)', + type=int) + + return parser.parse_args() + + +# -------------------------------------------------- +def main(): + """Make a jazz noise here""" + + args = get_args() + + n_line = 0 + for fh in args.file: + for line in fh: + n_line += 1 + + n_line0 = 0 + fh.seek(0) + for line in fh: + n_line0 += 1 + if n_line0 <= (n_line - int(args.num_lines)): + continue + print(f'{line}', end='') + + +# -------------------------------------------------- +if __name__ == '__main__': + main() diff --git a/06_wc/test.py b/06_wc/test.py index f9ebc9dea..e105478c7 100755 --- a/06_wc/test.py +++ b/06_wc/test.py @@ -105,3 +105,91 @@ def test_stdin(): rv, out = getstatusoutput(f'{prg} < {fox}') assert rv == 0 assert out.rstrip() == ' 1 9 45 ' + + +# -------------------------------------------------- +def test_char(): + """Test char colum""" + + rv, out = getstatusoutput(f'{prg} {fox} {sonnet} -c') + expected = (' 45 ../inputs/fox.txt\n' + ' 661 ../inputs/sonnet-29.txt\n' + ' 706 total') + assert rv == 0 + assert out.rstrip() == expected + + +# -------------------------------------------------- +def test_line(): + """Test line colum""" + + rv, out = getstatusoutput(f'{prg} {fox} {sonnet} -l') + expected = (' 1 ../inputs/fox.txt\n' + ' 17 ../inputs/sonnet-29.txt\n' + ' 18 total') + assert rv == 0 + assert out.rstrip() == expected + + +# -------------------------------------------------- +def test_word(): + """Test line colum""" + + rv, out = getstatusoutput(f'{prg} {fox} {sonnet} -w') + expected = (' 9 ../inputs/fox.txt\n' + ' 118 ../inputs/sonnet-29.txt\n' + ' 127 total') + assert rv == 0 + assert out.rstrip() == expected + + +# -------------------------------------------------- +def test_line_char(): + """Test char colum""" + + rv, out = getstatusoutput(f'{prg} {fox} {sonnet} -cl') + expected = (' 1 45 ../inputs/fox.txt\n' + ' 17 661 ../inputs/sonnet-29.txt\n' + ' 18 706 total') + assert rv == 0 + assert out.rstrip() == expected + + +# -------------------------------------------------- +def test_cat(): + + rv, out = getstatusoutput(f'cat {sonnet}') + rv0, out0 = getstatusoutput(f'./cat.py {sonnet}') + assert rv == 0 + assert rv0 == 0 + assert out.rstrip() == out0.rstrip() + + +# -------------------------------------------------- +def test_tac(): + + rv, out = getstatusoutput(f'tac {sonnet}') + rv0, out0 = getstatusoutput(f'./tac.py {sonnet}') + assert rv == 0 + assert rv0 == 0 + assert out.rstrip() == out0.rstrip() + + +# -------------------------------------------------- +def test_head(): + + rv, out = getstatusoutput(f'head {sonnet} -n 2') + rv0, out0 = getstatusoutput(f'./head.py {sonnet} -n 2') + assert rv == 0 + assert rv0 == 0 + assert out.rstrip() == out0.rstrip() + + +# -------------------------------------------------- +def test_tail(): + + rv, out = getstatusoutput(f'tail {sonnet} -n 2') + rv0, out0 = getstatusoutput(f'./tail.py {sonnet} -n 2') + assert rv == 0 + assert rv0 == 0 + assert out.rstrip() == out0.rstrip() diff --git a/06_wc/wc.py b/06_wc/wc.py new file mode 100755 index 000000000..23c8065ee --- /dev/null +++ b/06_wc/wc.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python3 +""" +Author : gmartins +Date : 2025-01-13 +Purpose: Rock the Casbah +""" + +import argparse +import sys + + +# -------------------------------------------------- +def get_args(): + """Get command-line arguments""" + + parser = argparse.ArgumentParser( + description='Emulate wc (word count)', + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + + parser.add_argument('file', + metavar='FILE', + nargs='*', + default=[sys.stdin], + help='Input file(s)', + type=argparse.FileType('rt')) + + parser.add_argument('-c', + '--num-chars', + help='Print number of chars', + action='store_true') + + parser.add_argument('-w', + '--num-words', + help='Print number of words', + action='store_true') + + parser.add_argument('-l', + '--num-lines', + help='Print number of lines', + action='store_true') + + return parser.parse_args() + + +def make_str(args, lines_in, words_in, bytes_in, name_in): + c = False + w = False + li = False + + if args.num_chars: + c = True + if args.num_words: + w = True + if args.num_lines: + li = True + + if (not c) and (not w) and (not li): + c = True + w = True + li = True + + S = ' ' + name_in + if c: + S = '{:8}'.format(bytes_in) + S + if w: + S = '{:8}'.format(words_in) + S + if li: + S = '{:8}'.format(lines_in) + S + + return S + + +# -------------------------------------------------- +def main(): + """Make a jazz noise here""" + + args = get_args() + + total_lines = 0 + total_words = 0 + total_bytes = 0 + + for fh in args.file: + num_lines = 0 + num_words = 0 + num_bytes = 0 + for line in fh: + num_lines += 1 + num_words += len(line.split()) + num_bytes += len(line) + + S = make_str(args, num_lines, num_words, num_bytes, fh.name) + print(f'{S}') + + total_lines += num_lines + total_words += num_words + total_bytes += num_bytes + + if len(args.file) > 1: + S = make_str(args, total_lines, total_words, total_bytes, 'total') + print(f'{S}') + + +# -------------------------------------------------- +if __name__ == '__main__': + main() From 499155603950f0539e385ad6a29f2dd74ee65540 Mon Sep 17 00:00:00 2001 From: Gabriel Soares Martins Date: Wed, 15 Jan 2025 11:11:00 -0300 Subject: [PATCH 08/12] Finished chapter 7 --- 07_gashlycrumb/gashlycrumb.py | 51 +++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100755 07_gashlycrumb/gashlycrumb.py diff --git a/07_gashlycrumb/gashlycrumb.py b/07_gashlycrumb/gashlycrumb.py new file mode 100755 index 000000000..5d8a489a6 --- /dev/null +++ b/07_gashlycrumb/gashlycrumb.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +""" +Author : gmartins +Date : 2025-01-13 +Purpose: Learning +""" + +import argparse + + +# -------------------------------------------------- +def get_args(): + """Get command-line arguments""" + + parser = argparse.ArgumentParser( + description='Emulate wc (word count)', + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + + parser.add_argument('letter', + metavar='letter', + nargs='+', + help='Input letter(s)', + type=str) + + parser.add_argument('-f', + '--file', + metavar='FILE', + nargs='?', + default='gashlycrumb.txt', + help='Input file(s)', + type=argparse.FileType('rt')) + + return parser.parse_args() + + +# -------------------------------------------------- +def main(): + """Make a jazz noise here""" + + args = get_args() + fh = args.file + + d = {line[0].upper(): line.rstrip() for line in fh} + + for letter in args.letter: + print(d.get(letter.upper(), f'I do not know "{letter}".')) + + +# -------------------------------------------------- +if __name__ == '__main__': + main() From 03668b34e425d7c5cfd3531c528e2b83dba4d56e Mon Sep 17 00:00:00 2001 From: Gabriel Soares Martins Date: Wed, 15 Jan 2025 11:11:16 -0300 Subject: [PATCH 09/12] Finished chapter 8 --- 08_apples_and_bananas/apples.py | 55 +++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100755 08_apples_and_bananas/apples.py diff --git a/08_apples_and_bananas/apples.py b/08_apples_and_bananas/apples.py new file mode 100755 index 000000000..eaae4cdd6 --- /dev/null +++ b/08_apples_and_bananas/apples.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 +""" +Author : gmartins +Date : 2025-01-13 +Purpose: Rock the Casbah +""" + +import argparse +import os + + +# -------------------------------------------------- +def get_args(): + """Get command-line arguments""" + + parser = argparse.ArgumentParser( + description='Apples and bananas', + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + + parser.add_argument('text', metavar='text', help='Input text or file') + + parser.add_argument('-v', + '--vowel', + metavar='vowel', + help='Vowel to change', + default='a', + choices=list('aieou'), + type=str) + + args = parser.parse_args() + + if os.path.isfile(args.text): + args.text = open(args.text).read().rstrip() + + return args + + +# -------------------------------------------------- +def main(): + """Make a jazz noise here""" + + args = get_args() + + S = args.text + for vowel_t in 'aeiouAEIOU': + to_char = args.vowel + if vowel_t.isupper(): + to_char = to_char.upper() + S = S.replace(vowel_t, to_char) + print(f'{S}') + + +# -------------------------------------------------- +if __name__ == '__main__': + main() From bbfddd9797ba0026e40fdb38e606bcdba71f4b06 Mon Sep 17 00:00:00 2001 From: Gabriel Soares Martins Date: Fri, 17 Jan 2025 18:53:30 -0300 Subject: [PATCH 10/12] Finished chapter nine --- 09_abuse/abuse.py | 149 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100755 09_abuse/abuse.py diff --git a/09_abuse/abuse.py b/09_abuse/abuse.py new file mode 100755 index 000000000..66d6ae087 --- /dev/null +++ b/09_abuse/abuse.py @@ -0,0 +1,149 @@ +#!/usr/bin/env python3 +""" +Author : gmartins +Date : 2025-01-13 +Purpose: Rock the Casbah +""" + +import argparse +import random + + +# -------------------------------------------------- +def get_args(): + """Get command-line arguments""" + + parser = argparse.ArgumentParser( + description='Abuse', + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + + parser.add_argument('-a', + '--adjectives', + metavar='adjectives', + default=2, + help='Number of adjectives', + type=int) + + parser.add_argument('-n', + '--number', + metavar='insults', + default=3, + help='Number of insults', + type=int) + + parser.add_argument('-s', + '--seed', + metavar='seed', + default=None, + help='Random seed', + type=int) + + args = parser.parse_args() + + if args.adjectives < 1: + parser.error(f'--adjectives "{args.adjectives}" must be > 0') + + if args.number < 1: + parser.error(f'--number "{args.number}" must be > 0') + + return args + + +# -------------------------------------------------- +def main(): + """Make a jazz noise here""" + + adjectives = """ + bankrupt + base + caterwauling + corrupt + cullionly + detestable + dishonest + false + filthsome + filthy + foolish + foul + gross + heedless + indistinguishable + infected + insatiate + irksome + lascivious + lecherous + loathsome + lubbery + old + peevish + rascaly + rotten + ruinous + scurilous + scurvy + slanderous + sodden-witted + thin-faced + toad-spotted + unmannered + vile + wall-eyed + """.split() + + nouns = """ + Judas + Satan + ape + ass + barbermonger + beggar + block + boy + braggart + butt + carbuncle + coward + coxcomb + cur + dandy + degenerate + fiend + fishmonger + fool + gull + harpy + jack + jolthead + knave + liar + lunatic + maw + milksop + minion + ratcatcher + recreant + rogue + scold + slave + swine + traitor + varlet + villain + worm + """.split() + + args = get_args() + + random.seed(args.seed) + + for _ in range(args.number): + a = ', '.join(random.sample(adjectives, k = args.adjectives)) + n = ''.join(random.choice(nouns)) + print(f'You {a} {n}!') + + +# -------------------------------------------------- +if __name__ == '__main__': + main() From 5bd3eb8b88e2a9e2a3bc5c6be921b726555bb230 Mon Sep 17 00:00:00 2001 From: Gabriel Soares Martins Date: Fri, 17 Jan 2025 18:53:44 -0300 Subject: [PATCH 11/12] Finished chapter ten --- 10_telephone/telephone.py | 81 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100755 10_telephone/telephone.py diff --git a/10_telephone/telephone.py b/10_telephone/telephone.py new file mode 100755 index 000000000..36a9ffd5f --- /dev/null +++ b/10_telephone/telephone.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python3 +""" +Author : gmartins +Date : 2025-01-13 +Purpose: Rock the Casbah +""" + +import argparse +import random +import os +import string + + +# -------------------------------------------------- +def get_args(): + """Get command-line arguments""" + + parser = argparse.ArgumentParser( + description='Telephone', + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + + parser.add_argument('input', + metavar='input', + type=str, + help='text or file') + + parser.add_argument('-m', + '--mutations', + metavar='mutations', + default=0.1, + help='Percentage of mutations', + type=float) + + parser.add_argument('-s', + '--seed', + metavar='seed', + default=None, + help='Random seed', + type=int) + + args = parser.parse_args() + + if os.path.isfile(args.input): + args.input = open(args.input, 'rt', encoding='utf-8').read().rstrip() + + if args.mutations < 0 or args.mutations > 1: + parser.error(f'--mutations "{args.mutations}" must be between 0 and 1') + + return args + + +# -------------------------------------------------- +def main(): + """Make a jazz noise here""" + + args = get_args() + random.seed(args.seed) + letter_bag = ''.join(sorted(string.ascii_letters + string.punctuation)) + line = args.input + + len_line = len(line) + samples = random.sample(range(len_line), + k=round(args.mutations * len_line)) + + def make_str(new_line, n): + s_b = new_line[:n] + s_a = new_line[n + 1:] + s_c = random.choice(letter_bag.replace(new_line[n], '')) + return s_b + s_c + s_a + + new_line = line + for n in samples: + new_line = make_str(new_line, n) + + print(f'You said: "{line.rstrip()}"') + print(f'I heard : "{new_line.rstrip()}"') + + +# -------------------------------------------------- +if __name__ == '__main__': + main() From ec8c5c7c298db9c6eaee770fe58a96530e471b54 Mon Sep 17 00:00:00 2001 From: Gabriel Soares Martins Date: Fri, 17 Jan 2025 18:58:03 -0300 Subject: [PATCH 12/12] Finished chapter ten --- 10_telephone/telephone.py | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/10_telephone/telephone.py b/10_telephone/telephone.py index 36a9ffd5f..f063b7e43 100755 --- a/10_telephone/telephone.py +++ b/10_telephone/telephone.py @@ -62,18 +62,12 @@ def main(): samples = random.sample(range(len_line), k=round(args.mutations * len_line)) - def make_str(new_line, n): - s_b = new_line[:n] - s_a = new_line[n + 1:] - s_c = random.choice(letter_bag.replace(new_line[n], '')) - return s_b + s_c + s_a - - new_line = line + new_line = list(line) for n in samples: - new_line = make_str(new_line, n) + new_line[n] = random.choice(letter_bag.replace(new_line[n], '')) - print(f'You said: "{line.rstrip()}"') - print(f'I heard : "{new_line.rstrip()}"') + print('You said: "{}"\nI heard : "{}"\n'.format( + line.rstrip(), ''.join(new_line).rstrip())) # --------------------------------------------------