Tree command for upysh

C programming, build, interpreter/VM.
Target audience: MicroPython Developers.
Post Reply
cgglzpy
Posts: 32
Joined: Thu Jul 18, 2019 4:20 pm

Tree command for upysh

Post by cgglzpy » Tue Jan 07, 2020 8:41 pm

Hi I made TREE class for upysh (using LS as a template)

So doing

Code: Select all

>>> from upysh import *
Then you will be able to do this

Code: Select all

>>> tree
  ATEXTFILE.txt
  THETESTCODE.py
  my_other_dir_sync <dir>
        └────  another_file.txt
  new_tree_test_dir <dir>
        ├────  example_code.py
        ├────  foo_file.txt
        ├────  sub_foo_test_dir <dir>
        │    ├────  file_code.py
        │    └────  foo2.txt
        ├────  w_name_dir <dir>
        │    └────  dummy_file.txt
        └────  zfile.py
  test_subdir_sync <dir>
        ├────  SUBTEXT.txt
        └────  sub_sub_dir_test_sync <dir>
             ├────  level_2_subtext.txt
             └────  level_3_subtext.txt
6 directories, 12 files
The class is this one:

Code: Select all

class TREE:

    def __repr__(self):
        self.__call__()
        return ""

    def __call__(self, path=".", level=0, is_last=False, is_root=True,
                 carrier="    "):
        l = os.listdir(path)
        nf = len([file for file in os.listdir(path) if not os.stat(file)[0] & 0x4000])
        nd = len(l) - nf
        ns_f, ns_d = 0, 0
        l.sort()
        if len(l) > 0:
            last_file = l[-1]
        else:
            last_file = ''
        for f in l:
            st = os.stat("%s/%s" % (path, f))
            if st[0] & 0x4000:  # stat.S_IFDIR
                print(self._treeindent(level, f, last_file, is_last=is_last, carrier=carrier) + "  %s <dir>" % f)
                os.chdir(f)
                level += 1
                lf = last_file == f
                if level > 1:
                    if lf:
                        carrier += "     "
                    else:
                        carrier += "    │"
                ns_f, ns_d = self.__call__(level=level, is_last=lf,
                                           is_root=False, carrier=carrier)
                if level > 1:
                    carrier = carrier[:-5]
                os.chdir('..')
                level += (-1)
                nf += ns_f
                nd += ns_d
            else:
                print(self._treeindent(level, f, last_file, is_last=is_last, carrier=carrier) + "  %s" % (f))
        if is_root:
            print('{} directories, {} files'.format(nd, nf))
        else:
            return (nf, nd)

    def _treeindent(self, lev, f, lastfile, is_last=False, carrier=None):
        if lev == 0:
            return ""
        else:
            if f != lastfile:
                return carrier + "    ├────"
            else:
                return carrier + "    └────"


tree = TREE()
If anyone is interested/this is good enough I guess I could do a pull request to micropython-lib?

Cheers

User avatar
rcolistete
Posts: 185
Joined: Thu Dec 31, 2015 3:12 pm
Location: Brazil

Re: Tree command for upysh

Post by rcolistete » Tue Jan 07, 2020 11:44 pm

Nice ! Yes, please (for the pull request).

User avatar
pythoncoder
Posts: 3935
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: Tree command for upysh

Post by pythoncoder » Wed Jan 08, 2020 7:49 am

That is very useful indeed, especially for those who use Python packages. :D

On a Pyboard with an SD card there is a problem: it lists the contents of .Trash-1000 which (on my SD card) are huge. It would be good if there were some way to disable this - an option to suppress hidden files/directories perhaps?

If you submit a PR I suggest you update the man command.
Peter Hinch

User avatar
devnull
Posts: 440
Joined: Sat Jan 07, 2017 1:52 am
Location: Singapore / Cornwall
Contact:

Re: Tree command for upysh

Post by devnull » Wed Jan 08, 2020 7:51 am

Nice, just included it in my custom sh file :-)

shaoziyang
Posts: 298
Joined: Sun Apr 17, 2016 1:55 pm

Re: Tree command for upysh

Post by shaoziyang » Wed Jan 08, 2020 8:23 am

It is useful.

cgglzpy
Posts: 32
Joined: Thu Jul 18, 2019 4:20 pm

Re: Tree command for upysh

Post by cgglzpy » Wed Jan 08, 2020 5:42 pm

Hi, thanks

I've just submitted the PR
pythoncoder wrote:
Wed Jan 08, 2020 7:49 am
That is very useful indeed, especially for those who use Python packages. :D

On a Pyboard with an SD card there is a problem: it lists the contents of .Trash-1000 which (on my SD card) are huge. It would be good if there were some way to disable this - an option to suppress hidden files/directories perhaps?

If you submit a PR I suggest you update the man command.
And yes , there is still room for improvement like the option to suppress hidden files/directories you mention or maybe max display depth of the directory tree for example.

That's definitely in the TODO list, and if anyone wants to contribute here is the fork https://github.com/Carglglz/micropython-lib

Cheers

User avatar
pythoncoder
Posts: 3935
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: Tree command for upysh

Post by pythoncoder » Wed Jan 08, 2020 6:28 pm

On a quick test the Linux implementation of tree ignores hidden files and directories. So I suggest changing this line to:

Code: Select all

