Version 0.0.8

Add path
This commit is contained in:
StableLlama
2025-05-08 23:02:30 +02:00
parent c5b303bde2
commit cabcd9cb52
4 changed files with 586 additions and 4 deletions
+7
View File
@@ -50,6 +50,13 @@ These nodes are very lightweight and require no additional dependencies.
- Collection conversions: to LIST, to SET, to DICT
- Special conversions: data list to LIST, data list to SET
- **path**: File system path manipulation nodes
- Basic operations: join, split, splitext, basename, dirname, normalize
- Path information: abspath, exists, is_file, is_dir, is_absolute, get_size, get_extension
- Directory operations: list_dir, get_cwd
- Path searching: glob, common_prefix
- Path conversions: relative, expand_vars
- **DICT**: Dictionary manipulation nodes
- Creation: create, from_items, from_lists, fromkeys, any_to_DICT
- Access: get, get_multiple, keys, values, items
+5 -3
View File
@@ -4,11 +4,13 @@ build-backend = "setuptools.build_meta"
[project]
name = "basic_data_handling"
version = "0.0.7"
description = """Basic Python functions for manipulating data that every programmer is used to.
version = "0.0.8"
description = """NOTE: Still in development! Expect breaking changes!
Basic Python functions for manipulating data that every programmer is used to.
Currently supported ComfyUI data types: BOOLEAN, FLOAT, INT, STRING and data lists.
Additionally supported Python data types as custom data types: DICT, LIST.
Data handling is supported with comparisons and control flow operations."""
Data handling is supported with comparisons and control flow operations.
Also contains math, regex, and file system path manipulation functions."""
authors = [
{name = "Basic data handling"}
]
+3 -1
View File
@@ -1,6 +1,6 @@
from . import (boolean_nodes, casting_nodes, comparison_nodes, control_flow,
data_list_nodes, dict_nodes, float_nodes, int_nodes, list_nodes,
math_nodes, regex_nodes, set_nodes, string_nodes)
math_nodes, path_nodes, regex_nodes, set_nodes, string_nodes)
NODE_CLASS_MAPPINGS = {}
NODE_CLASS_MAPPINGS.update(boolean_nodes.NODE_CLASS_MAPPINGS)
@@ -12,6 +12,7 @@ NODE_CLASS_MAPPINGS.update(dict_nodes.NODE_CLASS_MAPPINGS)
NODE_CLASS_MAPPINGS.update(float_nodes.NODE_CLASS_MAPPINGS)
NODE_CLASS_MAPPINGS.update(int_nodes.NODE_CLASS_MAPPINGS)
NODE_CLASS_MAPPINGS.update(list_nodes.NODE_CLASS_MAPPINGS)
NODE_CLASS_MAPPINGS.update(path_nodes.NODE_CLASS_MAPPINGS)
NODE_CLASS_MAPPINGS.update(regex_nodes.NODE_CLASS_MAPPINGS)
NODE_CLASS_MAPPINGS.update(set_nodes.NODE_CLASS_MAPPINGS)
NODE_CLASS_MAPPINGS.update(math_nodes.NODE_CLASS_MAPPINGS)
@@ -27,6 +28,7 @@ NODE_DISPLAY_NAME_MAPPINGS.update(dict_nodes.NODE_DISPLAY_NAME_MAPPINGS)
NODE_DISPLAY_NAME_MAPPINGS.update(float_nodes.NODE_DISPLAY_NAME_MAPPINGS)
NODE_DISPLAY_NAME_MAPPINGS.update(int_nodes.NODE_DISPLAY_NAME_MAPPINGS)
NODE_DISPLAY_NAME_MAPPINGS.update(list_nodes.NODE_DISPLAY_NAME_MAPPINGS)
NODE_DISPLAY_NAME_MAPPINGS.update(path_nodes.NODE_DISPLAY_NAME_MAPPINGS)
NODE_DISPLAY_NAME_MAPPINGS.update(regex_nodes.NODE_DISPLAY_NAME_MAPPINGS)
NODE_DISPLAY_NAME_MAPPINGS.update(set_nodes.NODE_DISPLAY_NAME_MAPPINGS)
NODE_DISPLAY_NAME_MAPPINGS.update(math_nodes.NODE_DISPLAY_NAME_MAPPINGS)
+571
View File
@@ -0,0 +1,571 @@
from inspect import cleandoc
import os
import glob
class PathJoin:
"""
Joins multiple path components into a single path.
This node takes multiple path components and joins them intelligently
to form a single path. It handles directory separators correctly
for the operating system.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"path1": ("STRING", {"default": ""}),
},
"optional": {
"path2": ("STRING", {"default": ""}),
}
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("path",)
CATEGORY = "Basic/path"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "join_paths"
def join_paths(self, path1: str, path2: str = "") -> tuple[str]:
paths = [p for p in [path1, path2] if p]
return (str(os.path.join(*paths)),)
class PathAbspath:
"""
Returns the absolute path of a file or directory.
This node takes a path and returns its absolute (full) path
by resolving any relative path components and symbolic links.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"path": ("STRING", {"default": ""}),
}
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("absolute_path",)
CATEGORY = "Basic/path"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "get_abspath"
def get_abspath(self, path: str) -> tuple[str]:
return (os.path.abspath(path),)
class PathExists:
"""
Checks if a path exists in the filesystem.
This node returns True if the path exists (either as a file or a directory),
and False otherwise.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"path": ("STRING", {"default": ""}),
}
}
RETURN_TYPES = ("BOOLEAN",)
RETURN_NAMES = ("exists",)
CATEGORY = "Basic/path"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "check_exists"
def check_exists(self, path: str) -> tuple[bool]:
return (os.path.exists(path),)
class PathIsFile:
"""
Checks if a path points to a file.
This node returns True if the path exists and is a regular file,
and False otherwise.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"path": ("STRING", {"default": ""}),
}
}
RETURN_TYPES = ("BOOLEAN",)
RETURN_NAMES = ("is_file",)
CATEGORY = "Basic/path"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "check_is_file"
def check_is_file(self, path: str) -> tuple[bool]:
return (os.path.isfile(path),)
class PathIsDir:
"""
Checks if a path points to a directory.
This node returns True if the path exists and is a directory,
and False otherwise.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"path": ("STRING", {"default": ""}),
}
}
RETURN_TYPES = ("BOOLEAN",)
RETURN_NAMES = ("is_dir",)
CATEGORY = "Basic/path"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "check_is_dir"
def check_is_dir(self, path: str) -> tuple[bool]:
return (os.path.isdir(path),)
class PathGetSize:
"""
Returns the size of a file in bytes.
This node returns the size in bytes of the file at the given path.
Raises an error if the path doesn't exist or isn't a file.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"path": ("STRING", {"default": ""}),
}
}
RETURN_TYPES = ("INT",)
RETURN_NAMES = ("size_bytes",)
CATEGORY = "Basic/path"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "get_size"
def get_size(self, path: str) -> tuple[int]:
if not os.path.exists(path):
raise FileNotFoundError(f"Path does not exist: {path}")
if not os.path.isfile(path):
raise ValueError(f"Path is not a file: {path}")
return (os.path.getsize(path),)
class PathSplit:
"""
Splits a path into directory and filename components.
This node takes a path and returns a tuple containing the directory path
and the filename.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"path": ("STRING", {"default": ""}),
}
}
RETURN_TYPES = ("STRING", "STRING")
RETURN_NAMES = ("directory", "filename")
CATEGORY = "Basic/path"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "split_path"
def split_path(self, path: str) -> tuple[str, str]:
return os.path.split(path)
class PathSplitExt:
"""
Splits a path into name and extension components.
This node takes a path and returns a tuple containing the path without
the extension and the extension (including the dot).
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"path": ("STRING", {"default": ""}),
}
}
RETURN_TYPES = ("STRING", "STRING")
RETURN_NAMES = ("path_without_ext", "extension")
CATEGORY = "Basic/path"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "split_ext"
def split_ext(self, path: str) -> tuple[str, str]:
return os.path.splitext(path)
class PathBasename:
"""
Returns the base name of a path.
This node extracts the filename component from a path,
removing any directory information.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"path": ("STRING", {"default": ""}),
}
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("basename",)
CATEGORY = "Basic/path"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "get_basename"
def get_basename(self, path: str) -> tuple[str]:
return (os.path.basename(path),)
class PathDirname:
"""
Returns the directory name of a path.
This node extracts the directory component from a path,
removing the filename.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"path": ("STRING", {"default": ""}),
}
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("dirname",)
CATEGORY = "Basic/path"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "get_dirname"
def get_dirname(self, path: str) -> tuple[str]:
return (os.path.dirname(path),)
class PathGetExtension:
"""
Returns the extension of a file.
This node extracts the file extension from a path,
including the dot (e.g., '.txt').
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"path": ("STRING", {"default": ""}),
}
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("extension",)
CATEGORY = "Basic/path"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "get_extension"
def get_extension(self, path: str) -> tuple[str]:
return (os.path.splitext(path)[1],)
class PathNormalize:
"""
Normalizes a path.
This node normalizes a path by collapsing redundant separators,
resolving up-level references, and converting to the correct
separator for the operating system.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"path": ("STRING", {"default": ""}),
}
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("normalized_path",)
CATEGORY = "Basic/path"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "normalize_path"
def normalize_path(self, path: str) -> tuple[str]:
return (os.path.normpath(path),)
class PathRelative:
"""
Returns a relative path.
This node computes a relative path from the 'start' path to the 'path'.
If 'start' is not provided, the current working directory is used.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"path": ("STRING", {"default": ""}),
},
"optional": {
"start": ("STRING", {"default": ""}),
}
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("relative_path",)
CATEGORY = "Basic/path"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "get_relative_path"
def get_relative_path(self, path: str, start: str = "") -> tuple[str]:
if not start:
start = os.getcwd()
return (os.path.relpath(path, start),)
class PathGlob:
"""
Finds paths matching a pattern.
This node returns a list of paths matching the given pattern.
The pattern follows shell-style wildcard rules:
* - matches any number of characters
? - matches a single character
[seq] - matches any character in seq
[!seq] - matches any character not in seq
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"pattern": ("STRING", {"default": "*.txt"}),
},
"optional": {
"recursive": (["False", "True"], {"default": "False"}),
}
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("matching_paths",)
CATEGORY = "Basic/path"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "glob_paths"
OUTPUT_IS_LIST = (True,)
def glob_paths(self, pattern: str, recursive: str = "False") -> tuple[list[str]]:
is_recursive = (recursive == "True")
if is_recursive:
# If recursive, use ** pattern with glob.glob's recursive option
return (glob.glob(pattern, recursive=True),)
else:
return (glob.glob(pattern),)
class PathExpandVars:
"""
Expands environment variables in a path.
This node replaces environment variables in a path with their values.
For example, $HOME or ${HOME} on Unix, or %USERPROFILE% on Windows.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"path": ("STRING", {"default": ""}),
}
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("expanded_path",)
CATEGORY = "Basic/path"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "expand_vars"
def expand_vars(self, path: str) -> tuple[str]:
return (os.path.expandvars(path),)
class PathGetCwd:
"""
Returns the current working directory.
This node returns the current working directory as an absolute path.
"""
@classmethod
def INPUT_TYPES(cls):
return {"required": {}}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("current_directory",)
CATEGORY = "Basic/path"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "get_cwd"
def get_cwd(self) -> tuple[str]:
return (os.getcwd(),)
class PathListDir:
"""
Lists the contents of a directory.
This node returns a list of files and directories in the specified path.
If 'files_only' is True, it only returns files.
If 'dirs_only' is True, it only returns directories.
If both are False, it returns all contents.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"path": ("STRING", {"default": ""}),
},
"optional": {
"files_only": (["False", "True"], {"default": "False"}),
"dirs_only": (["False", "True"], {"default": "False"}),
}
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("entries",)
CATEGORY = "Basic/path"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "list_directory"
OUTPUT_IS_LIST = (True,)
def list_directory(self, path: str, files_only: str = "False", dirs_only: str = "False") -> tuple[list[str]]:
if not path:
path = os.getcwd()
if not os.path.exists(path):
raise FileNotFoundError(f"Directory does not exist: {path}")
if not os.path.isdir(path):
raise NotADirectoryError(f"Path is not a directory: {path}")
entries = os.listdir(path)
if files_only == "True":
entries = [e for e in entries if os.path.isfile(os.path.join(path, e))]
elif dirs_only == "True":
entries = [e for e in entries if os.path.isdir(os.path.join(path, e))]
return (entries,)
class PathIsAbsolute:
"""
Checks if a path is absolute.
This node returns True if the path is absolute (begins at the root directory),
and False if it's relative.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"path": ("STRING", {"default": ""}),
}
}
RETURN_TYPES = ("BOOLEAN",)
RETURN_NAMES = ("is_absolute",)
CATEGORY = "Basic/path"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "check_is_absolute"
def check_is_absolute(self, path: str) -> tuple[bool]:
return (os.path.isabs(path),)
class PathCommonPrefix:
"""
Finds the common prefix of multiple paths.
This node returns the longest common leading component of the given paths.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"path1": ("STRING", {"default": ""}),
},
"optional": {
"path2": ("STRING", {"default": ""}),
}
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("common_prefix",)
CATEGORY = "Basic/path"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "get_common_prefix"
def get_common_prefix(self, path1: str, path2: str = "") -> tuple[str]:
paths = [p for p in [path1, path2] if p]
return (os.path.commonprefix(paths),)
NODE_CLASS_MAPPINGS = {
"PathJoin": PathJoin,
"PathAbspath": PathAbspath,
"PathExists": PathExists,
"PathIsFile": PathIsFile,
"PathIsDir": PathIsDir,
"PathGetSize": PathGetSize,
"PathSplit": PathSplit,
"PathSplitExt": PathSplitExt,
"PathBasename": PathBasename,
"PathDirname": PathDirname,
"PathGetExtension": PathGetExtension,
"PathNormalize": PathNormalize,
"PathRelative": PathRelative,
"PathGlob": PathGlob,
"PathExpandVars": PathExpandVars,
"PathGetCwd": PathGetCwd,
"PathListDir": PathListDir,
"PathIsAbsolute": PathIsAbsolute,
"PathCommonPrefix": PathCommonPrefix,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"PathJoin": "join",
"PathAbspath": "abspath",
"PathExists": "exists",
"PathIsFile": "is_file",
"PathIsDir": "is_dir",
"PathGetSize": "get_size",
"PathSplit": "split",
"PathSplitExt": "splitext",
"PathBasename": "basename",
"PathDirname": "dirname",
"PathGetExtension": "get_extension",
"PathNormalize": "normalize",
"PathRelative": "relative",
"PathGlob": "glob",
"PathExpandVars": "expand_vars",
"PathGetCwd": "get_cwd",
"PathListDir": "list_dir",
"PathIsAbsolute": "is_absolute",
"PathCommonPrefix": "common_prefix",
}