From d707efe53c3f5e157a172fbb2ad590c6ea9fb22a Mon Sep 17 00:00:00 2001 From: doctorpangloss <2229300+doctorpangloss@users.noreply.github.com> Date: Thu, 23 Oct 2025 12:50:34 -0700 Subject: [PATCH] Add default font, add package fs, prepare to add more sherlocked nodes --- comfy/cmd/main_pre.py | 10 ++ comfy/component_model/package_filesystem.py | 117 ++++++++++++++++++ comfy/fonts/OFL.md | 107 ++++++++++++++++ comfy/fonts/Tiny5-Regular.ttf | Bin 0 -> 131452 bytes comfy/fonts/__init__.py | 0 pyproject.toml | 1 + tests/unit/fsspec_tests/__init__.py | 0 tests/unit/fsspec_tests/files/__init__.py | 0 tests/unit/fsspec_tests/files/b.txt | 1 + tests/unit/fsspec_tests/files/subdir/a.txt | 1 + .../fsspec_tests/test_package_filesystem.py | 114 +++++++++++++++++ 11 files changed, 351 insertions(+) create mode 100644 comfy/component_model/package_filesystem.py create mode 100644 comfy/fonts/OFL.md create mode 100644 comfy/fonts/Tiny5-Regular.ttf create mode 100644 comfy/fonts/__init__.py create mode 100644 tests/unit/fsspec_tests/__init__.py create mode 100644 tests/unit/fsspec_tests/files/__init__.py create mode 100644 tests/unit/fsspec_tests/files/b.txt create mode 100644 tests/unit/fsspec_tests/files/subdir/a.txt create mode 100644 tests/unit/fsspec_tests/test_package_filesystem.py diff --git a/comfy/cmd/main_pre.py b/comfy/cmd/main_pre.py index d022e24d6..3bc3e9f0d 100644 --- a/comfy/cmd/main_pre.py +++ b/comfy/cmd/main_pre.py @@ -14,8 +14,11 @@ import os import shutil import warnings +import fsspec + from .. import options from ..app import logger +from ..component_model import package_filesystem os.environ['TORCH_ROCM_AOTRITON_ENABLE_EXPERIMENTAL'] = '1' os.environ["OPENCV_IO_ENABLE_OPENEXR"] = "1" @@ -155,8 +158,15 @@ def _configure_logging(): logging_level = args.logging_level logger.setup_logger(logging_level) +def _register_fsspec_fs(): + fsspec.register_implementation( + package_filesystem.PkgResourcesFileSystem.protocol, + package_filesystem.PkgResourcesFileSystem, + ) + _configure_logging() _fix_pytorch_240() +_register_fsspec_fs() tracer = _create_tracer() __all__ = ["args", "tracer"] diff --git a/comfy/component_model/package_filesystem.py b/comfy/component_model/package_filesystem.py new file mode 100644 index 000000000..683f98ae1 --- /dev/null +++ b/comfy/component_model/package_filesystem.py @@ -0,0 +1,117 @@ +import importlib.resources +from fsspec.spec import AbstractFileSystem +from fsspec.registry import register_implementation + + +class PkgResourcesFileSystem(AbstractFileSystem): + """ + An fsspec filesystem for reading Python package resources. + + Paths are expected in the format: + pkg:///path/to/resource.txt + """ + + protocol = "pkg" + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self._traversables = {} + + def _get_traversable(self, package_name): + """Get or cache the root Traversable for a package.""" + if package_name not in self._traversables: + try: + # Get the Traversable object for the root of the package + self._traversables[package_name] = importlib.resources.files(package_name) + except ModuleNotFoundError as e: + raise FileNotFoundError(f"Package '{package_name}' not found.") from e + return self._traversables[package_name] + + def _resolve_path(self, path): + """Split a pkg:// path into package name and resource path.""" + # Remove protocol and leading slashes + path_no_proto = self._strip_protocol(path).lstrip('/') + + if not path_no_proto: + raise ValueError("Path must include a package name.") + + parts = path_no_proto.split('/', 1) + package_name = parts[0] + + resource_path = parts[1] if len(parts) > 1 else "" + + root = self._get_traversable(package_name) + + # Resolve the final resource Traversable + if resource_path: + resource = root.joinpath(resource_path) + else: + resource = root + + return resource + + def _open( + self, + path, + mode="rb", + **kwargs, + ): + """Open a file for reading.""" + if "w" in mode or "a" in mode or "x" in mode: + raise NotImplementedError("Only read mode is supported.") + + try: + resource = self._resolve_path(path) + if not resource.is_file(): + raise FileNotFoundError(f"Path is not a file: {path}") + return resource.open("rb") + except (ModuleNotFoundError, FileNotFoundError): + raise FileNotFoundError(f"Resource not found: {path}") + except Exception as e: + raise IOError(f"Failed to open resource {path}: {e}") from e + + def ls(self, path, detail=False, **kwargs): + """List contents of a package directory.""" + try: + resource = self._resolve_path(path) + if not resource.is_dir(): + # If it's a file, 'ls' should return info on that file + return [self.info(path)] if detail else [path] + + items = [] + for item in resource.iterdir(): + item_path = f"{path.rstrip('/')}/{item.name}" + if detail: + items.append(self.info(item_path)) + else: + items.append(item_path) + return items + except (ModuleNotFoundError, FileNotFoundError): + raise FileNotFoundError(f"Resource path not found: {path}") + + def info(self, path, **kwargs): + """Get info about a resource.""" + try: + resource = self._resolve_path(path) + resource_type = "directory" if resource.is_dir() else "file" + + size = None + if resource_type == 'file': + # This is inefficient but demonstrates the principle + try: + with resource.open('rb') as f: + size = len(f.read()) + except Exception: + size = None # Could fail for some reason + + return { + "name": path, + "type": resource_type, + "size": size, + } + except (ModuleNotFoundError, FileNotFoundError): + raise FileNotFoundError(f"Resource not found: {path}") + + +# Register the filesystem with fsspec +register_implementation(PkgResourcesFileSystem.protocol, PkgResourcesFileSystem) diff --git a/comfy/fonts/OFL.md b/comfy/fonts/OFL.md new file mode 100644 index 000000000..e6f10ec71 --- /dev/null +++ b/comfy/fonts/OFL.md @@ -0,0 +1,107 @@ +Copyright 2022-2024 The Tiny5 Project Authors (https://github.com/Gissio/font_tiny5) + +This Font Software is licensed under the SIL Open Font License, Version 1.1 . This license is copied below, and is also available with a FAQ at: https://openfontlicense.org + +  + +\---------------------------------------------------------------------- + +#### SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 + +\---------------------------------------------------------------------- + +  + +PREAMBLE +----------- + +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +----------- + +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +----------- + +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, + in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, + redistributed and/or sold with any software, provided that each copy + contains the above copyright notice and this license. These can be + included either as stand-alone text files, human-readable headers or + in the appropriate machine-readable metadata fields within text or + binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font + Name(s) unless explicit written permission is granted by the corresponding + Copyright Holder. This restriction only applies to the primary font name as + presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font + Software shall not be used to promote, endorse or advertise any + Modified Version, except to acknowledge the contribution(s) of the + Copyright Holder(s) and the Author(s) or with their explicit written + permission. + +5) The Font Software, modified or unmodified, in part or in whole, + must be distributed entirely under this license, and must not be + distributed under any other license. The requirement for fonts to + remain under this license does not apply to any document created + using the Font Software. + +TERMINATION +----------- + +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +----------- + +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. + diff --git a/comfy/fonts/Tiny5-Regular.ttf b/comfy/fonts/Tiny5-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..635fe079f77dc82fbe2628a62ef16da64412aafd GIT binary patch literal 131452 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 literal 0 HcmV?d00001 diff --git a/comfy/fonts/__init__.py b/comfy/fonts/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/pyproject.toml b/pyproject.toml index bd338cdca..4a1ca30f9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -112,6 +112,7 @@ dependencies = [ # doesn't support linux correctly yet "stringzilla<4.2.0", "requests_cache", + "universal_pathlib", ] [build-system] diff --git a/tests/unit/fsspec_tests/__init__.py b/tests/unit/fsspec_tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/unit/fsspec_tests/files/__init__.py b/tests/unit/fsspec_tests/files/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/unit/fsspec_tests/files/b.txt b/tests/unit/fsspec_tests/files/b.txt new file mode 100644 index 000000000..a0aba9318 --- /dev/null +++ b/tests/unit/fsspec_tests/files/b.txt @@ -0,0 +1 @@ +OK \ No newline at end of file diff --git a/tests/unit/fsspec_tests/files/subdir/a.txt b/tests/unit/fsspec_tests/files/subdir/a.txt new file mode 100644 index 000000000..a0aba9318 --- /dev/null +++ b/tests/unit/fsspec_tests/files/subdir/a.txt @@ -0,0 +1 @@ +OK \ No newline at end of file diff --git a/tests/unit/fsspec_tests/test_package_filesystem.py b/tests/unit/fsspec_tests/test_package_filesystem.py new file mode 100644 index 000000000..694ca4d37 --- /dev/null +++ b/tests/unit/fsspec_tests/test_package_filesystem.py @@ -0,0 +1,114 @@ +import pytest +import fsspec +from comfy.component_model import package_filesystem +import os + + +# Ensure the filesystem is registered once for all tests +@pytest.fixture(scope="module", autouse=True) +def setup_package_filesystem(): + if "pkg" not in fsspec.available_protocols(): + fsspec.register_implementation( + package_filesystem.PkgResourcesFileSystem.protocol, + package_filesystem.PkgResourcesFileSystem, + ) + # Yield to allow tests to run, then teardown if necessary (though not needed here) + yield + + +@pytest.fixture +def pkg_fs(): + return fsspec.filesystem("pkg") + + +def test_open_file_in_package(pkg_fs): + """Test opening a file directly within a package.""" + with pkg_fs.open("pkg://tests.unit.fsspec_tests.files/b.txt", "rb") as f: + content = f.read() + assert content == b"OK" + + +def test_open_file_in_text_mode(pkg_fs): + """Test opening a file in text mode.""" + with pkg_fs.open("pkg://tests.unit.fsspec_tests.files/b.txt", "r") as f: + content = f.read() + assert content == "OK" + + +def test_open_file_in_subdir(pkg_fs): + """Test opening a file in a subdirectory of a package.""" + with pkg_fs.open("pkg://tests.unit.fsspec_tests.files/subdir/a.txt", "rb") as f: + content = f.read() + assert content == b"OK" + + +def test_file_not_found(pkg_fs): + """Test that opening a non-existent file raises FileNotFoundError.""" + with pytest.raises(FileNotFoundError): + pkg_fs.open("pkg://tests.unit.fsspec_tests.files/nonexistent.txt") + + +def test_package_not_found(pkg_fs): + """Test that using a non-existent package raises FileNotFoundError.""" + with pytest.raises(FileNotFoundError): + pkg_fs.open("pkg://non.existent.package/resource.txt") + + +def test_ls_package_root(pkg_fs): + """Test listing the contents of a package.""" + contents = pkg_fs.ls("pkg://tests.unit.fsspec_tests.files", detail=False) + expected_items = { + "pkg://tests.unit.fsspec_tests.files/b.txt", + "pkg://tests.unit.fsspec_tests.files/subdir", + "pkg://tests.unit.fsspec_tests.files/__init__.py", + } + # Use a subset assertion to be resilient to __pycache__ + normalized_contents = {os.path.normpath(p.split('@')[0]) for p in contents} + normalized_expected = {os.path.normpath(p) for p in expected_items} + assert normalized_expected.issubset(normalized_contents) + + +def test_ls_subdir(pkg_fs): + """Test listing the contents of a subdirectory.""" + contents = pkg_fs.ls("pkg://tests.unit.fsspec_tests.files/subdir", detail=False) + normalized_contents = [os.path.normpath(p.split('@')[0]) for p in contents] + assert os.path.normpath("pkg://tests.unit.fsspec_tests.files/subdir/a.txt") in normalized_contents + + +def test_info_file(pkg_fs): + """Test getting info for a file.""" + info = pkg_fs.info("pkg://tests.unit.fsspec_tests.files/b.txt") + assert info["type"] == "file" + assert info["name"] == "pkg://tests.unit.fsspec_tests.files/b.txt" + assert info["size"] == 2 + + +def test_info_directory(pkg_fs): + """Test getting info for a directory.""" + info = pkg_fs.info("pkg://tests.unit.fsspec_tests.files/subdir") + assert info["type"] == "directory" + assert info["name"] == "pkg://tests.unit.fsspec_tests.files/subdir" + # Directories typically don't have a size in this context, or it might be 0 + assert "size" in info # Ensure size key exists + assert info["size"] is None or info["size"] == 0 + + +def test_load_font_with_upath(pkg_fs): + """Test that a font can be loaded from the pkg filesystem using UPath.""" + from upath import UPath + from PIL import ImageFont, features + + # This test requires Pillow with FreeType support + if not features.check("freetype2"): + pytest.skip("Pillow FreeType support not available") + + # UPath will use the registered fsspec filesystem for "pkg" + font_path = UPath("pkg://comfy.fonts/Tiny5-Regular.ttf") + + # ImageFont.truetype can take a file-like object. + # UPath.open() provides one using the underlying fsspec filesystem. + with font_path.open("rb") as f: + font = ImageFont.truetype(f, 10) + + assert font is not None + assert isinstance(font, ImageFont.FreeTypeFont)