primitive 0.1.13__py3-none-any.whl → 0.1.15__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,175 +1,78 @@
1
- from typing import List, Optional
2
1
  from gql import gql
3
2
 
4
-
3
+ from typing import Optional
5
4
  from primitive.utils.actions import BaseAction
6
5
 
7
6
 
8
7
  class Projects(BaseAction):
9
- def get_job_runs(
8
+ def get_projects(
10
9
  self,
11
10
  organization_id: Optional[str] = None,
12
- project_id: Optional[str] = None,
13
- job_id: Optional[str] = None,
14
- reservation_id: Optional[str] = None,
15
- git_commit_id: Optional[str] = None,
16
- status: Optional[str] = None,
17
- conclusion: Optional[str] = None,
11
+ slug: Optional[str] = None,
18
12
  first: Optional[int] = 1,
19
13
  last: Optional[int] = None,
20
14
  ):
21
15
  query = gql(
22
- """
23
- fragment PageInfoFragment on PageInfo {
24
- hasNextPage
25
- hasPreviousPage
26
- startCursor
27
- endCursor
28
- }
16
+ """
17
+ fragment PageInfoFragment on PageInfo {
18
+ hasNextPage
19
+ hasPreviousPage
20
+ startCursor
21
+ endCursor
22
+ }
29
23
 
30
- fragment JobRunFragment on JobRun {
31
- id
32
- pk
33
- createdAt
34
- updatedAt
35
- completedAt
36
- startedAt
37
- status
38
- conclusion
39
- stdout
40
- job {
41
- id
42
- pk
43
- slug
44
- name
45
- createdAt
46
- updatedAt
47
- }
48
- gitCommit {
49
- sha
50
- branch
51
- repoFullName
52
- }
53
- }
24
+ fragment ProjectFragment on Project {
25
+ id
26
+ pk
27
+ slug
28
+ name
29
+ createdAt
30
+ updatedAt
31
+ }
54
32
 
55
- query jobRuns(
56
- $before: String
57
- $after: String
58
- $first: Int
59
- $last: Int
60
- $filters: JobRunFilters
61
- $order: JobRunOrder
62
- ) {
63
- jobRuns(
64
- before: $before
65
- after: $after
66
- first: $first
67
- last: $last
68
- filters: $filters
69
- order: $order
70
- ) {
71
- totalCount
72
- pageInfo {
73
- ...PageInfoFragment
74
- }
75
- edges {
76
- cursor
77
- node {
78
- ...JobRunFragment
79
- }
80
- }
81
- }
82
- }
83
- """
33
+ query projects(
34
+ $before: String
35
+ $after: String
36
+ $first: Int
37
+ $last: Int
38
+ $filters: ProjectFilters
39
+ ) {
40
+ projects(
41
+ before: $before
42
+ after: $after
43
+ first: $first
44
+ last: $last
45
+ filters: $filters
46
+ ) {
47
+ totalCount
48
+ pageInfo {
49
+ ...PageInfoFragment
50
+ }
51
+ edges {
52
+ cursor
53
+ node {
54
+ ...ProjectFragment
55
+ }
56
+ }
57
+ }
58
+ }
59
+ """
84
60
  )
85
61
 
86
62
  filters = {}
87
63
  if organization_id:
88
64
  filters["organization"] = {"id": organization_id}
89
- if project_id:
90
- filters["project"] = {"id": project_id}
91
- if job_id:
92
- filters["job"] = {"id": job_id}
93
- if reservation_id:
94
- filters["reservation"] = {"id": reservation_id}
95
- if git_commit_id:
96
- filters["gitCommit"] = {"id": git_commit_id}
97
- if status:
98
- filters["status"] = {"exact": status}
99
- if conclusion:
100
- filters["conclusion"] = {"exact": status}
65
+ if slug:
66
+ filters["slug"] = {"exact": slug}
101
67
 
102
68
  variables = {
103
69
  "first": first,
104
70
  "last": last,
105
71
  "filters": filters,
106
- "order": {
107
- "createdAt": "DESC",
108
- },
109
72
  }
110
73
 
111
- result = self.primitive.session.execute(query, variable_values=variables)
112
- return result
113
-
114
- def get_job_run(self, id: str):
115
- query = gql(
116
- """
117
- query jobRun($id: GlobalID!) {
118
- jobRun(id: $id) {
119
- id
120
- organization {
121
- id
122
- }
123
- }
124
- }
125
- """
126
- )
127
- variables = {"id": id}
128
- result = self.primitive.session.execute(query, variable_values=variables)
129
- return result
130
-
131
- def job_run_update(
132
- self,
133
- id: str,
134
- status: str = None,
135
- conclusion: str = None,
136
- stdout: str = None,
137
- file_ids: Optional[List[str]] = [],
138
- ):
139
- mutation = gql(
140
- """
141
- mutation jobRunUpdate($input: JobRunUpdateInput!) {
142
- jobRunUpdate(input: $input) {
143
- ... on JobRun {
144
- id
145
- status
146
- conclusion
147
- }
148
- }
149
- }
150
- """
151
- )
152
- input = {"id": id}
153
- if status:
154
- input["status"] = status
155
- if conclusion:
156
- input["conclusion"] = conclusion
157
- if file_ids and len(file_ids) > 0:
158
- input["files"] = file_ids
159
- if stdout:
160
- input["stdout"] = stdout
161
- variables = {"input": input}
162
- result = self.primitive.session.execute(mutation, variable_values=variables)
163
- return result
164
-
165
- def github_access_token_for_job_run(self, job_run_id: str):
166
- query = gql(
167
- """
168
- query ghAppTokenForJobRun($jobRunId: GlobalID!) {
169
- ghAppTokenForJobRun(jobRunId: $jobRunId)
170
- }
171
- """
74
+ result = self.primitive.session.execute(
75
+ query, variable_values=variables, get_execution_result=True
172
76
  )
173
- variables = {"jobRunId": job_run_id}
174
- result = self.primitive.session.execute(query, variable_values=variables)
175
- return result["ghAppTokenForJobRun"]
77
+ projects = [edge["node"] for edge in result.data["projects"]["edges"]]
78
+ return projects
@@ -0,0 +1,24 @@
1
+ import click
2
+
3
+ from ..utils.printer import print_result
4
+
5
+ import typing
6
+
7
+ if typing.TYPE_CHECKING:
8
+ from ..client import Primitive
9
+
10
+
11
+ @click.group()
12
+ @click.pass_context
13
+ def cli(context):
14
+ """Projects Commands"""
15
+ pass
16
+
17
+
18
+ @cli.command("list")
19
+ @click.pass_context
20
+ def list(context):
21
+ """List Projects"""
22
+ primitive: Primitive = context.obj.get("PRIMITIVE")
23
+ message = primitive.projects.get_projects()
24
+ print_result(message=message, context=context)
@@ -0,0 +1,190 @@
1
+ from gql import gql
2
+ from pathlib import Path
3
+ from primitive.utils.actions import BaseAction
4
+ from loguru import logger
5
+ import subprocess
6
+ from typing import Tuple, List
7
+ from ..utils.files import find_files_for_extension
8
+ import os
9
+ from .vcd import TokenKind, tokenize
10
+ import io
11
+ from collections import defaultdict
12
+ import urllib
13
+ import json
14
+
15
+
16
+ class Sim(BaseAction):
17
+ def execute(
18
+ self, source: Path = Path.cwd(), cmd: Tuple[str] = ["make"]
19
+ ) -> Tuple[bool, str]:
20
+ logger.debug(f"Starting simulation run for source: {source}")
21
+
22
+ os.chdir(source)
23
+ logger.debug(f"Changed to {source}, starting sim run")
24
+ try:
25
+ result = subprocess.run(cmd, capture_output=True, text=True)
26
+ except FileNotFoundError:
27
+ message = f"Did not find {cmd}"
28
+ logger.error(message)
29
+ return False, message
30
+
31
+ logger.debug("Sim run complete.")
32
+
33
+ message = ""
34
+ if result.stderr:
35
+ logger.error("\n" + result.stderr)
36
+ if result.stdout:
37
+ logger.info("\n" + result.stdout)
38
+ message = "See above logs for sim output."
39
+
40
+ if result.returncode != 0:
41
+ if not self.primitive.DEBUG:
42
+ message = result.stderr
43
+ return False, message
44
+ else:
45
+ message = "Sim run successful."
46
+
47
+ return True, message
48
+
49
+ def upload_file(self, path: Path) -> str:
50
+ file_upload_response = self.primitive.files.file_upload(
51
+ path, key_prefix=f"{self.job_run_id}/{str(path.parent)}"
52
+ )
53
+ return file_upload_response.json()["data"]["fileUpload"]["id"]
54
+
55
+ def collect_artifacts(self, source) -> None:
56
+ file_ids = []
57
+
58
+ # Look for VCD artifacts
59
+ files = find_files_for_extension(source, ".vcd")
60
+ for file in files:
61
+ trace_file_ids = self.generate_timeseries(path=file)
62
+ file_ids.extend(trace_file_ids)
63
+
64
+ logger.debug("Uploading additional artifacts...")
65
+ files = find_files_for_extension(source, (".xml", ".vcd", ".log", ".history"))
66
+ for file_path in files:
67
+ try:
68
+ file_ids.append(self.upload_file(file_path))
69
+ except FileNotFoundError:
70
+ logger.warning(f"{file_path} not found...")
71
+
72
+ logger.debug("Updating job run...")
73
+ if len(file_ids) > 0:
74
+ job_run_update_response = self.primitive.projects.job_run_update(
75
+ id=self.job_run_id, file_ids=file_ids
76
+ )
77
+ logger.success(job_run_update_response)
78
+
79
+ def generate_timeseries(self, path: Path) -> List[str]:
80
+ logger.debug("Parsing VCD file...")
81
+ with open(path, "rb") as f:
82
+ tokens = tokenize(io.BytesIO(f.read()))
83
+
84
+ metadata = defaultdict(dict)
85
+ traces = defaultdict(list)
86
+ timescale_unit = "s"
87
+ timescale_magnitude = 1
88
+ active_module: str = ""
89
+ time: int = 0
90
+
91
+ for token in tokens:
92
+ match token.kind:
93
+ case TokenKind.TIMESCALE:
94
+ timescale_unit = token.data.unit.value
95
+ timescale_magnitude = token.data.magnitude.value
96
+ case TokenKind.SCOPE:
97
+ active_module = token.data.ident
98
+ case TokenKind.CHANGE_TIME:
99
+ time = int(token.data)
100
+ case TokenKind.VAR:
101
+ var = {
102
+ "id_code": token.data.id_code,
103
+ "module": active_module,
104
+ "var_type": str(token.data.type_),
105
+ "var_size": token.data.size,
106
+ "reference": token.data.reference,
107
+ "bit_index": str(token.data.bit_index),
108
+ }
109
+ metadata[token.data.id_code] = var
110
+ case TokenKind.CHANGE_SCALAR:
111
+ traces[token.data.id_code].append(
112
+ (str(time), str(token.data.value))
113
+ )
114
+ case TokenKind.CHANGE_VECTOR:
115
+ traces[token.data.id_code].append(
116
+ (str(time), str(token.data.value))
117
+ )
118
+
119
+ # Add traces and write files
120
+ logger.debug("Uploading traces...")
121
+ trace_file_ids = []
122
+ for id_code, timeseries in traces.items():
123
+
124
+ def hashed(id_code):
125
+ return urllib.parse.quote_plus(id_code, safe="")
126
+
127
+ file_path = path.parent / f"{hashed(id_code)}.vcd.json"
128
+ with open(file_path, "w") as f:
129
+ f.write(json.dumps(timeseries))
130
+
131
+ trace_file_id = self.upload_file(file_path)
132
+ trace_file_ids.append(trace_file_id)
133
+
134
+ self.trace_create(
135
+ id_code=id_code,
136
+ module=metadata[id_code]["module"],
137
+ var_type=metadata[id_code]["var_type"],
138
+ var_size=metadata[id_code]["var_size"],
139
+ reference=metadata[id_code]["reference"],
140
+ bit_index=metadata[id_code]["bit_index"],
141
+ timescale_unit=timescale_unit,
142
+ timescale_magnitude=timescale_magnitude,
143
+ organization=self.organization_id,
144
+ file=trace_file_id,
145
+ job_run=self.job_run_id,
146
+ )
147
+
148
+ return trace_file_ids
149
+
150
+ def trace_create(
151
+ self,
152
+ id_code: str,
153
+ module: str,
154
+ var_type: str,
155
+ var_size: int,
156
+ reference: str,
157
+ bit_index: str,
158
+ timescale_unit: str,
159
+ timescale_magnitude: int,
160
+ organization: str,
161
+ file: str,
162
+ job_run: str,
163
+ ):
164
+ mutation = gql(
165
+ """
166
+ mutation createTrace($input: TraceCreateInput!) {
167
+ traceCreate(input: $input) {
168
+ ... on Trace {
169
+ id
170
+ }
171
+ }
172
+ }
173
+ """
174
+ )
175
+ input = {
176
+ "idCode": id_code,
177
+ "module": module,
178
+ "varType": var_type,
179
+ "varSize": var_size,
180
+ "reference": reference,
181
+ "bitIndex": bit_index,
182
+ "timescaleUnit": timescale_unit,
183
+ "timescaleMagnitude": timescale_magnitude,
184
+ "organization": organization,
185
+ "file": file,
186
+ "jobRun": job_run,
187
+ }
188
+ variables = {"input": input}
189
+ result = self.primitive.session.execute(mutation, variable_values=variables)
190
+ return result
@@ -0,0 +1,19 @@
1
+ import click
2
+ from pathlib import Path
3
+ import typing
4
+ from typing import Tuple
5
+ from ..utils.printer import print_result
6
+
7
+ if typing.TYPE_CHECKING:
8
+ from ..client import Primitive
9
+
10
+
11
+ @click.command("sim")
12
+ @click.pass_context
13
+ @click.argument("cmd", nargs=-1, required=True)
14
+ @click.option("--source", type=click.Path(exists=True), default=".")
15
+ def cli(context, source: str, cmd: Tuple[str]) -> None:
16
+ """Sim"""
17
+ primitive: Primitive = context.obj.get("PRIMITIVE")
18
+ result, message = primitive.sim.execute(source=Path(source), cmd=cmd)
19
+ print_result(message=message, context=context)