From 1620303fa1cdd57aad43dbf715a4f1f921a7bd69 Mon Sep 17 00:00:00 2001 From: Maksim Kiryanov Date: Thu, 7 Mar 2019 18:35:51 +0300 Subject: [PATCH 1/5] change logic for stat gathering --- ch05_modules/ls.py | 57 +++++++++++++++++++++++++++------------------- 1 file changed, 33 insertions(+), 24 deletions(-) diff --git a/ch05_modules/ls.py b/ch05_modules/ls.py index 842c4c4..50d1b19 100755 --- a/ch05_modules/ls.py +++ b/ch05_modules/ls.py @@ -1,51 +1,60 @@ #!/usr/bin/env python3 import os -from pprint import pprint def is_hidden_path(path): return path.startswith(".") +def by_path_name(stat_entry): + return stat_entry[0] + + def main(): # TODO: sanitize input roots - expanduser, expandvars, abspath - roots = ["/home/mkiryanov/", ".local/"] + root_names = ["/home/mkiryanov/", "~/.local/"] + expanded_roots_iter = zip(root_names, + [os.path.abspath(os.path.expanduser(os.path.expandvars(r))) for r in root_names]) - recursive = True + recursive = False show_hidden = False stats = {} if not recursive: - for root in roots: - abs_root = os.path.abspath(root) - for path in os.listdir(root): + for root_name, root_abs_path in expanded_roots_iter: + for path in os.listdir(root_abs_path): if not show_hidden and is_hidden_path(path): continue - abs_path = os.path.join(abs_root, path) - data = None - if not os.path.isdir(abs_path): - stat = os.stat(abs_path) - rel_path = os.path.join(root, path) - data = (rel_path, stat.st_size, stat.st_mtime_ns) - stats.update({abs_path: data}) + file_abs_path = os.path.join(root_abs_path, path) + path_name = os.path.join(root_name, path) + if not os.path.isdir(file_abs_path): + stat = os.stat(file_abs_path) + data = (path_name, True, stat.st_size, stat.st_mtime_ns) + else: + data = (path_name, False, None, None) + stats.update({file_abs_path: data}) else: - for root in roots: - for root, dirnames, filenames in os.walk(root): - abs_root = os.path.abspath(root) + for root_name, root_abs_path in expanded_roots_iter: + for root, dirnames, filenames in os.walk(root_abs_path): for path in dirnames: if not show_hidden and is_hidden_path(path): continue - stats.update({os.path.join(abs_root, path): None}) + path_name = os.path.join(root, path) + stats.update({path: (path_name, False, None, None)}) for path in filenames: if not show_hidden and is_hidden_path(path): continue - abs_path = os.path.join(abs_root, path) - stat = os.stat(abs_path, follow_symlinks=False) - rel_path = os.path.join(root, path) - data = (rel_path, stat.st_size, stat.st_mtime_ns) - stats.update({abs_path: data}) - - pprint(stats.values()) + file_abs_path = os.path.join(root, path) + stat = os.stat(file_abs_path) + path_name = os.path.join(root, path) + data = (path_name, True, stat.st_size, stat.st_mtime_ns) + stats.update({file_abs_path: data}) + + for path_name, stat in sorted(stats.items(), key=by_path_name): + if stat[1]: + print(stat[0], stat[2], stat[3]) + else: + print(stat[0]) main() From 5ab041a9a8d675f8ee5a749eef5d72a328d0f497 Mon Sep 17 00:00:00 2001 From: Maksim Kiryanov Date: Fri, 8 Mar 2019 00:32:58 +0300 Subject: [PATCH 2/5] refactor gathering fs stats --- ch05_modules/ls.py | 97 ++++++++++++++++++++++++++-------------------- 1 file changed, 55 insertions(+), 42 deletions(-) diff --git a/ch05_modules/ls.py b/ch05_modules/ls.py index 50d1b19..33b91fb 100755 --- a/ch05_modules/ls.py +++ b/ch05_modules/ls.py @@ -1,60 +1,73 @@ #!/usr/bin/env python3 import os +import sys + +FULL_PATH, IS_FILE, FILE_SIZE, FILE_MTIME = range(4) def is_hidden_path(path): return path.startswith(".") -def by_path_name(stat_entry): - return stat_entry[0] +def by_path_name(stat_item): + return stat_item[0] -def main(): - # TODO: sanitize input roots - expanduser, expandvars, abspath - root_names = ["/home/mkiryanov/", "~/.local/"] - expanded_roots_iter = zip(root_names, - [os.path.abspath(os.path.expanduser(os.path.expandvars(r))) for r in root_names]) +def gather_fs_stats_simply(root, show_hidden): + stats = [] + for filename in os.listdir(root): + if not show_hidden and is_hidden_path(filename): + continue + full_path = os.path.join(root, filename) + if not os.path.isdir(full_path): + stat = os.stat(full_path, follow_symlinks=False) + data = (full_path, True, stat.st_size, stat.st_mtime_ns) + else: + data = (full_path, False, None, None) + stats.append(data) + return stats + + +def gather_fs_stats_recursively(root_name, show_hidden): + stats = [] + for root, dirnames, filenames in os.walk(root_name): + for path in dirnames: + if not show_hidden and is_hidden_path(path): + continue + full_path = os.path.join(root, path) + stats.append((full_path, False, None, None)) + for path in filenames: + if not show_hidden and is_hidden_path(path): + continue + full_path = os.path.join(root, path) + stat = os.stat(full_path, follow_symlinks=False) + stats.append((full_path, True, stat.st_size, stat.st_mtime_ns)) + return stats - recursive = False - show_hidden = False - stats = {} +def gather_fs_stats(root_names, recursive, show_hidden): + stats = [] if not recursive: - for root_name, root_abs_path in expanded_roots_iter: - for path in os.listdir(root_abs_path): - if not show_hidden and is_hidden_path(path): - continue - file_abs_path = os.path.join(root_abs_path, path) - path_name = os.path.join(root_name, path) - if not os.path.isdir(file_abs_path): - stat = os.stat(file_abs_path) - data = (path_name, True, stat.st_size, stat.st_mtime_ns) - else: - data = (path_name, False, None, None) - stats.update({file_abs_path: data}) + for root_name in root_names: + stats.extend(gather_fs_stats_simply(root_name, show_hidden)) else: - for root_name, root_abs_path in expanded_roots_iter: - for root, dirnames, filenames in os.walk(root_abs_path): - for path in dirnames: - if not show_hidden and is_hidden_path(path): - continue - path_name = os.path.join(root, path) - stats.update({path: (path_name, False, None, None)}) - for path in filenames: - if not show_hidden and is_hidden_path(path): - continue - file_abs_path = os.path.join(root, path) - stat = os.stat(file_abs_path) - path_name = os.path.join(root, path) - data = (path_name, True, stat.st_size, stat.st_mtime_ns) - stats.update({file_abs_path: data}) - - for path_name, stat in sorted(stats.items(), key=by_path_name): - if stat[1]: - print(stat[0], stat[2], stat[3]) + for root_name in root_names: + stats.extend(gather_fs_stats_recursively(root_name, show_hidden)) + return stats + +def main(): + root_names = sys.argv[1:] + + recursive = False + show_hidden = True + + stats = gather_fs_stats(root_names, recursive, show_hidden) + + for stat in sorted(stats, key=by_path_name): + if stat[IS_FILE]: + print(stat[FULL_PATH], stat[FILE_SIZE], stat[FILE_MTIME]) else: - print(stat[0]) + print(stat[FULL_PATH]) main() From 5bec82aecaecef893f2b7dd2cf83cd8018847d36 Mon Sep 17 00:00:00 2001 From: Maksim Kiryanov Date: Sat, 9 Mar 2019 17:43:26 +0300 Subject: [PATCH 3/5] implement stat printing --- ch05_modules/ls.py | 64 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 56 insertions(+), 8 deletions(-) diff --git a/ch05_modules/ls.py b/ch05_modules/ls.py index 33b91fb..e00b931 100755 --- a/ch05_modules/ls.py +++ b/ch05_modules/ls.py @@ -1,6 +1,8 @@ #!/usr/bin/env python3 +import locale import os import sys +from datetime import datetime FULL_PATH, IS_FILE, FILE_SIZE, FILE_MTIME = range(4) @@ -10,7 +12,17 @@ def is_hidden_path(path): def by_path_name(stat_item): - return stat_item[0] + return stat_item[FULL_PATH] + + +def by_file_size(stat_item): + size = stat_item[FILE_SIZE] + return size if size else 0 + + +def by_file_mtime(stat_item): + mtime = stat_item[FILE_MTIME] + return mtime if mtime else 0 def gather_fs_stats_simply(root, show_hidden): @@ -21,7 +33,7 @@ def gather_fs_stats_simply(root, show_hidden): full_path = os.path.join(root, filename) if not os.path.isdir(full_path): stat = os.stat(full_path, follow_symlinks=False) - data = (full_path, True, stat.st_size, stat.st_mtime_ns) + data = (full_path, True, stat.st_size, stat.st_mtime) else: data = (full_path, False, None, None) stats.append(data) @@ -41,7 +53,7 @@ def gather_fs_stats_recursively(root_name, show_hidden): continue full_path = os.path.join(root, path) stat = os.stat(full_path, follow_symlinks=False) - stats.append((full_path, True, stat.st_size, stat.st_mtime_ns)) + stats.append((full_path, True, stat.st_size, stat.st_mtime)) return stats @@ -55,6 +67,43 @@ def gather_fs_stats(root_names, recursive, show_hidden): stats.extend(gather_fs_stats_recursively(root_name, show_hidden)) return stats + +def print_stats(stats, show_size, show_time, locale_id): + locale.setlocale(locale.LC_ALL, locale_id) + + print_template = "{name: Date: Sat, 9 Mar 2019 17:45:47 +0300 Subject: [PATCH 4/5] fix recursively file walk --- ch05_modules/ls.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ch05_modules/ls.py b/ch05_modules/ls.py index e00b931..cb8bed6 100755 --- a/ch05_modules/ls.py +++ b/ch05_modules/ls.py @@ -54,6 +54,8 @@ def gather_fs_stats_recursively(root_name, show_hidden): full_path = os.path.join(root, path) stat = os.stat(full_path, follow_symlinks=False) stats.append((full_path, True, stat.st_size, stat.st_mtime)) + + dirnames[:] = [dir for dir in dirnames if not is_hidden_path(dir)] return stats From e622e62fa7324b9e4817ed2de044e0fe14ecf232 Mon Sep 17 00:00:00 2001 From: Maksim Kiryanov Date: Sat, 9 Mar 2019 19:03:51 +0300 Subject: [PATCH 5/5] add options processing --- ch05_modules/ls.py | 46 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 36 insertions(+), 10 deletions(-) diff --git a/ch05_modules/ls.py b/ch05_modules/ls.py index cb8bed6..bbc8eca 100755 --- a/ch05_modules/ls.py +++ b/ch05_modules/ls.py @@ -1,8 +1,8 @@ #!/usr/bin/env python3 import locale import os -import sys from datetime import datetime +from optparse import OptionParser FULL_PATH, IS_FILE, FILE_SIZE, FILE_MTIME = range(4) @@ -106,18 +106,44 @@ def print_stats(stats, show_size, show_time, locale_id): dir_count, "y" if dir_count == 1 else "ies")) -def main(): - root_names = sys.argv[1:] - - recursive = False - show_hidden = True +def resolve_order_func(order_option): + if order_option in ("name", "n"): + return by_path_name + elif order_option in ("modified", "m"): + return by_file_mtime + else: + return by_file_size + + +def process_options(): + usage = ("%prog [options] [path1 [path2 [... pathN]]]\n" + "The paths are optional; if not given . is used.") + parser = OptionParser(usage=usage) + parser.add_option("-H", "--hidden", + action="store_true", dest="show_hidden", default=False, + help="show hidden files [default: off]") + parser.add_option("-m", "--modified", + action="store_true", dest="show_time", default=False, + help="show last modified date/time [default: off]") + parser.add_option("-o", "--order", + type="choice", choices=["name", "n", "modified", "m", "size", "s"], default="name", + help="order by ('name', 'n', 'modified', 'm', 'size', 's') [default: name]") + parser.add_option("-r", "--recursive", + action="store_true", dest="recursive", default=False, + help="recurse into subdirectories [default: off]") + parser.add_option("-s", "--sizes", + action="store_true", dest="show_size", default=False, + help="show sizes [default: off]") + return parser.parse_args() - stats = gather_fs_stats(root_names, recursive, show_hidden) - show_size = True - show_time = True +def main(): + (options, args) = process_options() - print_stats(sorted(stats, key=by_file_size), show_size, show_time, "ru_RU.UTF-8") + root_names = args if args else "." + stats = gather_fs_stats(root_names, options.recursive, options.show_hidden) + print_stats(sorted(stats, key=resolve_order_func(options.order)), + options.show_size, options.show_time, "ru_RU.UTF-8") main()