Fusion API: Milestone.name is documented as read/write but setter is ignored for existing milestones

Fusion API: Milestone.name is documented as read/write but setter is ignored for existing milestones

ppoloczek
Explorer Explorer
176 Views
1 Reply
Message 1 of 2

Fusion API: Milestone.name is documented as read/write but setter is ignored for existing milestones

ppoloczek
Explorer
Explorer

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=True

Additional 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

  1. Is Milestone.name supposed to be editable for existing milestones returned from dataFile.milestones?
  2. If yes, why does the Python setter silently fail/no-op?
  3. If no, can the documentation be updated to say that existing milestone names are read-only?
  4. Is there a supported public Fusion API for editing the same visible history/milestone description that the Fusion UI edits?
  5. Should the setter raise an exception if changing existing milestone names is not supported?

Thanks.

0 Likes
177 Views
1 Reply
Reply (1)
Message 2 of 2

kandennti
Mentor
Mentor

Hi @ppoloczek -san.

I tried it, and the milestone name was updated.

Windows11 Fusion 360 Ver. 2702.1.58

0 Likes