Skip to content

Commit a9791c5

Browse files
committed
Merge remote-tracking branch 'origin/dev' into dhruv/1780/quality-panel
2 parents 0c33d9a + ca0f6d0 commit a9791c5

File tree

144 files changed

+5884
-1632
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

144 files changed

+5884
-1632
lines changed

.github/workflows/FusionTyping.yml

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
name: Fusion - mypy Typing Validation
2+
3+
on:
4+
workflow_dispatch: {}
5+
push:
6+
branches: [ prod, dev ]
7+
pull_request:
8+
branches: [ prod, dev ]
9+
10+
jobs:
11+
mypy:
12+
name: Run mypy
13+
runs-on: ubuntu-latest
14+
defaults:
15+
run:
16+
working-directory: ./exporter/SynthesisFusionAddin
17+
steps:
18+
- uses: actions/checkout@v4
19+
- uses: actions/setup-python@v5
20+
with:
21+
python-version: '3.11'
22+
- run: pip install -r requirements-mypy.txt
23+
- run: mypy

.gitignore

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
.vs/
22
.vscode/
3-
/build/
3+
build/
4+
dist/
45
*.log
56
.DS_Store
7+
*.pkg
8+
*.exe

exporter/SynthesisFusionAddin/Synthesis.manifest

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"description": {
77
"": "Synthesis Exporter"
88
},
9-
"version": "1.0.0",
9+
"version": "2.0.0",
1010
"runOnStartup": true,
1111
"supportedOS": "windows|mac",
1212
"editEnabled": true

exporter/SynthesisFusionAddin/Synthesis.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import os
22
import sys
3+
from typing import Any
34

45
import adsk.core
56

@@ -43,7 +44,7 @@
4344

4445

4546
@logFailure
46-
def run(_):
47+
def run(_context: dict[str, Any]) -> None:
4748
"""## Entry point to application from Fusion.
4849
4950
Arguments:
@@ -63,7 +64,7 @@ def run(_):
6364

6465

6566
@logFailure
66-
def stop(_):
67+
def stop(_context: dict[str, Any]) -> None:
6768
"""## Fusion exit point - deconstructs buttons and handlers
6869
6970
Arguments:
+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[mypy]
2+
files = Synthesis.py, src
3+
warn_unused_configs = True
4+
check_untyped_defs = True
5+
warn_unreachable = True
6+
warn_redundant_casts = True
7+
warn_unused_ignores = True
8+
warn_no_return = True
9+
warn_return_any = True
10+
strict = True
11+
ignore_missing_imports = True
12+
follow_imports = skip
13+
disallow_subclassing_any = False
14+
disable_error_code = no-untyped-call

