From f67e92006796f558a2dc63ec3d8251678364fef5 Mon Sep 17 00:00:00 2001 From: Davi RF Date: Mon, 16 Mar 2026 21:20:07 -0300 Subject: [PATCH 1/5] Improve CLI validation --- rptree/__main__.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/rptree/__main__.py b/rptree/__main__.py index 0613440..30a68f5 100644 --- a/rptree/__main__.py +++ b/rptree/__main__.py @@ -1,23 +1,31 @@ """This module provides the RP Tree CLI.""" import pathlib -import sys from .cli import parse_cmd_line_arguments from .rptree import DirectoryTree -def main(): +def main() -> None: args = parse_cmd_line_arguments() - root_dir = pathlib.Path(args.root_dir) + root_dir = pathlib.Path(args.root_dir).resolve() + + if not root_dir.exists(): + print(f"Directory not found: {root_dir}") + exit() + if not root_dir.is_dir(): - print("The specified root directory doesn't exist") - sys.exit() + print(f"Path is not a directory: {root_dir}") + exit() + tree = DirectoryTree( - root_dir, dir_only=args.dir_only, output_file=args.output_file + root_dir, + dir_only=args.dir_only, + output_file=args.output_file, ) + tree.generate() if __name__ == "__main__": - main() + main() \ No newline at end of file From 4c8c40b292405517c0bf395e4c7940622fef42f6 Mon Sep 17 00:00:00 2001 From: Davi RF Date: Mon, 16 Mar 2026 21:27:20 -0300 Subject: [PATCH 2/5] Fix CLI program name in argparse --- rptree/cli.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/rptree/cli.py b/rptree/cli.py index c12eb43..afbfa97 100644 --- a/rptree/cli.py +++ b/rptree/cli.py @@ -1,19 +1,21 @@ """This module provides the RP Tree CLI.""" import argparse -import sys from . import __version__ -def parse_cmd_line_arguments(): +def parse_cmd_line_arguments() -> argparse.Namespace: parser = argparse.ArgumentParser( - prog="tree", - description="RP Tree, a directory tree generator", + prog="rptree", + description="Generate a directory tree", epilog="Thanks for using RP Tree!", ) + parser.version = f"RP Tree v{__version__}" + parser.add_argument("-v", "--version", action="version") + parser.add_argument( "root_dir", metavar="ROOT_DIR", @@ -21,18 +23,20 @@ def parse_cmd_line_arguments(): default=".", help="generate a full directory tree starting at ROOT_DIR", ) + parser.add_argument( "-d", "--dir-only", action="store_true", help="generate a directory-only tree", ) + parser.add_argument( "-o", "--output-file", metavar="OUTPUT_FILE", - nargs="?", - default=sys.stdout, + default=None, help="generate a full directory tree and save it to a file", ) - return parser.parse_args() + + return parser.parse_args() \ No newline at end of file From 7c0daba60d428e7e94f0c01c10ddbcf635ea7a4d Mon Sep 17 00:00:00 2001 From: Davi RF Date: Mon, 16 Mar 2026 21:57:23 -0300 Subject: [PATCH 3/5] feat: enhance tree generator with ordering options, ignore patterns, gitignore support and type hints --- rptree/cli.py | 54 +++++++++++++- rptree/rptree.py | 180 +++++++++++++++++++++++++++++++++++------------ 2 files changed, 186 insertions(+), 48 deletions(-) diff --git a/rptree/cli.py b/rptree/cli.py index afbfa97..e38cb99 100644 --- a/rptree/cli.py +++ b/rptree/cli.py @@ -31,12 +31,62 @@ def parse_cmd_line_arguments() -> argparse.Namespace: help="generate a directory-only tree", ) + parser.add_argument( + "-f", + "--files-only", + action="store_true", + help="generate a file-only tree", + ) + + ordering = parser.add_mutually_exclusive_group() + + ordering.add_argument( + "-df", + "--dirs-first", + action="store_true", + help="list directories before files", + ) + + ordering.add_argument( + "-ff", + "--files-first", + action="store_true", + help="list files before directories", + ) + + parser.add_argument( + "-i" + "--ignore", + metavar="NAME", + nargs="*", + default=None, + help="ignore specific files or directories", + ) + + parser.add_argument( + "-gi", + "--gitignore", + action="store_true", + help="respect .gitignore rules", + ) + parser.add_argument( "-o", "--output-file", metavar="OUTPUT_FILE", default=None, - help="generate a full directory tree and save it to a file", + help="save the generated tree to a file", ) - return parser.parse_args() \ No newline at end of file + args = parser.parse_args() + + if args.dir_only and args.files_only: + parser.error("cannot use --dir-only and --files-only together") + + if args.dir_only and (args.dirs_first or args.files_first): + parser.error("ordering options are not valid with --dir-only") + + if args.files_only and (args.dirs_first or args.files_first): + parser.error("ordering options are not valid with --files-only") + + return args \ No newline at end of file diff --git a/rptree/rptree.py b/rptree/rptree.py index 2581c0a..3e9ff43 100644 --- a/rptree/rptree.py +++ b/rptree/rptree.py @@ -1,9 +1,8 @@ """This module provides RP Tree main module.""" -import os import pathlib -import sys from collections import deque +from typing import Iterable, Optional, Set PIPE = "│" ELBOW = "└──" @@ -13,74 +12,163 @@ class DirectoryTree: - def __init__(self, root_dir, dir_only=False, output_file=sys.stdout): + def __init__( + self, + root_dir: str | pathlib.Path, + dir_only: bool = False, + files_only: bool = False, + dirs_first: bool = False, + files_first: bool = False, + output_file: Optional[str] = None, + ignore: Optional[Iterable[str]] = None, + use_gitignore: bool = False, + ) -> None: + self._root_dir = pathlib.Path(root_dir) self._output_file = output_file - self._generator = _TreeGenerator(root_dir, dir_only) - def generate(self): + if dir_only and files_only: + raise ValueError("Cannot use dir_only and files_only together") + + if dir_only and (dirs_first or files_first): + raise ValueError("Ordering options are not valid with dir_only") + + if files_only and (dirs_first or files_first): + raise ValueError("Ordering options are not valid with files_only") + + if dirs_first and files_first: + raise ValueError("Cannot enable dirs_first and files_first together") + + self._generator = _TreeGenerator( + root_dir=self._root_dir, + dir_only=dir_only, + files_only=files_only, + dirs_first=dirs_first, + files_first=files_first, + ignore=ignore, + use_gitignore=use_gitignore, + ) + + def generate(self) -> None: tree = self._generator.build_tree() - if self._output_file != sys.stdout: - # Wrap the tree in a markdown code block - tree.appendleft("```") - tree.append("```") - self._output_file = open( - self._output_file, mode="w", encoding="UTF-8" - ) - with self._output_file as stream: + + if self._output_file: + with open(self._output_file, "w", encoding="utf-8") as stream: + for entry in tree: + print(entry, file=stream) + else: for entry in tree: - print(entry, file=stream) + print(entry) class _TreeGenerator: - def __init__(self, root_dir, dir_only=False): - self._root_dir = pathlib.Path(root_dir) - self._dir_only = dir_only - self._tree = deque() + def __init__( + self, + root_dir: pathlib.Path, + dir_only: bool = False, + files_only: bool = False, + dirs_first: bool = False, + files_first: bool = False, + ignore: Optional[Iterable[str]] = None, + use_gitignore: bool = False, + ) -> None: + self._root_dir: pathlib.Path = root_dir + self._dir_only: bool = dir_only + self._files_only: bool = files_only + self._dirs_first: bool = dirs_first + self._files_first: bool = files_first + self._ignore: Set[str] = set(ignore or []) + self._tree: deque[str] = deque() + self._gitignore_spec = None + + if use_gitignore: + self._load_gitignore() + + def _load_gitignore(self) -> None: + gitignore = self._root_dir / ".gitignore" + if not gitignore.exists(): + return - def build_tree(self): + try: + import pathspec + + with open(gitignore) as f: + self._gitignore_spec = pathspec.PathSpec.from_lines( + "gitwildmatch", f + ) + except ImportError: + pass + + def build_tree(self) -> deque[str]: self._tree_head() self._tree_body(self._root_dir) return self._tree - def _tree_head(self): - self._tree.append(f"{self._root_dir}{os.sep}") + def _tree_head(self) -> None: + self._tree.append(f"{self._root_dir}/") - def _tree_body(self, directory, prefix=""): + def _tree_body(self, directory: pathlib.Path, prefix: str = "") -> None: entries = self._prepare_entries(directory) last_index = len(entries) - 1 + for index, entry in enumerate(entries): connector = ELBOW if index == last_index else TEE + if entry.is_dir(): - if index == 0: - self._tree.append(prefix + PIPE) - self._add_directory( - entry, index, last_index, prefix, connector - ) + self._add_directory(entry, index, last_index, prefix, connector) else: self._add_file(entry, prefix, connector) - def _prepare_entries(self, directory): - entries = sorted( - directory.iterdir(), key=lambda entry: str(entry) - ) + def _prepare_entries(self, directory: pathlib.Path) -> list[pathlib.Path]: + entries: list[pathlib.Path] = [] + + for entry in directory.iterdir(): + if entry.name in self._ignore: + continue + + if self._gitignore_spec and self._gitignore_spec.match_file( + str(entry.relative_to(self._root_dir)) + ): + continue + + entries.append(entry) + if self._dir_only: - return [entry for entry in entries if entry.is_dir()] - return sorted(entries, key=lambda entry: entry.is_file()) + entries = [e for e in entries if e.is_dir()] + + if self._files_only: + entries = [e for e in entries if e.is_file()] + + entries.sort(key=lambda e: e.name.lower()) + + if self._dirs_first: + entries.sort(key=lambda e: e.is_file()) + + if self._files_first: + entries.sort(key=lambda e: e.is_dir()) + + return entries def _add_directory( - self, directory, index, last_index, prefix, connector - ): - self._tree.append(f"{prefix}{connector} {directory.name}{os.sep}") + self, + directory: pathlib.Path, + index: int, + last_index: int, + prefix: str, + connector: str, + ) -> None: + self._tree.append(f"{prefix}{connector} {directory.name}/") + if index != last_index: - prefix += PIPE_PREFIX + new_prefix = prefix + PIPE_PREFIX else: - prefix += SPACE_PREFIX - self._tree_body( - directory=directory, - prefix=prefix, - ) - if prefix := prefix.rstrip(): - self._tree.append(prefix) + new_prefix = prefix + SPACE_PREFIX + + self._tree_body(directory, new_prefix) - def _add_file(self, file, prefix, connector): - self._tree.append(f"{prefix}{connector} {file.name}") + def _add_file( + self, + file: pathlib.Path, + prefix: str, + connector: str, + ) -> None: + self._tree.append(f"{prefix}{connector} {file.name}") \ No newline at end of file From 220f60a2291d6899c589faed403821a5a7068425 Mon Sep 17 00:00:00 2001 From: Davi RF Date: Mon, 16 Mar 2026 22:01:28 -0300 Subject: [PATCH 4/5] fix: pass new CLI arguments to DirectoryTree in main entrypoint --- rptree/__main__.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/rptree/__main__.py b/rptree/__main__.py index 30a68f5..a089403 100644 --- a/rptree/__main__.py +++ b/rptree/__main__.py @@ -19,9 +19,14 @@ def main() -> None: exit() tree = DirectoryTree( - root_dir, + root_dir=root_dir, dir_only=args.dir_only, + files_only=args.files_only, + dirs_first=args.dirs_first, + files_first=args.files_first, output_file=args.output_file, + ignore=args.ignore, + use_gitignore=args.gitignore, ) tree.generate() From 11bae2ea65c337ff9bdda0bf28ea69562c0c3a5e Mon Sep 17 00:00:00 2001 From: Davi RF Date: Tue, 17 Mar 2026 10:15:39 -0300 Subject: [PATCH 5/5] Add depth limit option and improve CLI and tree generation - Readme updated --- README.md | 150 +++++++++++++++++++++++++++--------- rptree/__init__.py | 2 +- rptree/__main__.py | 10 ++- rptree/cli.py | 19 ++++- rptree/rptree.py | 185 ++++++++++++++++++++------------------------- setup.py | 35 +++------ 6 files changed, 229 insertions(+), 172 deletions(-) diff --git a/README.md b/README.md index f647053..97aa7d8 100644 --- a/README.md +++ b/README.md @@ -4,59 +4,137 @@ RP Tree is a command-line tool to generate directory tree diagrams. ## Installation -To install **RP Tree**, just run the following command: - -```sh -$ pip install rptree +```bash +pip install rptree ``` ## Usage -```sh -$ rptree /path/to/directory/ +```bash +rptree [ROOT_DIR] +``` + +If no directory is provided, the current directory is used. + +```bash +rptree +``` + +Show help: + +```bash +rptree -h +``` + +## Options + +### General + +- `-h`, `--help` Show help message +- `-v`, `--version` Show application version + +### Modes + +- `-d`, `--dir-only` Display directories only +- `-f`, `--files-only` Display files only + +### Ordering + +- `-df`, `--dirs-first` List directories before files +- `-ff`, `--files-first` List files before directories + +> Note: Alphabetical order is always used as the base ordering. + +### Ignoring + +- `-i`, `--ignore NAME [NAME ...]` Ignore specific files or directories +- `-gi`, `--gitignore` Respect `.gitignore` rules + +### Depth + +- `-dl`, `--depth-level N` Limit the tree depth to N levels + +### Output + +- `-o`, `--output-file FILE` Save the generated tree to a file + +## Examples + +Generate tree for current directory: + +```bash +rptree +``` + +Directories first: + +```bash +rptree . -df +``` + +Files only: + +```bash +rptree . -f +``` + +Limit depth: + +```bash +rptree . -dl 2 +``` + +Ignore specific entries: + +```bash +rptree . -i node_modules dist .git ``` -**Note:** The `-h` or `--help` option provides help on using RP Tree. +Use `.gitignore` rules: + +```bash +rptree . -gi +``` + +Save output to file: + +```bash +rptree . -o tree.txt +``` ## Sample Output -```sh -$ rptree hello/ -./hello/ -│ -├── hello/ -│ ├── __init__.py -│ └── hello.py -│ +```bash +project/ +├── src/ +│ ├── main.py +│ └── utils.py ├── tests/ -│ └── test_hello.py -│ -├── LICENSE -├── README.md -├── requirements.txt -└── setup.py +│ └── test_main.py +└── README.md ``` -That's it! You've generated a nice directory tree diagram. +## Release History -## Features +### 0.2.0 -If you run RP Tree with a directory path as an argument, then you get a full directory tree diagram printed on your screen. The default input directory is your current directory. +- Added `--files-only` (`-f`) +- Added `--dirs-first` (`-df`) +- Added `--files-first` (`-ff`) +- Added `--ignore` (`-i`) +- Added `.gitignore` support (`-gi`) +- Added `--depth-level` (`-dl`) +- Improved tree formatting +- Added type hints -RP Tree also provides the following options: +### 0.1.1 -- `-v`, `--version` shows the application version and exits -- `-h`, `--help` show a usage message -- `-d`, `--dir-only` generates a directory-only tree diagram -- `-o`, `--output-file` generates a full directory tree diagram and save it to a file in markdown format +- Display entries in alphabetical order -## Release History +### 0.1.0 -- 0.1.1 - - Display the entries in alphabetical order -- 0.1.0 - - A work in progress +- Initial release -## About the Author +## Author -Leodanis Pozo Ramos - Email: leodanis@realpython.com +Leodanis Pozo Ramos diff --git a/rptree/__init__.py b/rptree/__init__.py index e8d5a69..6d8c007 100644 --- a/rptree/__init__.py +++ b/rptree/__init__.py @@ -1,3 +1,3 @@ """Top-level package for RP Tree.""" -__version__ = "0.1.1" +__version__ = "0.2.0" diff --git a/rptree/__main__.py b/rptree/__main__.py index a089403..56415f1 100644 --- a/rptree/__main__.py +++ b/rptree/__main__.py @@ -1,6 +1,7 @@ -"""This module provides the RP Tree CLI.""" +"""Entry point for RP Tree CLI.""" import pathlib +import sys from .cli import parse_cmd_line_arguments from .rptree import DirectoryTree @@ -12,11 +13,11 @@ def main() -> None: if not root_dir.exists(): print(f"Directory not found: {root_dir}") - exit() + sys.exit(1) if not root_dir.is_dir(): print(f"Path is not a directory: {root_dir}") - exit() + sys.exit(1) tree = DirectoryTree( root_dir=root_dir, @@ -24,9 +25,10 @@ def main() -> None: files_only=args.files_only, dirs_first=args.dirs_first, files_first=args.files_first, - output_file=args.output_file, ignore=args.ignore, use_gitignore=args.gitignore, + depth_level=args.depth_level, + output_file=args.output_file, ) tree.generate() diff --git a/rptree/cli.py b/rptree/cli.py index e38cb99..37c27e4 100644 --- a/rptree/cli.py +++ b/rptree/cli.py @@ -13,7 +13,6 @@ def parse_cmd_line_arguments() -> argparse.Namespace: ) parser.version = f"RP Tree v{__version__}" - parser.add_argument("-v", "--version", action="version") parser.add_argument( @@ -55,12 +54,12 @@ def parse_cmd_line_arguments() -> argparse.Namespace: ) parser.add_argument( - "-i" + "-i", "--ignore", - metavar="NAME", + metavar="PATTERN", nargs="*", default=None, - help="ignore specific files or directories", + help="ignore files or directories (supports patterns, e.g. *.py)", ) parser.add_argument( @@ -70,6 +69,15 @@ def parse_cmd_line_arguments() -> argparse.Namespace: help="respect .gitignore rules", ) + parser.add_argument( + "-dl", + "--depth-level", + metavar="N", + type=int, + default=None, + help="limit tree depth to N levels", + ) + parser.add_argument( "-o", "--output-file", @@ -89,4 +97,7 @@ def parse_cmd_line_arguments() -> argparse.Namespace: if args.files_only and (args.dirs_first or args.files_first): parser.error("ordering options are not valid with --files-only") + if args.depth_level is not None and args.depth_level < 0: + parser.error("--depth-level must be >= 0") + return args \ No newline at end of file diff --git a/rptree/rptree.py b/rptree/rptree.py index 3e9ff43..2ddb000 100644 --- a/rptree/rptree.py +++ b/rptree/rptree.py @@ -1,8 +1,12 @@ """This module provides RP Tree main module.""" +from __future__ import annotations + import pathlib +import sys from collections import deque -from typing import Iterable, Optional, Set +from fnmatch import fnmatch +from typing import Deque, List, Optional, Set PIPE = "│" ELBOW = "└──" @@ -14,99 +18,96 @@ class DirectoryTree: def __init__( self, - root_dir: str | pathlib.Path, + root_dir: pathlib.Path, + *, dir_only: bool = False, files_only: bool = False, dirs_first: bool = False, files_first: bool = False, - output_file: Optional[str] = None, - ignore: Optional[Iterable[str]] = None, + ignore: Optional[List[str]] = None, use_gitignore: bool = False, + depth_level: Optional[int] = None, + output_file: Optional[str] = None, ) -> None: - self._root_dir = pathlib.Path(root_dir) self._output_file = output_file - - if dir_only and files_only: - raise ValueError("Cannot use dir_only and files_only together") - - if dir_only and (dirs_first or files_first): - raise ValueError("Ordering options are not valid with dir_only") - - if files_only and (dirs_first or files_first): - raise ValueError("Ordering options are not valid with files_only") - - if dirs_first and files_first: - raise ValueError("Cannot enable dirs_first and files_first together") - self._generator = _TreeGenerator( - root_dir=self._root_dir, + root_dir=root_dir, dir_only=dir_only, files_only=files_only, dirs_first=dirs_first, files_first=files_first, ignore=ignore, use_gitignore=use_gitignore, + depth_level=depth_level, ) def generate(self) -> None: tree = self._generator.build_tree() if self._output_file: - with open(self._output_file, "w", encoding="utf-8") as stream: - for entry in tree: - print(entry, file=stream) + tree.appendleft("```") + tree.append("```") + with open(self._output_file, "w", encoding="utf-8") as f: + for line in tree: + print(line, file=f) else: - for entry in tree: - print(entry) + for line in tree: + print(line) class _TreeGenerator: def __init__( self, + *, root_dir: pathlib.Path, - dir_only: bool = False, - files_only: bool = False, - dirs_first: bool = False, - files_first: bool = False, - ignore: Optional[Iterable[str]] = None, - use_gitignore: bool = False, + dir_only: bool, + files_only: bool, + dirs_first: bool, + files_first: bool, + ignore: Optional[List[str]], + use_gitignore: bool, + depth_level: Optional[int], ) -> None: - self._root_dir: pathlib.Path = root_dir - self._dir_only: bool = dir_only - self._files_only: bool = files_only - self._dirs_first: bool = dirs_first - self._files_first: bool = files_first + self._root_dir = root_dir + self._dir_only = dir_only + self._files_only = files_only + self._dirs_first = dirs_first + self._files_first = files_first self._ignore: Set[str] = set(ignore or []) - self._tree: deque[str] = deque() - self._gitignore_spec = None + self._depth_level = depth_level + self._tree: Deque[str] = deque() + self._gitignore = None if use_gitignore: - self._load_gitignore() - - def _load_gitignore(self) -> None: - gitignore = self._root_dir / ".gitignore" - if not gitignore.exists(): - return - - try: - import pathspec - - with open(gitignore) as f: - self._gitignore_spec = pathspec.PathSpec.from_lines( - "gitwildmatch", f + try: + import pathspec + + gitignore = root_dir / ".gitignore" + if gitignore.exists(): + patterns = gitignore.read_text().splitlines() + self._gitignore = pathspec.PathSpec.from_lines( + "gitwildmatch", patterns + ) + except ImportError: + print( + "Warning: pathspec not installed, .gitignore ignored", + file=sys.stderr, ) - except ImportError: - pass - def build_tree(self) -> deque[str]: - self._tree_head() - self._tree_body(self._root_dir) + def build_tree(self) -> Deque[str]: + self._tree.append(f"{self._root_dir.name}/") + self._tree_body(self._root_dir, depth=0) return self._tree - def _tree_head(self) -> None: - self._tree.append(f"{self._root_dir}/") + def _tree_body( + self, + directory: pathlib.Path, + prefix: str = "", + depth: int = 0, + ) -> None: + if self._depth_level is not None and depth >= self._depth_level: + return - def _tree_body(self, directory: pathlib.Path, prefix: str = "") -> None: entries = self._prepare_entries(directory) last_index = len(entries) - 1 @@ -114,61 +115,41 @@ def _tree_body(self, directory: pathlib.Path, prefix: str = "") -> None: connector = ELBOW if index == last_index else TEE if entry.is_dir(): - self._add_directory(entry, index, last_index, prefix, connector) + self._tree.append(f"{prefix}{connector} {entry.name}/") + extension = SPACE_PREFIX if index == last_index else PIPE_PREFIX + self._tree_body(entry, prefix + extension, depth + 1) else: - self._add_file(entry, prefix, connector) - - def _prepare_entries(self, directory: pathlib.Path) -> list[pathlib.Path]: - entries: list[pathlib.Path] = [] + self._tree.append(f"{prefix}{connector} {entry.name}") - for entry in directory.iterdir(): - if entry.name in self._ignore: - continue + def _prepare_entries(self, directory: pathlib.Path) -> List[pathlib.Path]: + entries = sorted(directory.iterdir(), key=lambda e: e.name.lower()) - if self._gitignore_spec and self._gitignore_spec.match_file( - str(entry.relative_to(self._root_dir)) - ): - continue - - entries.append(entry) + entries = [e for e in entries if not self._is_ignored(e)] if self._dir_only: - entries = [e for e in entries if e.is_dir()] + return [e for e in entries if e.is_dir()] if self._files_only: - entries = [e for e in entries if e.is_file()] - - entries.sort(key=lambda e: e.name.lower()) + return [e for e in entries if e.is_file()] if self._dirs_first: - entries.sort(key=lambda e: e.is_file()) - - if self._files_first: - entries.sort(key=lambda e: e.is_dir()) + entries.sort(key=lambda e: (e.is_file(), e.name.lower())) + elif self._files_first: + entries.sort(key=lambda e: (e.is_dir(), e.name.lower())) return entries - def _add_directory( - self, - directory: pathlib.Path, - index: int, - last_index: int, - prefix: str, - connector: str, - ) -> None: - self._tree.append(f"{prefix}{connector} {directory.name}/") - - if index != last_index: - new_prefix = prefix + PIPE_PREFIX - else: - new_prefix = prefix + SPACE_PREFIX + def _is_ignored(self, entry: pathlib.Path) -> bool: + for pattern in self._ignore: + if fnmatch(entry.name, pattern): + return True - self._tree_body(directory, new_prefix) + if self._gitignore: + try: + rel = entry.relative_to(self._root_dir) + if self._gitignore.match_file(str(rel)): + return True + except ValueError: + pass - def _add_file( - self, - file: pathlib.Path, - prefix: str, - connector: str, - ) -> None: - self._tree.append(f"{prefix}{connector} {file.name}") \ No newline at end of file + return False \ No newline at end of file diff --git a/setup.py b/setup.py index 3ddee53..87ae9eb 100644 --- a/setup.py +++ b/setup.py @@ -1,38 +1,23 @@ -import pathlib -from setuptools import setup +from setuptools import setup, find_packages from rptree import __version__ -HERE = pathlib.Path().cwd() -DESCRIPTION = HERE.joinpath("README.md").read_text() -VERSION = __version__ - setup( name="rptree", - version=VERSION, - description="Generate directory tree diagrams for Real Python articles", - long_description=DESCRIPTION, - long_description_content_type="text/markdown", - url="https://github.com/realpython/rptree", + version=__version__, + description="A directory tree generator for the command line", author="Real Python", - author_email="info@realpython.com", - maintainer="Leodanis Pozo Ramos", - maintainer_email="leodanis@realpython.com", license="MIT", - classifiers=[ - "License :: OSI Approved :: MIT License", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: Implementation :: CPython", - ], - packages=["rptree"], + packages=find_packages(), include_package_data=True, + install_requires=[ + "pathspec>=0.10", + ], entry_points={ "console_scripts": [ "rptree=rptree.__main__:main", - ] + ], }, -) + python_requires=">=3.8", +) \ No newline at end of file