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.
- primitive/__about__.py +1 -1
- primitive/agent/actions.py +55 -24
- primitive/cli.py +8 -0
- primitive/client.py +6 -2
- primitive/daemons/launch_agents.py +11 -1
- primitive/daemons/launch_service.py +10 -1
- primitive/jobs/actions.py +307 -0
- primitive/jobs/commands.py +41 -0
- primitive/lint/actions.py +1 -1
- primitive/organizations/actions.py +84 -0
- primitive/organizations/commands.py +24 -0
- primitive/projects/actions.py +51 -148
- primitive/projects/commands.py +24 -0
- primitive/sim/actions.py +190 -0
- primitive/sim/commands.py +19 -0
- primitive/sim/vcd.py +761 -0
- {primitive-0.1.13.dist-info → primitive-0.1.15.dist-info}/METADATA +1 -1
- {primitive-0.1.13.dist-info → primitive-0.1.15.dist-info}/RECORD +22 -15
- primitive/simulations/actions.py +0 -48
- /primitive/{simulations → sim}/__init__.py +0 -0
- {primitive-0.1.13.dist-info → primitive-0.1.15.dist-info}/WHEEL +0 -0
- {primitive-0.1.13.dist-info → primitive-0.1.15.dist-info}/entry_points.txt +0 -0
- {primitive-0.1.13.dist-info → primitive-0.1.15.dist-info}/licenses/LICENSE.txt +0 -0
primitive/projects/actions.py
CHANGED
@@ -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
|
8
|
+
def get_projects(
|
10
9
|
self,
|
11
10
|
organization_id: Optional[str] = None,
|
12
|
-
|
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
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
}
|
16
|
+
"""
|
17
|
+
fragment PageInfoFragment on PageInfo {
|
18
|
+
hasNextPage
|
19
|
+
hasPreviousPage
|
20
|
+
startCursor
|
21
|
+
endCursor
|
22
|
+
}
|
29
23
|
|
30
|
-
fragment
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
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
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
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
|
90
|
-
filters["
|
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(
|
112
|
-
|
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
|
-
|
174
|
-
|
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)
|
primitive/sim/actions.py
ADDED
@@ -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)
|