l = [x for x in os.listdir(path) if not x.startswith('.')]
That fixes the problem here.
Peter Hinch

cgglzpy
Posts: 32
Joined: Thu Jul 18, 2019 4:20 pm

Re: Tree command for upysh

Post by cgglzpy » Thu Jan 09, 2020 3:42 pm

pythoncoder wrote:
Wed Jan 08, 2020 6:28 pm
On a quick test the Linux implementation of tree ignores hidden files and directories. So I suggest changing this line to:

Code: Select all

l = [x for x in os.listdir(path) if not x.startswith('.')]
That fixes the problem here.
I came to the same conclusion yesterday after a quick look so it ignores the hidden files and directories by default (with a 'hidden' option) the class is like this now:

Code: Select all

def __call__(self, path=".", level=0, is_last=False, is_root=True,
                 carrier="    ", hidden=False):
        if hidden:
            l = os.listdir(path)
        else:
            l = [f for f in os.listdir(path) if not f.startswith('.')]
I will modify the PR then, thanks for the idea :)

cgglzpy
Posts: 32
Joined: Thu Jul 18, 2019 4:20 pm

Re: Tree command for upysh

Post by cgglzpy » Fri Jan 17, 2020 4:56 pm

Hi, I made a 'display disk usage statistics' / 'du' command too, in case anyone is interested...

It's like 'ls' command but size is in 'Human-readable' output, by default do not list hidden files/dirs, it prints also size of dirs and
it has an option for the depth level (default 0) e.g.

Code: Select all

>>> du
8.3 KB    ./test_subdir_sync <dir>
177 by    ./new_tree_test_dir <dir>
0 by      ./my_other_dir_sync <dir>
122 by    ./ATEXTFILE.txt
302 by    ./THETESTCODE.py
or

Code: Select all

>>> du(max_dlev=2)
16 by     ./test_subdir_sync/SUBTEXT.txt
42 by     ./test_subdir_sync/sub_sub_dir_test_sync/level_3_subtext.txt
8.3 KB    ./test_subdir_sync/sub_sub_dir_test_sync/level_2_subtext.txt
0 by      ./new_tree_test_dir/zfile.py
56 by     ./new_tree_test_dir/w_name_dir/dummy_file.txt
29 by     ./new_tree_test_dir/foo_file.txt
92 by     ./new_tree_test_dir/example_code.py
0 by      ./new_tree_test_dir/sub_foo_test_dir/foo2.txt
0 by      ./new_tree_test_dir/sub_foo_test_dir/file_code.py
0 by      ./my_other_dir_sync/another_file.txt
122 by    ./ATEXTFILE.txt
302 by    ./THETESTCODE.py
The class is this one

Code: Select all

class DISK_USAGE:

    def __repr__(self):
        self.__call__()
        return ""

    def __call__(self, path=".", dlev=0, max_dlev=0, hidden=False):
        if path != ".":
                if not os.stat(path)[0] & 0x4000:
                    print('{:9} {}'.format(self.print_filesys_info(os.stat(path)[6]), path))
                else:
                    if hidden:
                        resp = {path+'/'+dir: os.stat(path+'/'+dir)[6] for dir in os.listdir(path)}
                    else:
                        resp = {path+'/'+dir: os.stat(path+'/'+dir)[6] for dir in os.listdir(path) if not dir.startswith('.')}
                    for dir in resp.keys():

                        if not os.stat(dir)[0] & 0x4000:
                            print('{:9} {}'.format(self.print_filesys_info(resp[dir]), dir))

                        else:
                            if dlev < max_dlev:
                                dlev += 1
                                self.__call__(path=dir, dlev=dlev, max_dlev=max_dlev, hidden=hidden)
                                dlev += (-1)
                            else:
                                print('{:9} {} {}'.format(self.print_filesys_info(self.get_dir_size_recursive(dir)), dir, '<dir>'))
        else:
            if hidden:
                resp = {path+'/'+dir: os.stat(path+'/'+dir)[6] for dir in os.listdir(path)}
            else:
                resp = {path+'/'+dir: os.stat(path+'/'+dir)[6] for dir in os.listdir(path) if not dir.startswith('.')}
            for dir in resp.keys():

                if not os.stat(dir)[0] & 0x4000:
                    print('{:9} {}'.format(self.print_filesys_info(resp[dir]), dir))

                else:
                    if dlev < max_dlev:
                        dlev += 1
                        self.__call__(path=dir, dlev=dlev, max_dlev=max_dlev, hidden=hidden)
                        dlev += (-1)
                    else:
                        print('{:9} {} {}'.format(self.print_filesys_info(self.get_dir_size_recursive(dir)), dir, '<dir>'))

    def print_filesys_info(self, filesize):
        _kB = 1024
        if filesize < _kB:
            sizestr = str(filesize) + " by"
        elif filesize < _kB**2:
            sizestr = "%0.1f KB" % (filesize / _kB)
        elif filesize < _kB**3:
            sizestr = "%0.1f MB" % (filesize / _kB**2)
        else:
            sizestr = "%0.1f GB" % (filesize / _kB**3)
        return sizestr

    def get_dir_size_recursive(self, dir):
        return sum([os.stat(dir+'/'+f)[6] if not os.stat(dir+'/'+f)[0] & 0x4000 else self.get_dir_size_recursive(dir+'/'+f) for f in os.listdir(dir)])
Cheers

Post Reply