Time-boxing a cat

My next challenge in Learn More Python The Hard Way is to implement my own version of the Unix cat command.

Similarly to the previous exercise argparse, this exercise is also about refining your project start up techniques, not about fully functional delivery. As noted in the above blog post, I made some observations about my attempt at this first hack, and decided to focus this session on my list-making and commenting, before I started to write any code.

So I started my Pomodoro timer and set off….

Research and notes #

First of all I created a file called challenge.txt where I started to write a quick TODO list of activities for the project. Even though I suspected it would be a fairly short list, I also needed a file to run my cat against later so it was worth producing.

The list contained there items:

  1. read man cat
  2. quick research of POSIX
  3. implement cat clone into mycat.py

After a quick read of man cat I decided to copy the content into my challenge.txt file for reference and to flesh it out. I skimmed through a wiki article on POSIX and after establishing it’s an API convention, decided it might be a little difficult in the timeframe, so moved onto the mycat.py file.

Improved commenting #

One of the improvements I hoped to make on this challenge was to enhance my code comments, that would pave the way for pseudo code, and eventually my working code.

I had already decided to use argparse again, so I laid out my comments in sequence, trying to consider the processes and sub-processes required.

# create file
# import argparse
# create parser
# create positional argument for input file(s)
# parse arguments to instance
# read file contents as args
# print args

I had already creating the mycat.py file to write the comments in, so I proceeded to import argparse, create a parser with a short description and create a simple positional argument for the file I’d be importing.

I was careful to avoid naming the argument file as this is a Python keyword. I also remembered to include the parse_args() function this time that I omitted in the last hack.

# import argparse
import argparse
# create parser
parser = argparse.ArgumentParser(description="simple clone of cat")
# create positional argument for input file(s)
parser.add_argument("filename", help='list file(s) to concatenate')
# parse arguments to instance
args = parser.parse_args()
# read file contents as args
# print args

This was a lot of the setup done and a quick test showed no errors. I could now include a filename after my script that I would need to open, read and print. And if I was stuck, I could always call –help.

The last two comments seemed pretty straight-forward so I opted for:

# read file contents as args
lines = args.filename.read()
# print args
print(lines)

Python didn’t like this and warned me off.

AttributeError: 'str' object has no attribute 'read'

It seems that when argparse was reading the filename input on the command line, it was not recognising that this was a file not a string. I had to tell argparse otherwise.

I remembered from my previous exercise and reading the docs quite extensively, that you could specific types in the add_argument parameters.
A quick check of the docs revealed that I could include FileType which should overcome the string issue.

# create positional argument for input file(s)
parser.add_argument("filename", type=argparse.FileType('r'),
                    help='list file(s) to concatenate')

Now when I ran the script with the text file as an argument, it worked!

python mycat.py challenge.txt

Screenshot 2018-12-03 at 19.45.15.png

Enhancing the script #

Looking at the output reminded me that cat also has a number of optional arguments and given I had five minutes left, I thought I’d try and include one into mycat.py.

cat suggested a lot of formatting options so I decided to add a pretty option. I wasn’t bothered about what this would do, so instead of reading the whole file, I’d read each line. I also included some logic to the print statement to call the pretty argument.

# add an optional to make it prettier
parser.add_argument("-p", "--pretty", help="adds a new line for each parsed line")
...
...
lines = args.filename.readlines()
# add logic to test for optional argument and print
if args.pretty:
    for line in lines:
        print(line)
else:
    print(lines)

Now when I ran the script, it printed the output in one long list:
Screenshot 2018-12-03 at 20.33.58.png

And now when I ran the script with the optional argument ‘-p’ or ‘–pretty’, I should see each line of the text separated. Only I didn’t. I saw one of two errors, depending on where I put the optional argument.
Screenshot 2018-12-03 at 20.36.30.png

PING!! Time up on my second Pomodoro! Arrrggghhh……..

I couldn’t leave it here so I took a few extra minutes to look at what was going on. On closer inspection, my logic test at the print stage didn’t have a value to test against. I had omitted to store a true value to the pretty optional argument, which would be tested against when printing.

# add an optional to make it prettier
parser.add_argument("-p", "--pretty", action="store_true",
                    help="adds a new line for each parsed line")

Now my could be made prettier with the optional argument! And I was done.
Screenshot 2018-12-03 at 20.46.33.png

Study Drills #

There were a few extra drills to consider as part of my review.

Were there any surprising features of cat that you have never used or were difficult to implement?

Yes. I had never actually looked at man cat before and wasn’t aware there were so many optional argument. I need to look at man a lot more in future.

Were you able to remove one friction blocker from your starting process?

Yes. Writing more detailed comments about process rather than results helped a lot. But I still didn’t actually write any pseudo code. I need to try this specifically on the next exercise.

Did you identify more things that get in the way?

Yes. I’m usually hacking on the train whilst commuting. This is not a good working environment in terms of distractions or interruptions. The posture is not great either, so Im going to perform the next exercise in a completely different environment for contrast, and probably record it.

 
0
Kudos
 
0
Kudos

Now read this

Inheritance: trying to keep DRY

Inheritance. The word alone fills me with expectation and a sense that I can benefit from the hard graft of others! There will be some price to pay, lets call it an inheritance tax, but once I’ve paid that inevitable price, I can reap... Continue →