์ฝ˜ํ…์ธ ๋กœ ๊ฑด๋„ˆ๋›ฐ๊ธฐ

์ฐธ์กฐ ultralytics/utils/files.py

์ฐธ๊ณ 

์ด ํŒŒ์ผ์€ https://github.com/ultralytics/ ultralytics/blob/main/ ultralytics/utils/files .py์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฌธ์ œ๋ฅผ ๋ฐœ๊ฒฌํ•˜๋ฉด ํ’€ ๋ฆฌํ€˜์ŠคํŠธ (๐Ÿ› ๏ธ)์— ๊ธฐ์—ฌํ•˜์—ฌ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋„๋ก ๋„์™€์ฃผ์„ธ์š”. ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค ๐Ÿ™!



ultralytics.utils.files.WorkingDirectory

๊ธฐ์ง€: ContextDecorator

์‚ฌ์šฉ๋ฒ•: ์ž‘์—… ๋””๋ ‰ํ„ฐ๋ฆฌ(dir) ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ๋˜๋Š” 'with WorkingDirectory(dir):' ์ปจํ…์ŠคํŠธ ๊ด€๋ฆฌ์ž.

์˜ ์†Œ์Šค ์ฝ”๋“œ ultralytics/utils/files.py
class WorkingDirectory(contextlib.ContextDecorator):
    """Usage: @WorkingDirectory(dir) decorator or 'with WorkingDirectory(dir):' context manager."""

    def __init__(self, new_dir):
        """Sets the working directory to 'new_dir' upon instantiation."""
        self.dir = new_dir  # new dir
        self.cwd = Path.cwd().resolve()  # current dir

    def __enter__(self):
        """Changes the current directory to the specified directory."""
        os.chdir(self.dir)

    def __exit__(self, exc_type, exc_val, exc_tb):  # noqa
        """Restore the current working directory on context exit."""
        os.chdir(self.cwd)

__enter__()

ํ˜„์žฌ ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ ์ง€์ •๋œ ๋””๋ ‰ํ„ฐ๋ฆฌ๋กœ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค.

์˜ ์†Œ์Šค ์ฝ”๋“œ ultralytics/utils/files.py
def __enter__(self):
    """Changes the current directory to the specified directory."""
    os.chdir(self.dir)

__exit__(exc_type, exc_val, exc_tb)

์ปจํ…์ŠคํŠธ ์ข…๋ฃŒ ์‹œ ํ˜„์žฌ ์ž‘์—… ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ ๋ณต์›ํ•ฉ๋‹ˆ๋‹ค.

์˜ ์†Œ์Šค ์ฝ”๋“œ ultralytics/utils/files.py
def __exit__(self, exc_type, exc_val, exc_tb):  # noqa
    """Restore the current working directory on context exit."""
    os.chdir(self.cwd)

__init__(new_dir)

์ธ์Šคํ„ด์Šคํ™” ์‹œ ์ž‘์—… ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ 'new_dir'๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

์˜ ์†Œ์Šค ์ฝ”๋“œ ultralytics/utils/files.py
def __init__(self, new_dir):
    """Sets the working directory to 'new_dir' upon instantiation."""
    self.dir = new_dir  # new dir
    self.cwd = Path.cwd().resolve()  # current dir



ultralytics.utils.files.spaces_in_path(path)

์ด๋ฆ„์— ๊ณต๋ฐฑ์ด ์žˆ๋Š” ๊ฒฝ๋กœ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ์ปจํ…์ŠคํŠธ ๊ด€๋ฆฌ์ž. ๊ฒฝ๋กœ์— ๊ณต๋ฐฑ์ด ํฌํ•จ๋˜์–ด ์žˆ์œผ๋ฉด ๊ณต๋ฐฑ์„ ๋ฐ‘์ค„๋กœ ๋ฐ”๊พธ๊ณ , ํŒŒ์ผ/๋””๋ ‰ํ† ๋ฆฌ๋ฅผ ์ƒˆ ๊ฒฝ๋กœ๋กœ ๋ณต์‚ฌํ•˜๊ณ , ์ปจํ…์ŠคํŠธ ์ฝ”๋“œ ๋ธ”๋ก์„ ์‹คํ–‰ํ•œ ๋‹ค์Œ, ํŒŒ์ผ/๋””๋ ‰ํ† ๋ฆฌ์˜ ํŒŒ์ผ/๋””๋ ‰ํ† ๋ฆฌ๋ฅผ ์›๋ž˜ ์œ„์น˜๋กœ ๋‹ค์‹œ ๋ณต์‚ฌํ•ฉ๋‹ˆ๋‹ค.

๋งค๊ฐœ๋ณ€์ˆ˜:

์ด๋ฆ„ ์œ ํ˜• ์„ค๋ช… ๊ธฐ๋ณธ๊ฐ’
path str | Path

์›๋ž˜ ๊ฒฝ๋กœ์ž…๋‹ˆ๋‹ค.

ํ•„์ˆ˜

์ˆ˜ํ™•๋Ÿ‰:

์œ ํ˜• ์„ค๋ช…
Path

