Skip to content

Commit cd1b831

Browse files
Fix static type checker errors
Fixes #7 Fix static type checker errors and add type annotations across multiple files. * **onshape_api/connect.py** - Add type annotations to all functions and methods. - Fix type errors in `Client` class methods. - Ensure all imports are correctly typed. * **onshape_api/data/preprocess.py** - Add type annotations to all functions. - Fix type errors in `extract_ids` and `get_assembly_df` functions. * **onshape_api/graph.py** - Add type annotations to all functions. - Fix type inconsistencies in `create_graph` and `get_robot_link` functions. * **onshape_api/log.py** - Add type annotations to all methods. - Fix type inconsistencies in `Logger` class methods. * **onshape_api/models/assembly.py** - Add type annotations to all classes and methods. - Fix type issues in `PartInstance` and `AssemblyInstance` classes. * **onshape_api/models/document.py** - Add type annotations to all classes and methods. - Fix type issues in `Document` and `DocumentMetaData` classes. * **onshape_api/models/element.py** - Add type annotations to all classes and methods. - Fix type issues in `Element` class. --- For more details, open the [Copilot Workspace session](https://copilot-workspace.githubnext.com/imsenthur/onshape-api/issues/7?shareId=XXXX-XXXX-XXXX-XXXX).
1 parent 6a2e8fb commit cd1b831

File tree

9 files changed

+118
-93
lines changed

9 files changed

+118
-93
lines changed

onshape_api/connect.py

Lines changed: 52 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import secrets
77
import string
88
from enum import Enum
9-
from typing import BinaryIO
9+
from typing import Any, BinaryIO, Optional, Union
1010
from urllib.parse import parse_qs, urlencode, urlparse
1111

1212
import requests
@@ -32,7 +32,7 @@ class HTTP(str, Enum):
3232
DELETE = "delete"
3333

3434

35-
def load_env_variables(env):
35+
def load_env_variables(env: str) -> tuple[str, str]:
3636
"""
3737
Load environment variables from the specified .env file.
3838
@@ -61,7 +61,7 @@ def load_env_variables(env):
6161
return access_key, secret_key
6262

6363

64-
def make_nonce():
64+
def make_nonce() -> str:
6565
"""
6666
Generate a unique ID for the request, 25 chars in length
6767
@@ -85,7 +85,7 @@ class Client:
8585
- logging (bool, default=True): Turn logging on or off
8686
"""
8787

88-
def __init__(self, env="./.env", log_file="./onshape.log", log_level=1):
88+
def __init__(self, env: str = "./.env", log_file: str = "./onshape.log", log_level: int = 1) -> None:
8989
"""
9090
Instantiates an instance of the Onshape class. Reads credentials from a .env file.
9191
@@ -96,13 +96,13 @@ def __init__(self, env="./.env", log_file="./onshape.log", log_level=1):
9696
- env (str, default='./.env'): Environment file location
9797
"""
9898

99-
self._url = BASE_URL
99+
self._url: str = BASE_URL
100100
self._access_key, self._secret_key = load_env_variables(env)
101101
LOGGER.set_file_name(log_file)
102102
LOGGER.set_stream_level(LOG_LEVEL[log_level])
103103
LOGGER.info(f"Onshape API initialized with env file: {env}")
104104

105-
def get_document(self, did):
105+
def get_document(self, did: str) -> DocumentMetaData:
106106
"""
107107
Get details for a specified document.
108108
@@ -116,7 +116,7 @@ def get_document(self, did):
116116

117117
return DocumentMetaData.model_validate(_request_json)
118118

119-
def get_elements(self, did, wtype, wid):
119+
def get_elements(self, did: str, wtype: str, wid: str) -> dict[str, Element]:
120120
"""
121121
Get list of elements in a document.
122122
@@ -138,7 +138,7 @@ def get_elements(self, did, wtype, wid):
138138

139139
return {element["name"]: Element.model_validate(element) for element in _elements_json}
140140

141-
def get_features_from_partstudio(self, did, wid, eid):
141+
def get_features_from_partstudio(self, did: str, wid: str, eid: str) -> requests.Response:
142142
"""
143143
Gets the feature list for specified document / workspace / part studio.
144144
@@ -156,7 +156,7 @@ def get_features_from_partstudio(self, did, wid, eid):
156156
"/api/partstudios/d/" + did + "/w/" + wid + "/e/" + eid + "/features",
157157
)
158158

