Skip to content

path_tools

collect_frames(files)

Returns dict of source path and its frame, if from sequence

Uses clique as most precise solution, used when anatomy template that created files is not known.

Assumption is that frames are separated by '.', negative frames are not allowed.

Parameters:

Name Type Description Default
files(list) or (set with single value

list of source paths

required

Returns:

Name Type Description
dict

{'/folder/product_v001.0001.png': '0001', ....}

Source code in client/ayon_core/lib/path_tools.py
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
def collect_frames(files):
    """Returns dict of source path and its frame, if from sequence

    Uses clique as most precise solution, used when anatomy template that
    created files is not known.

    Assumption is that frames are separated by '.', negative frames are not
    allowed.

    Args:
        files(list) or (set with single value): list of source paths

    Returns:
        dict: {'/folder/product_v001.0001.png': '0001', ....}
    """

    # clique.PATTERNS["frames"] supports only `.1001.exr` not `_1001.exr` so
    # we use a customized pattern.
    pattern = "[_.](?P<index>(?P<padding>0*)\\d+)\\.\\D+\\d?$"
    patterns = [pattern]
    collections, remainder = clique.assemble(
        files, minimum_items=1, patterns=patterns)

    sources_and_frames = {}
    if collections:
        for collection in collections:
            src_head = collection.head
            src_tail = collection.tail

            for index in collection.indexes:
                src_frame = collection.format("{padding}") % index
                src_file_name = "{}{}{}".format(
                    src_head, src_frame, src_tail)
                sources_and_frames[src_file_name] = src_frame
    else:
        sources_and_frames[remainder.pop()] = None

    return sources_and_frames

Create hardlink of file.

Parameters:

Name Type Description Default
src_path(str)

Full path to a file which is used as source for hardlink.

required
dst_path(str)

Full path to a file where a link of source will be added.

required
Source code in client/ayon_core/lib/path_tools.py
31
32
33
34
35
36
37
38
39
40
def create_hard_link(src_path, dst_path):
    """Create hardlink of file.

    Args:
        src_path(str): Full path to a file which is used as source for
            hardlink.
        dst_path(str): Full path to a file where a link of source will be
            added.
    """
    os.link(src_path, dst_path)

format_file_size(file_size, suffix=None)

Returns formatted string with size in appropriate unit.

Parameters:

Name Type Description Default
file_size int

Size of file in bytes.

required
suffix str

Suffix for formatted size. Default is 'B' (as bytes).

None

Returns:

Name Type Description
str

Formatted size using proper unit and passed suffix (e.g. 7 MiB).

Source code in client/ayon_core/lib/path_tools.py
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
def format_file_size(file_size, suffix=None):
    """Returns formatted string with size in appropriate unit.

    Args:
        file_size (int): Size of file in bytes.
        suffix (str): Suffix for formatted size. Default is 'B' (as bytes).

    Returns:
        str: Formatted size using proper unit and passed suffix (e.g. 7 MiB).
    """

    if suffix is None:
        suffix = "B"

    for unit in ["", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi"]:
        if abs(file_size) < 1024.0:
            return "%3.1f%s%s" % (file_size, unit, suffix)
        file_size /= 1024.0
    return "%.1f%s%s" % (file_size, "Yi", suffix)

get_last_version_from_path(path_dir, filter)

Find last version of given directory content.

Parameters:

Name Type Description Default
path_dir str

directory path

required
filter list

list of strings used as file name filter

required

Returns:

Name Type Description
str

file name with last version

Example

last_version_file = get_last_version_from_path( "/project/shots/shot01/work", ["shot01", "compositing", "nk"])

Source code in client/ayon_core/lib/path_tools.py
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
def get_last_version_from_path(path_dir, filter):
    """Find last version of given directory content.

    Args:
        path_dir (str): directory path
        filter (list): list of strings used as file name filter

    Returns:
        str: file name with last version

    Example:
        last_version_file = get_last_version_from_path(
            "/project/shots/shot01/work", ["shot01", "compositing", "nk"])
    """

    assert os.path.isdir(path_dir), "`path_dir` argument needs to be directory"
    assert isinstance(filter, list) and (
        len(filter) != 0), "`filter` argument needs to be list and not empty"

    filtered_files = list()

    # form regex for filtering
    pattern = r".*".join(filter)

    for file in os.listdir(path_dir):
        if not re.findall(pattern, file):
            continue
        filtered_files.append(file)

    if filtered_files:
        filtered_files.sort()
        return filtered_files[-1]

    return None

get_version_from_path(file)

Find version number in file path string.

Parameters:

Name Type Description Default
file str

file path

required

Returns:

Name Type Description
str

version number in string ('001')

Source code in client/ayon_core/lib/path_tools.py
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
def get_version_from_path(file):
    """Find version number in file path string.

    Args:
        file (str): file path

    Returns:
        str: version number in string ('001')
    """

    pattern = re.compile(r"[\._]v([0-9]+)", re.IGNORECASE)
    try:
        return pattern.findall(file)[-1]
    except IndexError:
        log.error(
            "templates:get_version_from_workfile:"
            "`{}` missing version string."
            "Example `v004`".format(file)
        )

version_up(filepath)

Version up filepath to a new non-existing version.

Parses for a version identifier like _v001 or .v001 When no version present _v001 is appended as suffix.

Parameters:

Name Type Description Default
filepath str

full url

required

Returns:

Type Description
str

filepath with increased version number

Source code in client/ayon_core/lib/path_tools.py
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
def version_up(filepath):
    """Version up filepath to a new non-existing version.

    Parses for a version identifier like `_v001` or `.v001`
    When no version present _v001 is appended as suffix.

    Args:
        filepath (str): full url

    Returns:
        (str): filepath with increased version number

    """
    dirname = os.path.dirname(filepath)
    basename, ext = os.path.splitext(os.path.basename(filepath))

    regex = r"[._]v\d+"
    matches = re.findall(regex, str(basename), re.IGNORECASE)
    if not matches:
        log.info("Creating version...")
        new_label = "_v{version:03d}".format(version=1)
        new_basename = "{}{}".format(basename, new_label)
    else:
        label = matches[-1]
        version = re.search(r"\d+", label).group()
        padding = len(version)

        new_version = int(version) + 1
        new_version = '{version:0{padding}d}'.format(version=new_version,
                                                     padding=padding)
        new_label = label.replace(version, new_version, 1)
        new_basename = _rreplace(basename, label, new_label)
    new_filename = "{}{}".format(new_basename, ext)
    new_filename = os.path.join(dirname, new_filename)
    new_filename = os.path.normpath(new_filename)

    if new_filename == filepath:
        raise RuntimeError("Created path is the same as current file,"
                           "this is a bug")

    # We check for version clashes against the current file for any file
    # that matches completely in name up to the {version} label found. Thus
    # if source file was test_v001_test.txt we want to also check clashes
    # against test_v002.txt but do want to preserve the part after the version
    # label for our new filename
    clash_basename = new_basename
    if not clash_basename.endswith(new_label):
        index = (clash_basename.find(new_label))
        index += len(new_label)
        clash_basename = clash_basename[:index]

    for file in os.listdir(dirname):
        if file.endswith(ext) and file.startswith(clash_basename):
            log.info("Skipping existing version %s" % new_label)
            return version_up(new_filename)

    log.info("New version %s" % new_label)
    return new_filename