๊ณต๋ฐฑ์ด ์žˆ๋Š” ๊ฒฝ์šฐ ๋ฐ‘์ค„๋กœ ๋Œ€์ฒด๋œ ์ž„์‹œ ๊ฒฝ๋กœ, ๊ทธ๋ ‡์ง€ ์•Š์€ ๊ฒฝ์šฐ ์›๋ž˜ ๊ฒฝ๋กœ์ž…๋‹ˆ๋‹ค.

์˜ˆ์ œ
with ultralytics.utils.files import spaces_in_path

with spaces_in_path('/path/with spaces') as new_path:
    # Your code here
์˜ ์†Œ์Šค ์ฝ”๋“œ ultralytics/utils/files.py
@contextmanager
def spaces_in_path(path):
    """
    Context manager to handle paths with spaces in their names. If a path contains spaces, it replaces them with
    underscores, copies the file/directory to the new path, executes the context code block, then copies the
    file/directory back to its original location.

    Args:
        path (str | Path): The original path.

    Yields:
        (Path): Temporary path with spaces replaced by underscores if spaces were present, otherwise the original path.

    Example:
        ```python
        with ultralytics.utils.files import spaces_in_path

        with spaces_in_path('/path/with spaces') as new_path:
            # Your code here
        ```
    """

    # If path has spaces, replace them with underscores
    if " " in str(path):
        string = isinstance(path, str)  # input type
        path = Path(path)

        # Create a temporary directory and construct the new path
        with tempfile.TemporaryDirectory() as tmp_dir:
            tmp_path = Path(tmp_dir) / path.name.replace(" ", "_")

            # Copy file/directory
            if path.is_dir():
                # tmp_path.mkdir(parents=True, exist_ok=True)
                shutil.copytree(path, tmp_path)
            elif path.is_file():
                tmp_path.parent.mkdir(parents=True, exist_ok=True)
                shutil.copy2(path, tmp_path)

            try:
                # Yield the temporary path
                yield str(tmp_path) if string else tmp_path

            finally:
                # Copy file/directory back
                if tmp_path.is_dir():
                    shutil.copytree(tmp_path, path, dirs_exist_ok=True)
                elif tmp_path.is_file():
                    shutil.copy2(tmp_path, path)  # Copy back the file

    else:
        # If there are no spaces, just yield the original path
        yield path



ultralytics.utils.files.increment_path(path, exist_ok=False, sep='', mkdir=False)

ํŒŒ์ผ ๋˜๋Š” ๋””๋ ‰ํ„ฐ๋ฆฌ ๊ฒฝ๋กœ๋ฅผ ์ฆ๊ฐ€์‹œํ‚ต๋‹ˆ๋‹ค(์˜ˆ: runs/exp --> runs/exp{sep}2, runs/exp{sep}3, ... ๋“ฑ).

๊ฒฝ๋กœ๊ฐ€ ์กด์žฌํ•˜๊ณ  exist_ok๊ฐ€ True๋กœ ์„ค์ •๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ, ๊ฒฝ๋กœ ๋์— ์ˆซ์ž๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  ๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ๊ฒฝ๋กœ๊ฐ€ ์ฆ๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. ๊ฒฝ๋กœ๊ฐ€ ํŒŒ์ผ์ธ ๊ฒฝ์šฐ ํŒŒ์ผ ํ™•์žฅ์ž๊ฐ€ ์œ ์ง€๋ฉ๋‹ˆ๋‹ค. ๊ฒฝ๋กœ๊ฐ€ ๋””๋ ‰ํ„ฐ๋ฆฌ์ธ ๊ฒฝ์šฐ ์ˆซ์ž๊ฐ€ ๊ฒฝ๋กœ ๋์— ๋ฐ”๋กœ ์ถ”๊ฐ€๋ฉ๋‹ˆ๋‹ค. mkdir์ด True๋กœ ์„ค์ •๋˜์–ด ์žˆ์œผ๋ฉด ๊ฒฝ๋กœ๊ฐ€ ๋””๋ ‰ํ„ฐ๋ฆฌ๋กœ ๋งŒ๋“ค์–ด์ง‘๋‹ˆ๋‹ค. ๋””๋ ‰ํ„ฐ๋ฆฌ๋กœ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.

๋งค๊ฐœ๋ณ€์ˆ˜:

์ด๋ฆ„ ์œ ํ˜• ์„ค๋ช… ๊ธฐ๋ณธ๊ฐ’
path (str, Path)

์ฆ๋ถ„ํ•  ๊ฒฝ๋กœ์ž…๋‹ˆ๋‹ค.

ํ•„์ˆ˜
exist_ok bool

True์ด๋ฉด ๊ฒฝ๋กœ๊ฐ€ ์ฆ๊ฐ€ํ•˜์ง€ ์•Š๊ณ  ๊ทธ๋Œ€๋กœ ๋ฐ˜ํ™˜๋ฉ๋‹ˆ๋‹ค. ๊ธฐ๋ณธ๊ฐ’์€ False์ž…๋‹ˆ๋‹ค.

False
sep str

