Skip to content

Commit

Permalink
Cleaned up unused code, fixed linter warnings, using custom PathEntry…
Browse files Browse the repository at this point in the history
… instead of os.DirEntry to handle single files.
  • Loading branch information
Troy Spradling committed Jul 14, 2023
1 parent 080e9b2 commit e0f594e
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 15 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,6 @@ dmypy.json

# Pyre type checker
.pyre/

# Idea configurations
.idea/
20 changes: 13 additions & 7 deletions xlsd/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import os
from typing import Callable
from .path import PathEntry


COLORS = {
'symlink_target': "{CYAN}",
Expand All @@ -8,10 +9,11 @@
'size_unit': "{CYAN}",
}

XlsdSortMethod = Callable[[list[os.DirEntry]], list[os.DirEntry]]

XlsdSortMethod = Callable[[list[PathEntry]], list[PathEntry]]

XLSD_SORT_METHODS: dict[str, XlsdSortMethod] = {}
#XLSD_SORT_METHODS = {}


def xlsd_register_sort_method(name: str):
"""
Expand All @@ -22,16 +24,18 @@ def decorator(func: XlsdSortMethod):
return func
return decorator

def _direntry_lowercase_name(entry: os.DirEntry) -> str:

def _direntry_lowercase_name(entry: PathEntry) -> str:
"""
Return the lowercase name for a DirEntry.
This is used to sort a list of DirEntry by name.
"""
return entry.name.lower()


@xlsd_register_sort_method('directories_first')
def xlsd_sort_directories_first(entries: list[os.DirEntry]) -> list[os.DirEntry]:
def xlsd_sort_directories_first(entries: list[PathEntry]) -> list[PathEntry]:
"""
Sort the entries in alphabetical order, directories first.
"""
Expand All @@ -52,16 +56,18 @@ def xlsd_sort_directories_first(entries: list[os.DirEntry]) -> list[os.DirEntry]

return directories + files


@xlsd_register_sort_method('alphabetical')
def xlsd_sort_alphabetical(entries: list[os.DirEntry]) -> list[os.DirEntry]:
def xlsd_sort_alphabetical(entries: list[PathEntry]) -> list[PathEntry]:
"""
Sort the entries in alphabetical order.
"""
entries.sort(key=_direntry_lowercase_name)
return entries


@xlsd_register_sort_method('as_is')
def xlsd_sort_as_is(entries: list[os.DirEntry]) -> list[os.DirEntry]:
def xlsd_sort_as_is(entries: list[PathEntry]) -> list[PathEntry]:
"""
Keep the entries in the same order they were returned by the OS.
"""
Expand Down
1 change: 1 addition & 0 deletions xlsd/icons.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

T = TypeVar('T')


class IconSet(Generic[T]):
"""
A storage for icons.
Expand Down
80 changes: 80 additions & 0 deletions xlsd/path.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import os
import stat


class PathEntry:
"""
FileEntry is a utility class that represents a filesystem entry at a given path. The entry can represent a file,
directory, or symbolic link. The class provides methods for checking the type of the entry and obtaining file
statistics for it.
The class can be used to mirror the behavior of os.DirEntry, because os.DirEntry objects returned by os.scandir()
do not handle individual file paths and do not allow selective control over following symbolic links for
different operations.
"""

def __init__(self, path: str) -> None:
"""
Initialize a new instance of the FileEntry class.
:param path: A string containing a path to a file or directory.
:type path: str
"""
self.path = path
self.name = os.path.basename(path)

def is_dir(self, follow_symlinks: bool = True) -> bool:
"""
Check if the path represents a directory.
:param follow_symlinks: If True (default), symlinks are followed (i.e., if the path points to a directory
through a symlink, it will return True). If False, symlinks are not followed (i.e.,
if the path points to a directory through a symlink, it will return False).
:type follow_symlinks: bool
:return: True if the path represents a directory, False otherwise.
:rtype: bool
"""
if follow_symlinks:
return os.path.isdir(self.path)
else:
return stat.S_ISDIR(self.stat(follow_symlinks=follow_symlinks).st_mode)

def is_file(self, follow_symlinks: bool = True) -> bool:
"""
Check if the path represents a file.
:param follow_symlinks: If True (default), symlinks are followed (i.e., if the path points to a file through a
symlink, it will return True). If False, symlinks are not followed (i.e., if the path
points to a file through a symlink, it will return False).
:type follow_symlinks: bool
:return: True if the path represents a file, False otherwise.
:rtype: bool
"""
if follow_symlinks:
return os.path.isfile(self.path)
else:
return stat.S_ISREG(self.stat(follow_symlinks=follow_symlinks).st_mode)

def is_symlink(self) -> bool:
"""
Check if the path represents a symbolic link.
:return: True if the path represents a symbolic link, False otherwise.
:rtype: bool
"""
return os.path.islink(self.path)

def stat(self, follow_symlinks: bool = True) -> os.stat_result:
"""
Perform a stat system call on the given path.
:param follow_symlinks: If True (default), symlinks are followed, similar to os.stat(). If False, symlinks are
not followed, similar to os.lstat().
:type follow_symlinks: bool
:return: The result of the stat or lstat call on the path.
:rtype: os.stat_result
"""
if follow_symlinks:
return os.stat(self.path)
else:
return os.lstat(self.path)
37 changes: 29 additions & 8 deletions xontrib/xlsd.xsh
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,18 @@ An improved ls for xonsh, inspired by lsd.
Registers automatically as an alias for ls on load.
"""
from enum import Enum, auto
import math
import os
from typing import Callable, Dict, List, Optional, Tuple, Union
import math

from enum import Enum, auto
from typing import Callable, List, Optional

from xonsh.lazyasd import lazyobject
from xonsh.tools import format_color, print_color, is_string_seq

from xlsd.path import PathEntry


# Lazy imports
@lazyobject
def grp():
Expand Down Expand Up @@ -343,6 +347,18 @@ def _format_direntry_name(entry: os.DirEntry, show_target: bool = True) -> str:

return "".join(colors) + name

def _scan_dir(path: str):
"""
Scan the directory with the given path and yield FileEntry instances for each entry.
:param path: A string containing a path to a directory.
:type path: str
:yield: FileEntry instance for each entry in path.
:rtype: Iterator[FileEntry]
"""
for entry in os.scandir(path):
yield PathEntry(entry.path)


def _direntry_lowercase_name(entry: os.DirEntry) -> str:
"""
Expand All @@ -358,16 +374,21 @@ def _get_entries(path: str, show_hidden: bool) -> List[os.DirEntry]:
Return the list of DirEntrys for a path, sorted by name, directories first.
"""
entries = []
try:
with os.scandir(path) as iterator:
for entry in iterator:

path_entry = PathEntry(path)
if path_entry.is_dir(path):
try:
for entry in _scan_dir(path):
# Skip entries that start with a '.'
if not show_hidden and entry.name.startswith('.'):
continue

entries.append(entry)
except PermissionError:
pass
except PermissionError:
pass
elif path_entry.is_file(path):
# If the path is a file, create a DirEntry for it
entries.append(PathEntry(path))

sort_method = xlsd.XLSD_SORT_METHODS.get($XLSD_SORT_METHOD, lambda x: x)

Expand Down

0 comments on commit e0f594e

Please sign in to comment.