Fusion API: Milestone.name is documented as read/write but setter is ignored for existing milestones
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report
Hi Autodesk Fusion API team,
I think there may be a bug or documentation mismatch in the Fusion Python API for adsk.core.Milestone.name.
According to the API documentation, Milestone.name:
Gets and sets the name of the milestone.
It is documented as a read/write string property.
However, when I try to change the name of an existing milestone returned from dataFile.milestones, the assignment completes without exception, but the value does not change. Reading milestone.name immediately after assignment still returns the old value.
Environment
- Product: Autodesk Fusion
- API: Fusion Python API
- OS: Windows
- File type: Cloud Fusion design with existing milestones
- File is saved: Yes
- Existing milestones: Yes
Expected behavior
This code should change the milestone name:
target_milestone.name = "-"or, if existing milestone names cannot be changed, the API should raise an exception or the documentation should say that existing milestone names are read-only.
Actual behavior
The assignment completes without exception, but the name remains unchanged.
Example result:
Target version: 15
Before assignment:
milestone.name = 'D'
Tried:
milestone.name = '-'
Immediately after assignment:
milestone.name = 'D'
Assignment completed without exception.Minimal reproduction script
import adsk.core
import adsk.fusion
import traceback
app = adsk.core.Application.get()
ui = app.userInterface
TARGET_VERSION_NUMBER = 15
NEW_MILESTONE_NAME = "-"
def get_sorted_milestones(data_file):
result = []
milestones = data_file.milestones
for i in range(milestones.count):
milestone = milestones.item(i)
try:
version_number = milestone.version.versionNumber
except Exception:
version_number = 0
result.append((version_number, milestone))
result.sort(key=lambda x: x[0])
return [x[1] for x in result]
def find_milestone_by_version(data_file, target_version_number):
for milestone in get_sorted_milestones(data_file):
try:
if milestone.version.versionNumber == target_version_number:
return milestone
except Exception:
pass
return None
def list_milestones(data_file):
lines = []
for milestone in get_sorted_milestones(data_file):
try:
version_number = milestone.version.versionNumber
except Exception:
version_number = "?"
try:
name = milestone.name
except Exception:
name = "<error reading name>"
try:
description = milestone.version.description
except Exception:
description = "<error reading description>"
try:
is_valid = milestone.isValid
except Exception:
is_valid = "?"
lines.append(
f"Version {version_number}: "
f"name='{name}', "
f"description='{description}', "
f"isValid={is_valid}"
)
return "\n".join(lines)
def run(_context):
try:
document = app.activeDocument
if not document:
ui.messageBox("No active document.")
return
design = adsk.fusion.Design.cast(app.activeProduct)
if not design:
ui.messageBox("Active product is not a Fusion design.")
return
document = design.parentDocument
data_file = document.dataFile
before_text = list_milestones(data_file)
target_milestone = find_milestone_by_version(
data_file,
TARGET_VERSION_NUMBER
)
if not target_milestone:
ui.messageBox(
f"Could not find milestone for version {TARGET_VERSION_NUMBER}.\n\n"
f"Existing milestones:\n\n{before_text}"
)
return
old_name = target_milestone.name
rename_error = None
try:
target_milestone.name = NEW_MILESTONE_NAME
except Exception:
rename_error = traceback.format_exc()
immediate_name = target_milestone.name
after_text = list_milestones(data_file)
message = (
"Milestone rename test finished.\n\n"
f"Document: {document.name}\n"
f"Target version: {TARGET_VERSION_NUMBER}\n\n"
f"Before assignment:\n"
f"milestone.name = '{old_name}'\n\n"
f"Tried:\n"
f"milestone.name = '{NEW_MILESTONE_NAME}'\n\n"
f"Immediately after assignment:\n"
f"milestone.name = '{immediate_name}'\n\n"
)
if rename_error:
message += (
"Assignment raised an exception:\n"
f"{rename_error}\n\n"
)
else:
message += "Assignment completed without exception.\n\n"
message += (
"Milestones BEFORE:\n"
f"{before_text}\n\n"
"Milestones AFTER:\n"
f"{after_text}"
)
ui.messageBox(message)
except Exception:
ui.messageBox(f"Script error:\n\n{traceback.format_exc()}")Actual output
Milestone rename test finished.
Document: 2003_0010_SHT___Blacha okragla
Target version: 15
Before assignment:
milestone.name = 'D'
Tried:
milestone.name = '-'
Immediately after assignment:
milestone.name = 'D'
Assignment completed without exception.
Milestones BEFORE:
Version 3: name='A', description='Milestone A created with updated part number 2003_0010_A_SHT___Blacha okragla', isValid=True
Version 4: name='B', description='User Saved', isValid=True
Version 5: name='API test version from saveDataVersion()', description='User Saved', isValid=True
Version 14: name='C', description='User Saved', isValid=True
Version 15: name='D', description='User Saved', isValid=True
Version 16: name='D', description='User Saved', isValid=True
Version 17: name='D', description='User Saved', isValid=True
Version 18: name='D', description='User Saved', isValid=True
Milestones AFTER:
Version 3: name='A', description='Milestone A created with updated part number 2003_0010_A_SHT___Blacha okragla', isValid=True
Version 4: name='B', description='User Saved', isValid=True
Version 5: name='API test version from saveDataVersion()', description='User Saved', isValid=True
Version 14: name='C', description='User Saved', isValid=True
Version 15: name='D', description='User Saved', isValid=True
Version 16: name='D', description='User Saved', isValid=True
Version 17: name='D', description='User Saved', isValid=True
Version 18: name='D', description='User Saved', isValid=TrueAdditional observation
The Fusion history UI / web viewer can edit the visible history entry text manually. I inspected the network request in Developer Tools and saw that the UI uses an internal GraphQL mutation:
mutation CDEHistory_UpdateHistoryChangeDescription($input: UpdateHistoryChangeDescriptionInput!) {
updateHistoryChangeDescription(input: $input) {
historyChange {
id
description
author {
lastName
firstName
}
timestamp
}
}
}The request payload contains:
{
"description": "-",
"historyChangeId": "<redacted>"
}The response confirms:
{
"description": "-"
}However, after changing the visible history entry in the UI, the Fusion Python API still reports the original milestone.name value.
This suggests that the Fusion UI is editing a historyChange.description field, while the Python API exposes Milestone.name, which either points to a different backend field or has a non-functional setter for existing milestones.
Questions
- Is Milestone.name supposed to be editable for existing milestones returned from dataFile.milestones?
- If yes, why does the Python setter silently fail/no-op?
- If no, can the documentation be updated to say that existing milestone names are read-only?
- Is there a supported public Fusion API for editing the same visible history/milestone description that the Fusion UI edits?
- Should the setter raise an exception if changing existing milestone names is not supported?
Thanks.