159-
def get_features_from_assembly(self, did, wtype, wid, eid):
159+
def get_features_from_assembly(self, did: str, wtype: str, wid: str, eid: str) -> dict[str, Any]:
160160
"""
161161
Gets the feature list for specified document / workspace / part studio.
162162
@@ -173,7 +173,7 @@ def get_features_from_assembly(self, did, wtype, wid, eid):
173173
"get", "/api/assemblies/d/" + did + "/" + wtype + "/" + wid + "/e/" + eid + "/features"
174174
).json()
175175

176-
def get_variables(self, did, wid, eid):
176+
def get_variables(self, did: str, wid: str, eid: str) -> dict[str, Variable]:
177177
"""
178178
Get list of variables in a variable studio.
179179
@@ -194,7 +194,7 @@ def get_variables(self, did, wid, eid):
194194

195195
return {variable["name"]: Variable.model_validate(variable) for variable in _variables_json[0]["variables"]}
196196

197-
def set_variables(self, did, wid, eid, variables):
197+
def set_variables(self, did: str, wid: str, eid: str, variables: dict[str, Variable]) -> requests.Response:
198198
"""
199199
Set variables in a variable studio.
200200
@@ -219,7 +219,7 @@ def set_variables(self, did, wid, eid, variables):
219219
body=payload,
220220
)
221221

222-
def create_assembly(self, did, wid, name="My Assembly"):
222+
def create_assembly(self, did: str, wid: str, name: str = "My Assembly") -> requests.Response:
223223
"""
224224
Creates a new assembly element in the specified document / workspace.
225225
@@ -236,7 +236,9 @@ def create_assembly(self, did, wid, name="My Assembly"):
236236

237237
return self.request(HTTP.POST, "/api/assemblies/d/" + did + "/w/" + wid, body=payload)
238238

