(output, inputs, no_output=False)
| 143 | |
| 144 | |
| 145 | def create_callback_id(output, inputs, no_output=False): |
| 146 | # A single dot within a dict id key or value is OK |
| 147 | # but in case of multiple dots together escape each dot |
| 148 | # with `\` so we don't mistake it for multi-outputs |
| 149 | hashed_inputs = None |
| 150 | |
| 151 | def _hash_inputs(): |
| 152 | return hashlib.sha256( |
| 153 | ".".join(str(x) for x in inputs).encode("utf-8") |
| 154 | ).hexdigest() |
| 155 | |
| 156 | def _concat(x): |
| 157 | nonlocal hashed_inputs |
| 158 | _id = x.component_id_str().replace(".", "\\.") + "." + x.component_property |
| 159 | if x.allow_duplicate: |
| 160 | if not hashed_inputs: |
| 161 | hashed_inputs = _hash_inputs() |
| 162 | # Actually adds on the property part. |
| 163 | _id += f"@{hashed_inputs}" |
| 164 | return _id |
| 165 | |
| 166 | if no_output: |
| 167 | # No output will hash the inputs. |
| 168 | # For no-input callbacks, also include the call site to make each unique |
| 169 | if not inputs: |
| 170 | # Get the call site of the @callback decorator |
| 171 | stack = inspect.stack() |
| 172 | # Walk up the stack to find the actual callback call site |
| 173 | # Fallback to empty hash if no external frame found |
| 174 | # (skip internal dash package frames) |
| 175 | dash_package_path = os.path.dirname(__file__) |
| 176 | for frame_info in stack: |
| 177 | # Skip frames from within the dash package itself |
| 178 | if not frame_info.filename.startswith(dash_package_path): |
| 179 | call_site = f"{frame_info.filename}:{frame_info.lineno}" |
| 180 | return hashlib.sha256(call_site.encode("utf-8")).hexdigest() |
| 181 | |
| 182 | return _hash_inputs() |
| 183 | |
| 184 | if isinstance(output, (list, tuple)): |
| 185 | return ".." + "...".join(_concat(x) for x in output) + ".." |
| 186 | |
| 187 | return _concat(output) |
| 188 | |
| 189 | |
| 190 | # inverse of create_callback_id - should only be relevant if an old renderer is |
searching dependent graphs…