๊ฒฝ๋กœ์™€ ์ฆ๋ถ„ ๋ฒˆํ˜ธ ์‚ฌ์ด์— ์‚ฌ์šฉํ•  ๊ตฌ๋ถ„ ๊ธฐํ˜ธ์ž…๋‹ˆ๋‹ค. ๊ธฐ๋ณธ๊ฐ’์€ ''์ž…๋‹ˆ๋‹ค.

''
mkdir bool

๋””๋ ‰ํ† ๋ฆฌ๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ ๋””๋ ‰ํ† ๋ฆฌ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ๋ณธ๊ฐ’์€ False์ž…๋‹ˆ๋‹ค.

False

๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค:

์œ ํ˜• ์„ค๋ช…
Path

์ฆ๊ฐ€๋œ ๊ฒฝ๋กœ.

์˜ ์†Œ์Šค ์ฝ”๋“œ ultralytics/utils/files.py
def increment_path(path, exist_ok=False, sep="", mkdir=False):
    """
    Increments a file or directory path, i.e. runs/exp --> runs/exp{sep}2, runs/exp{sep}3, ... etc.

    If the path exists and exist_ok is not set to True, the path will be incremented by appending a number and sep to
    the end of the path. If the path is a file, the file extension will be preserved. If the path is a directory, the
    number will be appended directly to the end of the path. If mkdir is set to True, the path will be created as a
    directory if it does not already exist.

    Args:
        path (str, pathlib.Path): Path to increment.
        exist_ok (bool, optional): If True, the path will not be incremented and returned as-is. Defaults to False.
        sep (str, optional): Separator to use between the path and the incrementation number. Defaults to ''.
        mkdir (bool, optional): Create a directory if it does not exist. Defaults to False.

    Returns:
        (pathlib.Path): Incremented path.
    """
    path = Path(path)  # os-agnostic
    if path.exists() and not exist_ok:
        path, suffix = (path.with_suffix(""), path.suffix) if path.is_file() else (path, "")

        # Method 1
        for n in range(2, 9999):
            p = f"{path}{sep}{n}{suffix}"  # increment path
            if not os.path.exists(p):
                break
        path = Path(p)

    if mkdir:
        path.mkdir(parents=True, exist_ok=True)  # make directory

    return path



ultralytics.utils.files.file_age(path=__file__)

๋งˆ์ง€๋ง‰ ํŒŒ์ผ ์—…๋ฐ์ดํŠธ ์ดํ›„ ๋ฐ˜ํ™˜ ์ผ์ˆ˜์ž…๋‹ˆ๋‹ค.

์˜ ์†Œ์Šค ์ฝ”๋“œ ultralytics/utils/files.py
def file_age(path=__file__):
    """Return days since last file update."""
    dt = datetime.now() - datetime.fromtimestamp(Path(path).stat().st_mtime)  # delta
    return dt.days  # + dt.seconds / 86400  # fractional days



ultralytics.utils.files.file_date(path=__file__)

์‚ฌ๋žŒ์ด ์ฝ์„ ์ˆ˜ ์žˆ๋Š” ํŒŒ์ผ ์ˆ˜์ • ๋‚ ์งœ(์˜ˆ: '2021-3-26')๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

์˜ ์†Œ์Šค ์ฝ”๋“œ ultralytics/utils/files.py
def file_date(path=__file__):
    """Return human-readable file modification date, i.e. '2021-3-26'."""
    t = datetime.fromtimestamp(Path(path).stat().st_mtime)
    return f"{t.year}-{t.month}-{t.day}"



ultralytics.utils.files.file_size(path)

ํŒŒ์ผ/๋””๋ ‰ํ† ๋ฆฌ ํฌ๊ธฐ(MB)๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

์˜ ์†Œ์Šค ์ฝ”๋“œ ultralytics/utils/files.py
def file_size(path):
    """Return file/dir size (MB)."""
    if isinstance(path, (str, Path)):
        mb = 1 << 20  # bytes to MiB (1024 ** 2)
        path = Path(path)
        if path.is_file():
            return path.stat().st_size / mb
        elif path.is_dir():
            return sum(f.stat().st_size for f in path.glob("**/*") if f.is_file()) / mb
    return 0.0



ultralytics.utils.files.get_latest_run(search_dir='.')

runs์—์„œ ๊ฐ€์žฅ ์ตœ๊ทผ์˜ 'last.pt'์— ๋Œ€ํ•œ ๊ฒฝ๋กœ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค(์ฆ‰, --resume์—์„œ).

์˜ ์†Œ์Šค ์ฝ”๋“œ ultralytics/utils/files.py
def get_latest_run(search_dir="."):
    """Return path to most recent 'last.pt' in /runs (i.e. to --resume from)."""
    last_list = glob.glob(f"{search_dir}/**/last*.pt", recursive=True)
    return max(last_list, key=os.path.getctime) if last_list else ""





2023-11-12 ์ƒ์„ฑ, 2023-11-25 ์—…๋ฐ์ดํŠธ๋จ
์ž‘์„ฑ์ž: glenn-jocher (3), Laughing-q (1)