London | 26-SDC-Mar | Khilola Rustamova| Sprint 4 |Implement shell tools python#537
London | 26-SDC-Mar | Khilola Rustamova| Sprint 4 |Implement shell tools python#537HilolaRustam wants to merge 8 commits into
Conversation
illicitonion
left a comment
There was a problem hiding this comment.
This is generally looking good, and like things work :) I left a few style comments, and many of my comments to one project apply to the others as well.
| return sorted(files) | ||
|
|
||
| def read_lines(file): | ||
| with open(file, "r", encoding="utf-8") as f: |
There was a problem hiding this comment.
This block is more indented than the others - why is that?
There was a problem hiding this comment.
That was an accident, i have aligned it.
| try: | ||
| lines = read_lines(file) | ||
| except FileNotFoundError: | ||
| print(f"cat: {file}: No such file or directory", file=sys.stderr) |
There was a problem hiding this comment.
Good job printing to stderr not stdout :)
However! If you ran into an issue here, I think your program will still exit with exit code 0. What exit code do you think it should exit with?
There was a problem hiding this comment.
You're right. A missing file should result in a non-zero exit code to indicate an error. I've updated the code to exit with code 1 if any file cannot be read.
| sys.stdout.write(prefix + line) | ||
|
|
||
|
|
||
| def main(): |
There was a problem hiding this comment.
This all works, but often people will have their main function at the top of the file so it's the first thing you read, and allow you to scroll down to read details if needed. You don't need to follow this, but it can often make it easier to read/review code.
There was a problem hiding this comment.
Thank you, I've moved main() higher in the file to make the program flow easier to see.
| paths.append(a) | ||
|
|
||
| files = expand_paths(paths) | ||
| print_lines(files, number_all,number_nonempty) |
There was a problem hiding this comment.
I've noticed your whitespace is a bit inconsistent (e.g. no space after this ,) - you may want investigate a tool like black to help automate fixing this.
There was a problem hiding this comment.
Thank you for your suggestion. I've cleaned up the whitespace issues and I'll take a look at Black for automatic formatting
| number_all = True | ||
| elif a == "-b": | ||
| number_nonempty = True | ||
| number_all = False #-b overrides -n |
There was a problem hiding this comment.
What would happen if -b appeared before -n in the command line?
There was a problem hiding this comment.
I've changed the flag handling so -b always takes precedence regardless of argument order.
| paths.append(a) | ||
|
|
||
| files = expand_paths(paths) | ||
| print_lines(files, number_all,number_nonempty) |
There was a problem hiding this comment.
We often used named arguments in Python when we have multiple arguments of the same type, to make sure we don't accidentally pass them in the wrong order. If you wrote print_lines(files, number_nonempty, number_all) here it wouldn't be obvious that the order was swapped. Instead we can write:
print_lines(files, number_all=number_all, number_nonempty=number_nonempty)and if we swapped them by mistake it would be obvious.
There was a problem hiding this comment.
That makes sense. I've updated the call to use named arguments
| def list_dir(path, show_all=False, one_per_line = False): | ||
| try: | ||
| entries = os.listdir(path) | ||
| except FileNotFoundError: |
There was a problem hiding this comment.
Same question as above about exit codes.
illicitonion
left a comment
There was a problem hiding this comment.
This looks really good, well done!
| for a in args: | ||
| if a == "-n": | ||
| number_all = True | ||
| elif a == "-b": | ||
| number_nonempty = True | ||
| else: | ||
| paths.append(a) | ||
|
|
||
| if number_nonempty: | ||
| number_all = False |
There was a problem hiding this comment.
This kind of relationship where different options/states are related but exclusive can be hard to follow. (Here, it should never be the case that number_all and number_nonempty are both True - it's an invalid state in the program).
Instead of this, we sometimes use enums for this (or can use strings as enums). Consider a single variable number_mode which could be set to either "none", "non_empty" or "all" - here we don't need to think about what both True mean - we just have one variable which could have one of three variables. What do you think about this?
|
|
||
| return line_count, word_count, byte_count | ||
|
|
||
| def expand(paths): |
There was a problem hiding this comment.
In general you shouldn't need to do this kind of glob expansion yourself - the shell should do this for you and expand the globs into different file names when calling your program. You can test this out by printing what sys.argv is when you run python3 my-wc.py some*glob - look at what arguments you actually get :)
| try: | ||
| l, w, c = count_file(file) | ||
| except FileNotFoundError: | ||
| print(f"wc: {file}: No such file or directory", file=sys.stderr) |
There was a problem hiding this comment.
What exit code will you exit with if you failed to read one file?
Learners, PR Template
Self checklist
Changelist
implementation of cat, ls and wc with python