Zadanie 1.3 omówienie rozwiązania - WykopWyzwanie/wykop_wyzwaniepython GitHub Wiki

##Zadanie 1.3

Link do rozwiązania

Do rozwiązania potrzebne będą 4 biblioteki - os, shutil, datetime oraz glob.

import os
import shutil
from datetime import datetime
from glob import glob
from os.path import abspath, getctime, getmtime, getsize, isdir, isfile

-- Następnie tworzona jest klasa CmdHandlers, ale nie należy dać się zmylić - ten program nie ma nic wspólnego z paradygmatem obiektowym. Klasa jest tu używana jako namespace - w praktyce lepiej gdyby jej zawartość była po prostu umieszczona w osobnym module.

class CmdHandlers():

-- Przyjętą konwencją jest nazywanie metody obsługującej daną komendę nazwą obsługiwanej komendy. Metoda obsługująca komendę przyjmuje takie same argumenty jak obsługiwana komenda według specyfikacji. Jak widać 6 z 8 handlerów to funkcje jednolinijkowe używające odpowiednich elementów bibliotek.

class CmdHandlers():
    def pwd():
        return os.getcwd()

    def cd(path):
        os.chdir(path)

    def mv(src, dst):
        shutil.move(src, dst)

    def ls():
        return '\n'.join(os.listdir(os.getcwd()))

    def touch(path):
        with open(path, 'w+'):
            pass

    def cp(src, dst):
        shutil.copy2(src, dst) if isfile(src) else shutil.copytree(src, dst)

    def rm(path):
        os.remove(path) if isfile(path) else shutil.rmtree(path)

-- Trochę ciekawsza jest tylko obsługa komendy info, ponieważ informacje wypisywane dla folderów różnią się od informacji wypisywanych dla plików. Wartości odpowiednich pól (a także informacja czy to pole ma być wyświetlane) są generowane dynamicznie.

    def info(path):
        filesizes = CmdHandlers._lst_file_sizes(path) if isdir(path) else [getsize(path)]
        return '\n'.join('{}: {}'.format(name, value) for (name, value) in [
            ('typ', 'plik' if isfile(path) else 'katalog' if isdir(path) else 'inny'),
            ('sciezka', abspath(path)),
            ('rozmiar', '{}B'.format(sum(filesizes))),
            ('liczba_plikow', sum(isfile(f) for f in os.listdir()) if isdir(path) else None),
            ('ctime', datetime.fromtimestamp(getctime(path)).date()),
            ('mtime', datetime.fromtimestamp(getmtime(path)).date()),
        ] if value is not None)

    def _lst_file_sizes(path):
        return list(getsize(f) for f in glob("{}/**".format(path), recursive=True) if isfile(f))

-- Funkcja parse(cmd, *args, **kwargs) działa w bardzo prosty sposób. Próbuje ona wyciągnąć funkcję obsługującą daną komendę z namespace CmdHandlers, a jeśli się to nie uda to funkcją obsługującą jest pusta lambda, czyli w praktyce komenda zostaje pominięta. Po wyciągnięciu odpowiedniej funkcji obsługującej jest ona wywoływana z argumentami przekazanymi jako *args i **kwargs.

def parse(cmd, *args, **kwargs):
    return CmdHandlers.__dict__.get(cmd, lambda *a, **kw: None)(*args, **kwargs)

-- Funkcja main odpowiada za interaktywną obsługę wpisywanych komend. To co wpisze użytkownik jest dzielone na komendę i jej argumenty, a następnie używając tych wartości wywoływana jest funkcja parse. Jeżeli parse zwróci jakiś napis jest on wypisywany na konsolę, w przeciwnym wypadku wypisywana jest pusta linia oznaczająca zakończenie wykonywania polecenia. Aby opuścić tryb interaktywny należy wpisać komendę exit.

def main():
    command, args = '', []
    while command != 'exit':
        command, *args = input().split()
        print(parse(command, *args) or '')


if __name__ == '__main__':
    main()