exporter/SynthesisFusionAddin/proto/build.bat

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22
md .\proto_out\
33
@RD /S /Q "./proto_out/__pycache__"
44
@echo on
5-
protoc -I=../../../mirabuf --python_out=./proto_out ../../../mirabuf/*.proto
5+
protoc -I=../../../mirabuf --python_out=./proto_out --mypy_out=./proto_out ../../../mirabuf/*.proto
66
@echo off
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
rm -rf -v ./proto_out
22
mkdir ./proto_out
33
git submodule update --init --recursive
4-
protoc -I=../../../mirabuf --python_out=./proto_out ../../../mirabuf/*.proto
4+
protoc -I=../../../mirabuf --python_out=./proto_out --mypy_out=./proto_out ../../../mirabuf/*.proto
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
black
2+
isort
23
pyminifier
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
mypy
2+
types-protobuf
3+
types-requests

exporter/SynthesisFusionAddin/src/APS/APS.py

+29-16
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import http.client
12
import json
23
import os
34
import pathlib
@@ -52,21 +53,21 @@ def getAPSAuth() -> APSAuth | None:
5253
return APS_AUTH
5354

5455

55-
def _res_json(res):
56-
return json.loads(res.read().decode(res.info().get_param("charset") or "utf-8"))
56+
def _res_json(res: http.client.HTTPResponse) -> dict[str, Any]:
57+
return dict(json.loads(res.read().decode(str(res.info().get_param("charset")) or "utf-8")))
5758

5859

5960
def getCodeChallenge() -> str | None:
6061
endpoint = "https://synthesis.autodesk.com/api/aps/challenge/"
61-
res = urllib.request.urlopen(endpoint)
62+
res: http.client.HTTPResponse = urllib.request.urlopen(endpoint)
6263
data = _res_json(res)
63-
return data["challenge"]
64+
return str(data["challenge"])
6465

6566

6667
def getAuth() -> APSAuth | None:
6768
global APS_AUTH
6869
if APS_AUTH is not None:
69-
return APS_AUTH
70+
return APS_AUTH # type: ignore[unreachable]
7071

7172
currTime = time.time()
7273
if os.path.exists(auth_path):
@@ -86,7 +87,7 @@ def getAuth() -> APSAuth | None:
8687
return APS_AUTH
8788

8889

89-
def convertAuthToken(code: str):
90+
def convertAuthToken(code: str) -> None:
9091
global APS_AUTH
9192
authUrl = f'https://synthesis.autodesk.com/api/aps/code/?code={code}&redirect_uri={urllib.parse.quote_plus("https://synthesis.autodesk.com/api/aps/exporter/")}'
9293
res = urllib.request.urlopen(authUrl)
@@ -106,14 +107,14 @@ def convertAuthToken(code: str):
106107
_ = loadUserInfo()
107108

108109

109-
def removeAuth():
110+
def removeAuth() -> None:
110111
global APS_AUTH, APS_USER_INFO
111112
APS_AUTH = None
112113
APS_USER_INFO = None
113114
pathlib.Path.unlink(pathlib.Path(auth_path))
114115

115116

116-
def refreshAuthToken():
117+
def refreshAuthToken() -> None:
117118
global APS_AUTH
118119
if APS_AUTH is None or APS_AUTH.refresh_token is None:
119120
raise Exception("No refresh token found.")
@@ -178,6 +179,8 @@ def loadUserInfo() -> APSUserInfo | None:
178179
removeAuth()
179180
logger.error(f"User Info Error:\n{e.code} - {e.reason}")
180181
gm.ui.messageBox("Please sign in again.")
182+
finally:
183+
return None
181184

182185

183186
def getUserInfo() -> APSUserInfo | None:
@@ -259,20 +262,30 @@ def upload_mirabuf(project_id: str, folder_id: str, file_name: str, file_content
259262
global APS_AUTH
260263
if APS_AUTH is None:
261264
gm.ui.messageBox("You must login to upload designs to APS", "USER ERROR")
265+
return None
266+
262267
auth = APS_AUTH.access_token
263268
# Get token from APS API later
264269

265270
new_folder_id = get_item_id(auth, project_id, folder_id, "MirabufDir", "folders")
266271
if new_folder_id is None:
267-
folder_id = create_folder(auth, project_id, folder_id, "MirabufDir")
272+
created_folder_id = create_folder(auth, project_id, folder_id, "MirabufDir")
268273
else:
269-
folder_id = new_folder_id
270-
(lineage_id, file_id, file_version) = get_file_id(auth, project_id, folder_id, file_name)
274+
created_folder_id = new_folder_id
275+
276+
if created_folder_id is None:
277+
return None
278+
279+
file_id_data = get_file_id(auth, project_id, created_folder_id, file_name)
280+
if file_id_data is None:
281+
return None
282+
283+
(lineage_id, file_id, file_version) = file_id_data
271284

272285
"""
273286
Create APS Storage Location
274287
"""
275-
object_id = create_storage_location(auth, project_id, folder_id, file_name)
288+
object_id = create_storage_location(auth, project_id, created_folder_id, file_name)
276289
if object_id is None:
277290
gm.ui.messageBox("UPLOAD ERROR", "Object id is none; check create storage location")
278291
return None
@@ -297,10 +310,10 @@ def upload_mirabuf(project_id: str, folder_id: str, file_name: str, file_content
297310
return None
298311
if file_id != "":
299312
update_file_version(
300-
auth, project_id, folder_id, lineage_id, file_id, file_name, file_contents, file_version, object_id
313+
auth, project_id, created_folder_id, lineage_id, file_id, file_name, file_contents, file_version, object_id
301314
)
302315
else:
303-
_lineage_info = create_first_file_version(auth, str(object_id), project_id, str(folder_id), file_name)
316+
_lineage_info = create_first_file_version(auth, str(object_id), project_id, str(created_folder_id), file_name)
304317
return ""
305318

306319

@@ -376,7 +389,7 @@ def get_item_id(auth: str, project_id: str, parent_folder_id: str, folder_name:
376389
return ""
377390
for item in data:
378391
if item["type"] == item_type and item["attributes"]["name"] == folder_name:
379-
return item["id"]
392+
return str(item["id"])
380393
return None
381394

382395

@@ -500,7 +513,7 @@ def get_file_id(auth: str, project_id: str, folder_id: str, file_name: str) -> t
500513
elif not file_res.ok:
501514
gm.ui.messageBox(f"UPLOAD ERROR: {file_res.text}", "Failed to get file")
502515
return None
503-
file_json: list[dict[str, Any]] = file_res.json()
516+
file_json: dict[str, Any] = file_res.json()
504517
if len(file_json["data"]) == 0:
505518
return ("", "", "")
506519
id: str = str(file_json["data"][0]["id"])

exporter/SynthesisFusionAddin/src/Dependencies.py

+8-4
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,14 @@
2121

2222

2323
@logFailure
24-
def getInternalFusionPythonInstillationFolder() -> str:
24+
def getInternalFusionPythonInstillationFolder() -> str | os.PathLike[str]:
2525
# Thank you Kris Kaplan
2626
# Find the folder location where the Autodesk python instillation keeps the 'os' standard library module.
27-
pythonStandardLibraryModulePath = importlib.machinery.PathFinder.find_spec("os", sys.path).origin
27+
pythonOSModulePath = importlib.machinery.PathFinder.find_spec("os", sys.path)
28+
if pythonOSModulePath:
29+
pythonStandardLibraryModulePath = pythonOSModulePath.origin or "ERROR"
30+
else:
31+
raise BaseException("Could not locate spec 'os'")
2832

2933
# Depending on platform, adjust to folder to where the python executable binaries are stored.
3034
if SYSTEM == "Windows":
@@ -36,10 +40,10 @@ def getInternalFusionPythonInstillationFolder() -> str:
3640
return folder
3741

3842

39-
def executeCommand(*args: str) -> subprocess.CompletedProcess:
43+
def executeCommand(*args: str) -> subprocess.CompletedProcess[str]:
4044
logger.debug(f"Running Command -> {' '.join(args)}")
4145
try:
42-
result: subprocess.CompletedProcess = subprocess.run(
46+
result: subprocess.CompletedProcess[str] = subprocess.run(
4347
args, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, check=True
4448
)
4549
logger.debug(f"Command Output:\n{result.stdout}")
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,53 @@
11
""" Initializes the global variables that are set in the run method to reduce hanging commands. """
22

3+
from typing import Any
4+
35
import adsk.core
46
import adsk.fusion
57

68

7-
class GlobalManager(object):
8-
"""Global Manager instance"""
9-
10-
class __GlobalManager:
11-
def __init__(self):
12-
self.app = adsk.core.Application.get()
13-
14-
if self.app:
15-
self.ui = self.app.userInterface
16-
17-
self.connected = False
18-
""" Is unity currently connected """
19-
20-
self.uniqueIds = []
21-
""" Collection of unique ID values to not overlap """
22-
23-
self.elements = []
24-
""" Unique constructed buttons to delete """
25-
26-
self.palettes = []
27-
""" Unique constructed palettes to delete """
9+
class GlobalManager:
10+
def __init__(self) -> None:
11+
self.app = adsk.core.Application.get()
2812

29-
self.handlers = []
30-
""" Object to store all event handlers to custom events like saving. """
13+
if self.app:
14+
self.ui = self.app.userInterface
3115

32-
self.tabs = []
33-
""" Set of Tab objects to keep track of. """
16+
self.connected = False
17+
""" Is unity currently connected """
3418

35-
self.queue = []
36-
""" This will eventually implement the Python SimpleQueue synchronized workflow
37-
- this is the list of objects being sent
38-
"""
19+
self.uniqueIds: list[str] = [] # type of HButton
20+
""" Collection of unique ID values to not overlap """
3921

40-
self.files = []
22+
self.elements: list[Any] = []
23+
""" Unique constructed buttons to delete """
4124

42-
def __str__(self):
43-
return "GlobalManager"
25+
# Transition: AARD-1765
26+
# Will likely be removed later as this is no longer used. Avoiding adding typing for now.
27+
self.palettes = [] # type: ignore
28+
""" Unique constructed palettes to delete """
4429

45-
def clear(self):
46-
for attr, value in self.__dict__.items():
47-
if isinstance(value, list):
48-
setattr(self, attr, [])
30+
self.handlers: list[adsk.core.EventHandler] = []
31+
""" Object to store all event handlers to custom events like saving. """
4932

50-
instance = None
33+
self.tabs: list[adsk.core.ToolbarPanel] = []
34+
""" Set of Tab objects to keep track of. """
5135

52-
def __new__(cls):
53-
if not GlobalManager.instance:
54-
GlobalManager.instance = GlobalManager.__GlobalManager()
36+
# Transition: AARD-1765
37+
# Will likely be removed later as this is no longer used. Avoiding adding typing for now.
38+
self.queue = [] # type: ignore
39+
""" This will eventually implement the Python SimpleQueue synchronized workflow
40+
- this is the list of objects being sent
41+
"""
5542

56-
return GlobalManager.instance
43+
# Transition: AARD-1765
44+
# Will likely be removed later as this is no longer used. Avoiding adding typing for now.
45+
self.files = [] # type: ignore
5746

58-
def __getattr__(self, name):
59-
return getattr(self.instance, name)
47+
def __str__(self) -> str:
48+
return "GlobalManager"
6049

61-
def __setattr__(self, name):
62-
return setattr(self.instance, name)
50+
def clear(self) -> None:
51+
for attr, value in self.__dict__.items():
52+
if isinstance(value, list):
53+
setattr(self, attr, [])

0 commit comments

Comments
 (0)