239-
def get_assembly(self, did, wtype, wid, eid, configuration="default"):
239+
def get_assembly(
240+
self, did: str, wtype: str, wid: str, eid: str, configuration: str = "default"
241+
) -> tuple[Assembly, dict[str, Any]]:
240242
_request_path = "/api/assemblies/d/" + did + "/" + wtype + "/" + wid + "/e/" + eid
241243
_assembly_json = self.request(
242244
HTTP.GET,
@@ -251,10 +253,10 @@ def get_assembly(self, did, wtype, wid, eid, configuration="default"):
251253

252254
return Assembly.model_validate(_assembly_json), _assembly_json
253255

254-
def get_parts(self, did, wid, eid):
256+
def get_parts(self, did: str, wid: str, eid: str) -> None:
255257
pass
256258

257-
def download_stl(self, did, wid, eid, partID, buffer: BinaryIO):
259+
def download_stl(self, did: str, wid: str, eid: str, partID: str, buffer: BinaryIO) -> None:
258260
"""
259261
Exports STL export from a part studio and saves it to a file.
260262
@@ -288,7 +290,7 @@ def download_stl(self, did, wid, eid, partID, buffer: BinaryIO):
288290
else:
289291
LOGGER.info(f"Failed to download STL file: {response.status_code} - {response.text}")
290292

291-
def get_mass_properties(self, did, wid, eid, partID):
293+
def get_mass_properties(self, did: str, wid: str, eid: str, partID: str) -> MassModel:
292294
"""
293295
Get mass properties for a part in a part studio.
294296
@@ -306,7 +308,16 @@ def get_mass_properties(self, did, wid, eid, partID):
306308

307309
return MassModel.model_validate(_resonse_json["bodies"][partID])
308310

309-
def request(self, method, path, query=None, headers=None, body=None, base_url=None, log_response=True):
311+
def request(
312+
self,
313+
method: Union[HTTP, str],
314+
path: str,
315+
query: Optional[dict[str, Any]] = None,
316+
headers: Optional[dict[str, str]] = None,
317+
body: Optional[dict[str, Any]] = None,
318+
base_url: Optional[str] = None,
319+
log_response: bool = True,
320+
) -> requests.Response:
310321
"""
311322
Issues a request to Onshape
312323
@@ -347,10 +358,12 @@ def request(self, method, path, query=None, headers=None, body=None, base_url=No
347358

348359
return res
349360

350-
def _build_url(self, base_url, path, query):
361+
def _build_url(self, base_url: str, path: str, query: dict[str, Any]) -> str:
351362
return base_url + path + "?" + urlencode(query)
352363

353-
def _send_request(self, method, url, headers, body):
364+
def _send_request(
365+
self, method: Union[HTTP, str], url: str, headers: dict[str, str], body: Optional[dict[str, Any]]
366+
) -> requests.Response:
354367
return requests.request(
355368
method,
356369
url,
@@ -361,7 +374,9 @@ def _send_request(self, method, url, headers, body):
361374
timeout=10, # Specify an appropriate timeout value in seconds
362375
)
363376

364-
def _handle_redirect(self, res, method, headers, log_response=True):
377+
def _handle_redirect(
378+
self, res: requests.Response, method: Union[HTTP, str], headers: dict[str, str], log_response: bool = True
379+
) -> requests.Response:
365380
location = urlparse(res.headers["Location"])
366381
querystring = parse_qs(location.query)
367382

@@ -374,13 +389,21 @@ def _handle_redirect(self, res, method, headers, log_response=True):
374389
method, location.path, query=new_query, headers=headers, base_url=new_base_url, log_response=log_response
375390
)
376391

377-
def _log_response(self, res):
392+
def _log_response(self, res: requests.Response) -> None:
378393
if not 200 <= res.status_code <= 206:
379394
LOGGER.debug(f"Request failed, details: {res.text}")
380395
else:
381396
LOGGER.debug(f"Request succeeded, details: {res.text}")
382397

383-
def _make_auth(self, method, date, nonce, path, query=None, ctype="application/json"):
398+
def _make_auth(
399+
self,
400+
method: Union[HTTP, str],
401+
date: str,
402+
nonce: str,
403+
path: str,
404+
query: Optional[dict[str, Any]] = None,
405+
ctype: str = "application/json",
406+
) -> str:
384407
"""
385408
Create the request signature to authenticate
386409
@@ -412,7 +435,13 @@ def _make_auth(self, method, date, nonce, path, query=None, ctype="application/j
412435

413436
return auth
414437

415-
def _make_headers(self, method, path, query=None, headers=None):
438+
def _make_headers(
439+
self,
440+
method: Union[HTTP, str],
441+
path: str,
442+
query: Optional[dict[str, Any]] = None,
443+
headers: Optional[dict[str, str]] = None,
444+
) -> dict[str, str]:
416445
"""
417446
Creates a headers object to sign the request
418447

onshape_api/data/preprocess.py

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,22 @@
11
import json
22
import os
33
import re
4-
54
import pandas as pd
6-
75
import onshape_api as osa
86

97
AUTOMATE_ASSEMBLYID_PATTERN = r"(?P<documentId>\w{24})_(?P<documentMicroversion>\w{24})_(?P<elementId>\w{24})"
108

11-
12-
def extract_ids(assembly_id):
9+
def extract_ids(assembly_id: str) -> dict[str, str | None]:
1310
match = re.match(AUTOMATE_ASSEMBLYID_PATTERN, assembly_id)
1411
if match:
1512
return match.groupdict()
1613
else:
1714
return {"documentId": None, "documentMicroversion": None, "elementId": None}
1815

19-
20-
def get_assembly_df(automate_assembly_df):
16+
def get_assembly_df(automate_assembly_df: pd.DataFrame) -> pd.DataFrame:
2117
assembly_df = automate_assembly_df["assemblyId"].apply(extract_ids).apply(pd.Series)
2218
return assembly_df
2319

24-
2520
if __name__ == "__main__":
2621
client = osa.Client()
2722

onshape_api/graph.py

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import io
22
import os
33
import random
4-
from typing import Optional, Union
4+
from typing import Optional, Union, Dict, Tuple, List
55

66
import matplotlib.pyplot as plt
77
import networkx as nx
@@ -39,7 +39,7 @@
3939
CURRENT_DIR = os.getcwd()
4040

4141

42-
def generate_names(max_length: int) -> list[str]:
42+
def generate_names(max_length: int) -> List[str]:
4343
words_file_path = os.path.join(SCRIPT_DIR, "words.txt")
4444

4545
with open(words_file_path) as file:
@@ -51,25 +51,25 @@ def generate_names(max_length: int) -> list[str]:
5151
return random.sample(words, max_length)
5252

5353

54-
def show_graph(graph: nx.Graph):
54+
def show_graph(graph: nx.Graph) -> None:
5555
nx.draw_circular(graph, with_labels=True)
5656
plt.show()
5757

5858

59-
def convert_to_digraph(graph: nx.Graph) -> nx.DiGraph:
59+
def convert_to_digraph(graph: nx.Graph) -> Tuple[nx.DiGraph, str]:
6060
_centrality = nx.closeness_centrality(graph)
6161
_root_node = max(_centrality, key=_centrality.get)
6262
_graph = nx.bfs_tree(graph, _root_node)
6363
return _graph, _root_node
6464

6565

6666
def create_graph(
67-
occurences: dict[str, Occurrence],
68-
instances: dict[str, Instance],
69-
parts: dict[str, Part],
70-
mates: dict[str, MateFeatureData],
67+
occurences: Dict[str, Occurrence],
68+
instances: Dict[str, Instance],
69+
parts: Dict[str, Part],
70+
mates: Dict[str, MateFeatureData],
7171
directed: bool = True,
72-
):
72+
) -> Union[nx.Graph, Tuple[nx.DiGraph, str]]:
7373
graph = nx.Graph()
7474

7575
for occurence in occurences:
@@ -90,14 +90,14 @@ def create_graph(
9090
LOGGER.warning(f"Mate {mate} not found")
9191

9292
if directed:
93-
graph = convert_to_digraph(graph)
93+
graph, root_node = convert_to_digraph(graph)
9494

9595
LOGGER.info(f"Graph created with {len(graph.nodes)} nodes and {len(graph.edges)} edges")
9696

9797
return graph
9898

9999

100-
def download_stl_mesh(did, wid, eid, partID, client: Client, transform: np.ndarray, file_name: str) -> str:
100+
def download_stl_mesh(did: str, wid: str, eid: str, partID: str, client: Client, transform: np.ndarray, file_name: str) -> str:
101101
try:
102102
with io.BytesIO() as buffer:
103103
LOGGER.info(f"Downloading mesh for {file_name}...")
@@ -128,7 +128,7 @@ def get_robot_link(
128128
workspaceId: str,
129129
client: Client,
130130
mate: Optional[Union[MateFeatureData, None]] = None,
131-
):
131+
) -> Tuple[Link, np.matrix]:
132132
LOGGER.info(f"Creating robot link for {name}")
133133

134134
if mate is None:
@@ -190,7 +190,7 @@ def get_robot_joint(
190190
child: str,
191191
mate: MateFeatureData,
192192
stl_to_parent_tf: np.matrix,
193-
):
193+
) -> Union[RevoluteJoint, FixedJoint]:
194194
LOGGER.info(f"Creating robot joint from {parent} to {child}")
195195

196196
parent_to_mate_tf = mate.matedEntities[1].matedCS.part_to_mate_tf
@@ -228,20 +228,20 @@ def get_robot_joint(
228228
def get_urdf_components(
229229
graph: Union[nx.Graph, nx.DiGraph],
230230
workspaceId: str,
231-
parts: dict[str, Part],
232-
mass_properties: dict[str, MassModel],
233-
mates: dict[str, MateFeatureData],
231+
parts: Dict[str, Part],
232+
mass_properties: Dict[str, MassModel],
233+
mates: Dict[str, MateFeatureData],
234234
client: Client,
235-
):
235+
) -> Tuple[List[Link], List[Union[RevoluteJoint, FixedJoint]]]:
236236
if not isinstance(graph, nx.DiGraph):
237237
graph, root_node = convert_to_digraph(graph)
238238

239-
joints = []
240-
links = []
239+
joints: List[Union[RevoluteJoint, FixedJoint]] = []
240+
links: List[Link] = []
241241

242242
_readable_names = generate_names(len(graph.nodes))
243243
_readable_names_mapping = dict(zip(graph.nodes, _readable_names))
244-
_stl_to_link_tf_mapping = {}
244+
_stl_to_link_tf_mapping: Dict[str, np.matrix] = {}
245245

246246
LOGGER.info(f"Processing root node: {_readable_names_mapping[root_node]}")
247247

0 commit comments

Comments
 (0)