primitive 0.1.14__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 CHANGED
@@ -1,4 +1,4 @@
1
1
  # SPDX-FileCopyrightText: 2024-present Dylan Stein <dylan@steins.studio>
2
2
  #
3
3
  # SPDX-License-Identifier: MIT
4
- __version__ = "0.1.14"
4
+ __version__ = "0.1.15"
@@ -65,28 +65,59 @@ class Agent(BaseAction):
65
65
  )
66
66
  )
67
67
 
68
- if job_run["job"]["slug"] == "lint":
69
- logger.debug("Executing Lint Job")
70
-
71
- self.primitive.jobs.job_run_update(
72
- job_run["id"], status="request_in_progress"
73
- )
74
-
75
- result, message = self.primitive.lint.execute(
76
- source=downloaded_git_repository_dir
77
- )
78
- if result:
79
- conclusion = "success"
80
- else:
81
- conclusion = "failure"
82
- self.primitive.jobs.job_run_update(
83
- job_run["id"],
84
- status="request_completed",
85
- conclusion=conclusion,
86
- stdout=message,
87
- )
88
-
89
- logger.debug("Lint Job Completed")
68
+ match job_run["job"]["slug"]:
69
+ case "lint":
70
+ logger.debug("Executing Lint Job")
71
+
72
+ self.primitive.jobs.job_run_update(
73
+ job_run["id"], status="request_in_progress"
74
+ )
75
+
76
+ result, message = self.primitive.lint.execute(
77
+ source=downloaded_git_repository_dir
78
+ )
79
+ if result:
80
+ conclusion = "success"
81
+ else:
82
+ conclusion = "failure"
83
+ self.primitive.jobs.job_run_update(
84
+ job_run["id"],
85
+ status="request_completed",
86
+ conclusion=conclusion,
87
+ stdout=message,
88
+ )
89
+
90
+ logger.debug("Lint Job Completed")
91
+ case "sim":
92
+ logger.debug("Executing Sim Job")
93
+
94
+ self.primitive.job.job_run_update(
95
+ job_run["id"], status="request_in_progress"
96
+ )
97
+
98
+ result, message = self.primitive.sim.execute(
99
+ source=downloaded_git_repository_dir,
100
+ cmd=(
101
+ "make",
102
+ "all",
103
+ ), # TODO: Change this to use container args container cmd
104
+ )
105
+
106
+ # Attempt artifact collection
107
+ self.primitive.sim.collect_artifacts(
108
+ source=downloaded_git_repository_dir
109
+ )
110
+
111
+ if result:
112
+ conclusion = "success"
113
+ else:
114
+ conclusion = "failure"
115
+ self.primitive.jobs.job_run_update(
116
+ job_run["id"],
117
+ status="request_completed",
118
+ conclusion=conclusion,
119
+ stdout=message,
120
+ )
90
121
 
91
122
  sleep(5)
92
123
  except KeyboardInterrupt:
primitive/cli.py CHANGED
@@ -13,6 +13,7 @@ from .daemons.commands import cli as daemons_commands
13
13
  from .jobs.commands import cli as jobs_commands
14
14
  from .organizations.commands import cli as organizations_commands
15
15
  from .projects.commands import cli as projects_commands
16
+ from .sim.commands import cli as sim_commands
16
17
 
17
18
 
18
19
  @click.group()
@@ -67,6 +68,7 @@ cli.add_command(daemons_commands, "daemons")
67
68
  cli.add_command(jobs_commands, "jobs")
68
69
  cli.add_command(organizations_commands, "organizations")
69
70
  cli.add_command(projects_commands, "projects")
71
+ cli.add_command(sim_commands, "sim")
70
72
 
71
73
  if __name__ == "__main__":
72
74
  cli(obj={})
primitive/client.py CHANGED
@@ -4,7 +4,7 @@ from .projects.actions import Projects
4
4
  from .graphql.sdk import create_session
5
5
  from .utils.config import read_config_file
6
6
  from .files.actions import Files
7
- from .simulations.actions import Simulations
7
+ from .sim.actions import Sim
8
8
  from .hardware.actions import Hardware
9
9
  from .lint.actions import Lint
10
10
  from .agent.actions import Agent
@@ -61,7 +61,7 @@ class Primitive:
61
61
  self.projects: Projects = Projects(self)
62
62
  self.jobs: Jobs = Jobs(self)
63
63
  self.files: Files = Files(self)
64
- self.simulations: Simulations = Simulations(self)
64
+ self.sim: Sim = Sim(self)
65
65
  self.hardware: Hardware = Hardware(self)
66
66
  self.lint: Lint = Lint(self)
67
67
  self.agent: Agent = Agent(self)
@@ -6,6 +6,7 @@ HOME_DIRECTORY = Path.home()
6
6
  CURRENT_USER = str(HOME_DIRECTORY.expanduser()).lstrip("/Users/")
7
7
 
8
8
  PRIMITIVE_BINARY_PATH = Path(HOME_DIRECTORY / ".pyenv" / "shims" / "primitive")
9
+
9
10
  PRIMITIVE_LAUNCH_AGENT_FILEPATH = Path(
10
11
  HOME_DIRECTORY / "Library" / "LaunchAgents" / "tech.primitive.agent.plist"
11
12
  )
@@ -78,6 +79,15 @@ def populate_fresh_launch_agent():
78
79
  PRIMITIVE_LAUNCH_AGENT_FILEPATH.parent.mkdir(parents=True, exist_ok=True)
79
80
  PRIMITIVE_LAUNCH_AGENT_FILEPATH.touch()
80
81
 
82
+ found_primitive_binary_path = PRIMITIVE_BINARY_PATH
83
+ if not PRIMITIVE_BINARY_PATH.exists():
84
+ result = subprocess.run(["which", "primitive"], capture_output=True)
85
+ if result.returncode == 0:
86
+ found_primitive_binary_path = result.stdout.decode().rstrip("\n")
87
+ else:
88
+ print("primitive binary not found")
89
+ return False
90
+
81
91
  PRIMITIVE_LAUNCH_AGENT_FILEPATH.write_text(
82
92
  f"""<?xml version="1.0" encoding="UTF-8"?>
83
93
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
@@ -96,7 +106,7 @@ def populate_fresh_launch_agent():
96
106
  </array>
97
107
  <key>ProgramArguments</key>
98
108
  <array>
99
- <string>{PRIMITIVE_BINARY_PATH}</string>
109
+ <string>{found_primitive_binary_path}</string>
100
110
  <string>agent</string>
101
111
  </array>
102
112
  <key>RunAtLoad</key>
@@ -82,8 +82,17 @@ def populate_service_file():
82
82
  "After": "network.target",
83
83
  }
84
84
 
85
+ found_primitive_binary_path = PRIMITIVE_BINARY_PATH
86
+ if not PRIMITIVE_BINARY_PATH.exists():
87
+ result = subprocess.run(["which", "primitive"], capture_output=True)
88
+ if result.returncode == 0:
89
+ found_primitive_binary_path = result.stdout.decode().rstrip("\n")
90
+ else:
91
+ print("primitive binary not found")
92
+ return False
93
+
85
94
  config["Service"] = {
86
- "ExecStart": f"{PRIMITIVE_BINARY_PATH} agent",
95
+ "ExecStart": f"{found_primitive_binary_path} agent",
87
96
  "Restart": "always",
88
97
  "StandardError": f"append:{PRIMITIVE_AGENT_LOGS_FILEPATH}",
89
98
  "StandardOutput": f"append:{PRIMITIVE_AGENT_LOGS_FILEPATH}",
primitive/jobs/actions.py CHANGED
@@ -125,6 +125,10 @@ fragment JobRunFragment on JobRun {
125
125
  name
126
126
  createdAt
127
127
  updatedAt
128
+ }
129
+ jobSettings {
130
+ containerArgs
131
+ containerCommand
128
132
  }
129
133
  gitCommit {
130
134
  sha
primitive/lint/actions.py CHANGED
@@ -9,7 +9,7 @@ from ..utils.verible import install_verible
9
9
 
10
10
  class Lint(BaseAction):
11
11
  def execute(self, source: Path = Path.cwd()) -> Tuple[bool, str]:
12
- logger.debug("Starting linter for source: {source}")
12
+ logger.debug(f"Starting linter for source: {source}")
13
13
  files = find_files_for_extension(source, ".sv")
14
14
  if not files:
15
15
  message = "No files found to lint."
@@ -5,7 +5,6 @@ from primitive.utils.actions import BaseAction
5
5
 
6
6
 
7
7
  class Projects(BaseAction):
8
-
9
8
  def get_projects(
10
9
  self,
11
10
  organization_id: Optional[str] = None,
@@ -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)
primitive/sim/vcd.py ADDED
@@ -0,0 +1,761 @@
1
+ """Read Value Change Dump (VCD) files.
2
+
3
+ Adapted from Western Digital's PyVCD: https://github.com/westerndigitalcorporation/pyvcd
4
+
5
+ The primary interface is the :func:`tokenize()` generator function,
6
+ parses a binary VCD stream, yielding tokens as they are encountered.
7
+
8
+ .. code::
9
+
10
+ >>> import io
11
+ >>> from vcd.reader import TokenKind, tokenize
12
+ >>> vcd = b"$date today $end $timescale 1 ns $end"
13
+ >>> tokens = tokenize(io.BytesIO(vcd))
14
+ >>> token = next(tokens)
15
+ >>> assert token.kind is TokenKind.DATE
16
+ >>> assert token.date == 'today'
17
+ >>> token = next(tokens)
18
+ >>> assert token.kind is TokenKind.TIMESCALE
19
+ >>> assert token.timescale.magnitude.value == 1
20
+ >>> assert token.timescale.unit.value == 'ns'
21
+
22
+ """
23
+
24
+ import io
25
+ from dataclasses import dataclass
26
+ from enum import Enum
27
+ from typing import Iterator, NamedTuple, Optional, Tuple, Union
28
+
29
+
30
+ class ScopeType(Enum):
31
+ """Valid VCD scope types."""
32
+
33
+ begin = "begin"
34
+ fork = "fork"
35
+ function = "function"
36
+ module = "module"
37
+ task = "task"
38
+
39
+
40
+ class VarType(Enum):
41
+ """Valid VCD variable types."""
42
+
43
+ event = "event"
44
+ integer = "integer"
45
+ parameter = "parameter"
46
+ real = "real"
47
+ realtime = "realtime"
48
+ reg = "reg"
49
+ supply0 = "supply0"
50
+ supply1 = "supply1"
51
+ time = "time"
52
+ tri = "tri"
53
+ triand = "triand"
54
+ trior = "trior"
55
+ trireg = "trireg"
56
+ tri0 = "tri0"
57
+ tri1 = "tri1"
58
+ wand = "wand"
59
+ wire = "wire"
60
+ wor = "wor"
61
+ string = "string"
62
+
63
+ def __str__(self) -> str:
64
+ return self.value
65
+
66
+
67
+ class TimescaleMagnitude(Enum):
68
+ """Valid timescale magnitudes."""
69
+
70
+ one = 1
71
+ ten = 10
72
+ hundred = 100
73
+
74
+
75
+ class TimescaleUnit(Enum):
76
+ """Valid timescale units."""
77
+
78
+ second = "s"
79
+ millisecond = "ms"
80
+ microsecond = "us"
81
+ nanosecond = "ns"
82
+ picosecond = "ps"
83
+ femtosecond = "fs"
84
+
85
+
86
+ class Timescale(NamedTuple):
87
+ """Timescale magnitude and unit."""
88
+
89
+ magnitude: TimescaleMagnitude
90
+ unit: TimescaleUnit
91
+
92
+ @classmethod
93
+ def from_str(cls, s: str) -> "Timescale":
94
+ for unit in TimescaleUnit:
95
+ if s == unit.value:
96
+ mag = TimescaleMagnitude(1)
97
+ break
98
+ else:
99
+ for mag in reversed(TimescaleMagnitude):
100
+ mag_str = str(mag.value)
101
+ if s.startswith(mag_str):
102
+ unit_str = s[len(mag_str) :].lstrip(" ")
103
+ unit = TimescaleUnit(unit_str)
104
+ break
105
+ else:
106
+ raise ValueError(f"Invalid timescale magnitude {s!r}")
107
+ return Timescale(mag, unit)
108
+
109
+ def __str__(self) -> str:
110
+ return f"{self.magnitude.value} {self.unit.value}"
111
+
112
+
113
+ class TokenKind(Enum):
114
+ """Kinds of VCD tokens."""
115
+
116
+ COMMENT = 1
117
+ DATE = 2
118
+ ENDDEFINITIONS = 3
119
+ SCOPE = 4
120
+ TIMESCALE = 5
121
+ UPSCOPE = 6
122
+ VAR = 7
123
+ VERSION = 8
124
+ DUMPALL = 9
125
+ DUMPOFF = 10
126
+ DUMPON = 11
127
+ DUMPVARS = 12
128
+ END = 13
129
+ CHANGE_TIME = 14
130
+ CHANGE_SCALAR = 15
131
+ CHANGE_VECTOR = 16
132
+ CHANGE_REAL = 17
133
+ CHANGE_STRING = 18
134
+
135
+
136
+ class VarDecl(NamedTuple):
137
+ """VCD variable declaration.
138
+
139
+ Examples::
140
+
141
+ $var wire 4 !@# foobar [ 3 : 1 ] $end
142
+ $var real 1 aaa foobar $end
143
+ $var integer 32 > foobar[8] $end
144
+
145
+ """
146
+
147
+ type_: VarType #: Type of variable
148
+ size: int #: Size, in bits, of variable
149
+ id_code: str
150
+ """Identifer code of variable.
151
+
152
+ This code is used in subsequent value change descriptors
153
+ to map-back to this variable declaration."""
154
+
155
+ reference: str
156
+ """Reference name of variable.
157
+
158
+ This human-readable name typically corresponds to the name of a
159
+ variable in the model that output the VCD."""
160
+
161
+ bit_index: Union[None, int, Tuple[int, int]]
162
+ """Optional range of bits to select from the variable.
163
+
164
+ May select a single bit index, e.g. ``ref [ 3 ]``. Or a range of
165
+ bits, e.g. from ``ref [ 7 : 3 ]`` (MSB index then LSB index)."""
166
+
167
+ @property
168
+ def ref_str(self) -> str:
169
+ if self.bit_index is None:
170
+ return self.reference
171
+ elif isinstance(self.bit_index, int):
172
+ return f"{self.reference}[{self.bit_index}]"
173
+ else:
174
+ return f"{self.reference}[{self.bit_index[0]}:{self.bit_index[1]}]"
175
+
176
+
177
+ class ScopeDecl(NamedTuple):
178
+ """VCD scope declaration.
179
+
180
+ Examples::
181
+
182
+ $scope module Foo $end
183
+ $scope
184
+ fork alpha_beta
185
+ $end
186
+
187
+ """
188
+
189
+ type_: ScopeType #: Type of scope
190
+ ident: str #: Scope name
191
+
192
+
193
+ class VectorChange(NamedTuple):
194
+ """Vector value change descriptor.
195
+
196
+ A vector value consists of multiple 4-state values, where the four
197
+ states are 0, 1, X, and Z. When a vector value consists entirely
198
+ of 0 and 1 states, :attr:`value` will be an int. Otherwise
199
+ :attr:`value` will be a str.
200
+
201
+ """
202
+
203
+ id_code: str #: Identifier code of associated variable.
204
+ value: Union[int, str] #: New value of associated vector variable.
205
+
206
+
207
+ class RealChange(NamedTuple):
208
+ """Real value (floating point) change descriptor."""
209
+
210
+ id_code: str #: Identifier code of associated variable.
211
+ value: float #: New value of associated real variable.
212
+
213
+
214
+ class ScalarChange(NamedTuple):
215
+ """Scalar value change descriptor.
216
+
217
+ A scalar is a single 4-state value. The value is one of '0', '1',
218
+ 'X', or 'Z'.
219
+
220
+ """
221
+
222
+ id_code: str #: Identifier code of associated variable.
223
+ value: str #: New value of associated scalar variable.
224
+
225
+
226
+ class StringChange(NamedTuple):
227
+ """String value change descriptor.
228
+
229
+ Strings are VCD extension supported by GTKWave.
230
+
231
+ """
232
+
233
+ id_code: str #: Identifier code of associated variable.
234
+ value: str #: New value of associated string variable.
235
+
236
+
237
+ class Location(NamedTuple):
238
+ """Describe location within VCD stream/file."""
239
+
240
+ line: int #: Line number
241
+ column: int #: Column number
242
+
243
+
244
+ class Span(NamedTuple):
245
+ """Describe location span within VCD stream/file."""
246
+
247
+ start: Location #: Start of span
248
+ end: Location #: End of span
249
+
250
+
251
+ class Token(NamedTuple):
252
+ """VCD token yielded from :func:`tokenize()`.
253
+
254
+ These are relatively high-level tokens insofar as each token fully
255
+ captures an entire VCD declaration, command, or change descriptor.
256
+
257
+ The :attr:`kind` attribute determines the :attr:`data` type. Various
258
+ kind-specific properties provide runtime type-checked access to the
259
+ kind-specific data.
260
+
261
+ .. Note::
262
+
263
+ The :attr:`data` attribute may be accessed directly to avoid
264
+ runtime type checks and thus achieve better runtime performance
265
+ versus accessing kind-specific properties such as
266
+ :attr:`scalar_change`.
267
+
268
+ """
269
+
270
+ kind: TokenKind
271
+ "The kind of token."
272
+
273
+ span: Span
274
+ "The start and end location of the token within the file/stream."
275
+
276
+ data: Union[
277
+ None, # $enddefinitions $upscope $dump* $end
278
+ int, # time change
279
+ str, # $comment, $date, $version
280
+ ScopeDecl, # $scope
281
+ Timescale, # $timescale
282
+ VarDecl, # $var
283
+ ScalarChange,
284
+ VectorChange,
285
+ RealChange,
286
+ StringChange,
287
+ ]
288
+ "Data associated with the token. The data type depends on :attr:`kind`."
289
+
290
+ @property
291
+ def comment(self) -> str:
292
+ """Unstructured text from a ``$comment`` declaration."""
293
+ assert self.kind is TokenKind.COMMENT
294
+ assert isinstance(self.data, str)
295
+ return self.data
296
+
297
+ @property
298
+ def date(self) -> str:
299
+ """Unstructured text from a ``$date`` declaration."""
300
+ assert self.kind is TokenKind.DATE
301
+ assert isinstance(self.data, str)
302
+ return self.data
303
+
304
+ @property
305
+ def scope(self) -> ScopeDecl:
306
+ """Scope type and identifier from ``$scope`` declaration."""
307
+ assert self.kind is TokenKind.SCOPE
308
+ assert isinstance(self.data, ScopeDecl)
309
+ return self.data
310
+
311
+ @property
312
+ def timescale(self) -> Timescale:
313
+ """Magnitude and unit from ``$timescale`` declaration."""
314
+ assert self.kind is TokenKind.TIMESCALE
315
+ assert isinstance(self.data, Timescale)
316
+ return self.data
317
+
318
+ @property
319
+ def var(self) -> VarDecl:
320
+ """Details from a ``$var`` declaration."""
321
+ assert self.kind is TokenKind.VAR
322
+ assert isinstance(self.data, VarDecl)
323
+ return self.data
324
+
325
+ @property
326
+ def version(self) -> str:
327
+ """Unstructured text from a ``$version`` declaration."""
328
+ assert self.kind is TokenKind.VERSION
329
+ assert isinstance(self.data, str)
330
+ return self.data
331
+
332
+ @property
333
+ def time_change(self) -> int:
334
+ """Simulation time change."""
335
+ assert self.kind is TokenKind.CHANGE_TIME
336
+ assert isinstance(self.data, int)
337
+ return self.data
338
+
339
+ @property
340
+ def scalar_change(self) -> ScalarChange:
341
+ """Scalar value change descriptor."""
342
+ assert self.kind is TokenKind.CHANGE_SCALAR
343
+ assert isinstance(self.data, ScalarChange)
344
+ return self.data
345
+
346
+ @property
347
+ def vector_change(self) -> VectorChange:
348
+ """Vector value change descriptor."""
349
+ assert self.kind is TokenKind.CHANGE_VECTOR
350
+ assert isinstance(self.data, VectorChange)
351
+ return self.data
352
+
353
+ @property
354
+ def real_change(self) -> RealChange:
355
+ """Real (float) value change descriptor."""
356
+ assert self.kind is TokenKind.CHANGE_REAL
357
+ assert isinstance(self.data, RealChange)
358
+ return self.data
359
+
360
+ @property
361
+ def string_change(self) -> StringChange:
362
+ "String value change descriptor."
363
+ assert self.kind is TokenKind.CHANGE_STRING
364
+ assert isinstance(self.data, StringChange)
365
+ return self.data
366
+
367
+
368
+ class VCDParseError(Exception):
369
+ """Catch-all error for any VCD parsing errors."""
370
+
371
+ def __init__(self, loc: Location, msg: str) -> None:
372
+ super().__init__(f"{loc.line}:{loc.column}: {msg}")
373
+ self.loc = loc
374
+ "Location within VCD file where error was detected."
375
+
376
+
377
+ HasReadinto = Union[io.BufferedIOBase, io.RawIOBase]
378
+
379
+
380
+ def tokenize(stream: HasReadinto, buf_size: Optional[int] = None) -> Iterator[Token]:
381
+ """Parse VCD stream into tokens.
382
+
383
+ The input stream must be opened in binary mode. E.g. with ``open(path, 'rb')``.
384
+
385
+ """
386
+ if buf_size is None:
387
+ buf_size = io.DEFAULT_BUFFER_SIZE
388
+
389
+ s = _TokenizerState(stream, bytearray(buf_size))
390
+
391
+ try:
392
+ while True:
393
+ s.advance()
394
+ yield _parse_token(s)
395
+ except StopIteration:
396
+ return
397
+
398
+
399
+ @dataclass
400
+ class _TokenizerState:
401
+ stream: HasReadinto
402
+ buf: bytearray
403
+ pos: int = 0
404
+ end: int = 0
405
+ lineno: int = 1
406
+ column: int = 0
407
+
408
+ @property
409
+ def loc(self) -> Location:
410
+ return Location(self.lineno, self.column)
411
+
412
+ def span(self, start: Location) -> Span:
413
+ return Span(start, self.loc)
414
+
415
+ def advance(self, raise_on_eof: bool = True) -> int:
416
+ if self.pos < self.end:
417
+ self.pos += 1
418
+ else:
419
+ n = self.stream.readinto(self.buf)
420
+ if n:
421
+ self.end = n - 1
422
+ self.pos = 0
423
+ elif raise_on_eof:
424
+ raise StopIteration()
425
+ else:
426
+ return 0
427
+ c = self.buf[self.pos]
428
+ if c == 10:
429
+ self.lineno += 1
430
+ self.column = 1
431
+ else:
432
+ self.column += 1
433
+ return self.buf[self.pos]
434
+
435
+ def skip_ws(self) -> int:
436
+ c = self.buf[self.pos]
437
+ while c == 32 or 9 <= c <= 13:
438
+ c = self.advance()
439
+ return c
440
+
441
+ def take_ws_after_kw(self, kw: str) -> None:
442
+ if _is_ws(self.buf[self.pos]):
443
+ self.advance()
444
+ else:
445
+ raise VCDParseError(self.loc, f"Expected whitespace after identifier ${kw}")
446
+
447
+ def take_decimal(self) -> int:
448
+ digits = []
449
+ c = self.buf[self.pos]
450
+ while 48 <= c <= 57: # '0' <= c <= '9'
451
+ digits.append(c)
452
+ c = self.advance(raise_on_eof=False)
453
+ if digits:
454
+ return int(bytes(digits))
455
+ else:
456
+ raise VCDParseError(self.loc, "Expected decimal value")
457
+
458
+ def take_id_code(self) -> str:
459
+ printables = []
460
+ c = self.buf[self.pos]
461
+ while 33 <= c <= 126: # printable character
462
+ printables.append(c)
463
+ c = self.advance(raise_on_eof=False)
464
+ if printables:
465
+ return bytes(printables).decode("ascii")
466
+ else:
467
+ raise VCDParseError(self.loc, "Expected id code")
468
+
469
+ def take_identifier(self) -> str:
470
+ identifier = []
471
+ c = self.buf[self.pos]
472
+
473
+ # Identifiers must start with letter or underscore
474
+ if (
475
+ 65 <= c <= 90 # 'A' <= c <= 'Z'
476
+ or 97 <= c <= 122 # 'a' - 'z'
477
+ or c == 95 # '_'
478
+ ):
479
+ identifier.append(c)
480
+ c = self.advance()
481
+ elif c == 92:
482
+ # If we encounter an escaped identifier,
483
+ # just remove the escape character
484
+ # NOTE: may need to revisit this
485
+ c = self.advance()
486
+ else:
487
+ raise VCDParseError(self.loc, "Identifier must start with a-zA-Z_")
488
+
489
+ while (
490
+ 48 <= c <= 57 # '0' - '9'
491
+ or 65 <= c <= 90 # 'A' - 'Z'
492
+ or 97 <= c <= 122 # 'a' - 'z'
493
+ or c == 95 # '_'
494
+ or c == 36 # '$'
495
+ or c == 46 # '.' not in spec, but seen in the wild
496
+ or c == 40 # '(' - produced by cva6 core
497
+ or c == 41 # ')' - produced by cva6 core
498
+ or c == 91 # '[' - produced by dumping packed arrays
499
+ or c == 93 # ']' - produced by dumping packed arrays
500
+ ):
501
+ identifier.append(c)
502
+ c = self.advance(raise_on_eof=False)
503
+
504
+ return bytes(identifier).decode("ascii")
505
+
506
+ def take_bit_index(self) -> Union[int, Tuple[int, int]]:
507
+ self.skip_ws()
508
+ index0 = self.take_decimal()
509
+ index1: Optional[int]
510
+
511
+ c = self.skip_ws()
512
+ if c == 58: # ':'
513
+ self.advance()
514
+ self.skip_ws()
515
+ index1 = self.take_decimal()
516
+ else:
517
+ index1 = None
518
+
519
+ c = self.skip_ws()
520
+ if c == 93: # ']'
521
+ self.advance(raise_on_eof=False)
522
+ if index1 is None:
523
+ return index0
524
+ else:
525
+ return (index0, index1)
526
+ else:
527
+ raise VCDParseError(self.loc, 'Expected bit index to terminate with "]"')
528
+
529
+ def take_to_end(self) -> str:
530
+ chars = [
531
+ self.buf[self.pos], # $
532
+ self.advance(), # --> e
533
+ self.advance(), # --> n
534
+ self.advance(), # --> d
535
+ ]
536
+ while not ( # Check for 'd' 'n' 'e' '$'
537
+ chars[-1] == 100
538
+ and chars[-2] == 110
539
+ and chars[-3] == 101
540
+ and chars[-4] == 36
541
+ ):
542
+ chars.append(self.advance())
543
+
544
+ if len(chars) > 4 and not _is_ws(chars[-5]):
545
+ loc = Location(self.lineno, self.column - min(len(chars), 5))
546
+ raise VCDParseError(loc, "Expected whitespace before $end")
547
+
548
+ return bytes(chars[:-5]).decode("ascii")
549
+
550
+ def take_end(self) -> None:
551
+ if (
552
+ self.skip_ws() != 36 # '$'
553
+ or self.advance() != 101 # 'e'
554
+ or self.advance() != 110 # 'n'
555
+ or self.advance() != 100 # 'd'
556
+ ):
557
+ raise VCDParseError(self.loc, "Expected $end")
558
+
559
+
560
+ def _is_ws(c: int) -> bool:
561
+ return c == 32 or 9 <= c <= 13
562
+
563
+
564
+ def _parse_token(s: _TokenizerState) -> Token:
565
+ c = s.skip_ws()
566
+ start = s.loc
567
+ if c == 35: # '#'
568
+ # Parse time change
569
+ s.advance()
570
+ time = s.take_decimal()
571
+ return Token(TokenKind.CHANGE_TIME, s.span(start), time)
572
+ elif c == 48 or c == 49 or c == 122 or c == 90 or c == 120 or c == 88:
573
+ # c in '01zZxX'
574
+ # Parse scalar change
575
+ scalar_value = chr(c)
576
+ s.advance()
577
+ id_code = s.take_id_code()
578
+ return Token(
579
+ TokenKind.CHANGE_SCALAR, s.span(start), ScalarChange(id_code, scalar_value)
580
+ )
581
+ elif c == 66 or c == 98: # 'B' or 'b'
582
+ # Parse vector change
583
+ vector = []
584
+ c = s.advance()
585
+ while c == 48 or c == 49: # '0' or '1'
586
+ vector.append(c)
587
+ c = s.advance()
588
+ vector_value: Union[int, str]
589
+
590
+ if c == 122 or c == 90 or c == 120 or c == 88: # c in 'zZxX'
591
+ vector.append(c)
592
+ c = s.advance()
593
+ while (
594
+ c == 48 or c == 49 or c == 122 or c == 90 or c == 120 or c == 88
595
+ ): # c in '01zZxX'
596
+ vector.append(c)
597
+ c = s.advance()
598
+ vector_value = bytes(vector).decode("ascii")
599
+ else:
600
+ vector_value = int(bytes(vector), 2)
601
+
602
+ if not _is_ws(c):
603
+ raise VCDParseError(s.loc, "Expected whitespace after vector value")
604
+
605
+ s.skip_ws()
606
+
607
+ id_code = s.take_id_code()
608
+
609
+ return Token(
610
+ TokenKind.CHANGE_VECTOR, s.span(start), VectorChange(id_code, vector_value)
611
+ )
612
+ elif c == 82 or c == 114: # 'R' or 'r'
613
+ # Parse real change
614
+ real_digits = []
615
+ c = s.advance()
616
+
617
+ while not _is_ws(c):
618
+ real_digits.append(c)
619
+ c = s.advance()
620
+
621
+ try:
622
+ real = float(bytes(real_digits))
623
+ except ValueError:
624
+ real_str = bytes(real_digits).decode("ascii")
625
+ raise VCDParseError(start, f"Expected real value, got: {real_str}")
626
+
627
+ s.skip_ws()
628
+
629
+ id_code = s.take_id_code()
630
+
631
+ return Token(TokenKind.CHANGE_REAL, s.span(start), RealChange(id_code, real))
632
+ elif c == 83 or c == 115: # 'S' or 's'
633
+ chars = []
634
+ c = s.advance()
635
+ while not _is_ws(c):
636
+ chars.append(c)
637
+ c = s.advance()
638
+ s.skip_ws()
639
+ id_code = s.take_id_code()
640
+ string_value = bytes(chars).decode("ascii")
641
+ return Token(
642
+ TokenKind.CHANGE_STRING, s.span(start), StringChange(id_code, string_value)
643
+ )
644
+ elif c == 36: # '$'
645
+ s.advance()
646
+ kw = s.take_identifier()
647
+
648
+ if kw == "comment":
649
+ s.take_ws_after_kw(kw)
650
+ comment = s.take_to_end()
651
+ return Token(TokenKind.COMMENT, s.span(start), comment)
652
+ elif kw == "date":
653
+ s.take_ws_after_kw(kw)
654
+ date_str = s.take_to_end()
655
+ return Token(TokenKind.DATE, s.span(start), date_str)
656
+ elif kw == "enddefinitions":
657
+ s.take_ws_after_kw(kw)
658
+ s.take_end()
659
+ return Token(TokenKind.ENDDEFINITIONS, s.span(start), None)
660
+ elif kw == "scope":
661
+ s.take_ws_after_kw(kw)
662
+ s.skip_ws()
663
+ identifier = s.take_identifier()
664
+ try:
665
+ scope_type = ScopeType(identifier)
666
+ except ValueError:
667
+ raise VCDParseError(s.loc, f"Invalid $scope type: {identifier}")
668
+
669
+ s.skip_ws()
670
+
671
+ scope_ident = s.take_identifier()
672
+
673
+ s.take_end()
674
+
675
+ scope_decl = ScopeDecl(scope_type, scope_ident)
676
+
677
+ return Token(TokenKind.SCOPE, s.span(start), scope_decl)
678
+ elif kw == "timescale":
679
+ s.take_ws_after_kw(kw)
680
+ s.skip_ws()
681
+ mag_int = s.take_decimal()
682
+
683
+ try:
684
+ magnitude = TimescaleMagnitude(mag_int)
685
+ except ValueError:
686
+ valid_magnitudes = ", ".join(str(m.value) for m in TimescaleMagnitude)
687
+ raise VCDParseError(
688
+ s.loc,
689
+ f"Invalid $timescale magnitude: {mag_int}. "
690
+ f"Must be one of: {valid_magnitudes}.",
691
+ )
692
+
693
+ s.skip_ws()
694
+ unit_str = s.take_identifier()
695
+ try:
696
+ unit = TimescaleUnit(unit_str)
697
+ except ValueError:
698
+ valid_units = ", ".join(u.value for u in TimescaleUnit)
699
+ raise VCDParseError(
700
+ s.loc,
701
+ f"Invalid $timescale unit: {unit_str}. "
702
+ f"Must be one of: {valid_units}.",
703
+ )
704
+
705
+ s.take_end()
706
+
707
+ timescale = Timescale(magnitude, unit)
708
+ return Token(TokenKind.TIMESCALE, s.span(start), timescale)
709
+ elif kw == "upscope":
710
+ s.take_ws_after_kw(kw)
711
+ s.take_end()
712
+ return Token(TokenKind.UPSCOPE, s.span(start), None)
713
+ elif kw == "var":
714
+ s.take_ws_after_kw(kw)
715
+ s.skip_ws()
716
+ type_str = s.take_identifier()
717
+ try:
718
+ type_ = VarType(type_str)
719
+ except ValueError:
720
+ valid_types = ", ".join(t.value for t in VarType)
721
+ raise VCDParseError(
722
+ s.loc,
723
+ f"Invalid $var type: {type_str}. Must be one of: {valid_types}",
724
+ )
725
+
726
+ s.skip_ws()
727
+ size = s.take_decimal()
728
+ s.skip_ws()
729
+ id_code = s.take_id_code()
730
+ s.skip_ws()
731
+ ident = s.take_identifier()
732
+
733
+ bit_index: Union[None, int, Tuple[int, int]]
734
+ c = s.skip_ws()
735
+ if c == 91: # '['
736
+ s.advance()
737
+ bit_index = s.take_bit_index()
738
+ else:
739
+ bit_index = None
740
+
741
+ s.take_end()
742
+ var_decl = VarDecl(type_, size, id_code, ident, bit_index)
743
+ return Token(TokenKind.VAR, s.span(start), var_decl)
744
+ elif kw == "version":
745
+ s.take_ws_after_kw(kw)
746
+ version = s.take_to_end()
747
+ return Token(TokenKind.VERSION, s.span(start), version)
748
+ elif kw == "dumpall":
749
+ return Token(TokenKind.DUMPALL, s.span(start), None)
750
+ elif kw == "dumpoff":
751
+ return Token(TokenKind.DUMPOFF, s.span(start), None)
752
+ elif kw == "dumpon":
753
+ return Token(TokenKind.DUMPON, s.span(start), None)
754
+ elif kw == "dumpvars":
755
+ return Token(TokenKind.DUMPVARS, s.span(start), None)
756
+ elif kw == "end":
757
+ return Token(TokenKind.END, s.span(start), None)
758
+ else:
759
+ raise VCDParseError(s.loc, f"invalid keyword ${kw}")
760
+ else:
761
+ raise VCDParseError(s.loc, f"confused: {chr(c)}")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: primitive
3
- Version: 0.1.14
3
+ Version: 0.1.15
4
4
  Project-URL: Documentation, https://github.com//primitivecorp/primitive-cli#readme
5
5
  Project-URL: Issues, https://github.com//primitivecorp/primitive-cli/issues
6
6
  Project-URL: Source, https://github.com//primitivecorp/primitive-cli
@@ -1,16 +1,16 @@
1
- primitive/__about__.py,sha256=NZWfnlUO354pWRTdV9CUZL2iipJ1paYR9Gljq6eTciM,129
1
+ primitive/__about__.py,sha256=HGWvZTOWl3GbIPsR_WSgUmWzbrPLCY9-2njkzMWnpBk,129
2
2
  primitive/__init__.py,sha256=bwKdgggKNVssJFVPfKSxqFMz4IxSr54WWbmiZqTMPNI,106
3
- primitive/cli.py,sha256=VA1c9RiH79mqYvIvYT0msu4tEPlYiU1C2LstbdzyjWk,2291
4
- primitive/client.py,sha256=FYc-Onu1c0fjttSWlhK0B1ZOizDsP3qGtOAUHvPGqG0,2410
5
- primitive/agent/actions.py,sha256=8G9-QjUKRvlvlZ_6NyZr2KvvW1FTS54q-KPUzyAPccE,3657
3
+ primitive/cli.py,sha256=VQPSewC6ouGdEG9W1gllawGJTydpOY0Lzg7LURXcqQg,2374
4
+ primitive/client.py,sha256=SFPG4H2wJao8euGdnYp-l7dk_fDpWeVn2aT2WNJUAqo,2370
5
+ primitive/agent/actions.py,sha256=e0xqQm0EeAqwhS45JrsxjNdT_JPMAgolav62jsHjdVM,5131
6
6
  primitive/agent/commands.py,sha256=-dVDilELfkGfbZB7qfEPs77Dm1oT62qJj4tsIk4KoxI,254
7
7
  primitive/auth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
8
  primitive/auth/actions.py,sha256=N2bGcwXNsB89pzs66gF9A5_WzUScY5fhfOyWixqo2y8,1054
9
9
  primitive/auth/commands.py,sha256=JahUq0E2e7Xa-FX1WEUv7TgM6ieDvNH4VwRRtxAW7HE,2340
10
10
  primitive/daemons/actions.py,sha256=Nt3yNtbBhen0jK4sRsH_N7AP3UBuyL48VaUhtC7wYq8,2015
11
11
  primitive/daemons/commands.py,sha256=-Muh-6ib4uAVtPn_67AcMrDwuCwYlCnRQozCi2Xurmk,1726
12
- primitive/daemons/launch_agents.py,sha256=xR_JDL93mt5-fSbj-v3qvYFvLqfn486fPNbNxZOb1wU,4412
13
- primitive/daemons/launch_service.py,sha256=3ktr0n0rSNA-IV6hQ8hbG50L2Y3V8-IeW79C8CrZi8U,5203
12
+ primitive/daemons/launch_agents.py,sha256=qovt32gwpjGDd82z_SY5EGCUjaUyNA49pZFajZsw3eE,4796
13
+ primitive/daemons/launch_service.py,sha256=GZYUQfScpFAxma_pCZH4nRSVtrynTQEfeAvn9eJK2PE,5586
14
14
  primitive/files/actions.py,sha256=f4JN3QFB2WXw-0JpnE-4-movnqtvXIpCrGd_9pdkeW4,2624
15
15
  primitive/files/commands.py,sha256=DDizo3xJnU3KLUBTMeeM72viVpnJinLwxs84tmqKhqo,810
16
16
  primitive/git/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -20,17 +20,19 @@ primitive/graphql/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU
20
20
  primitive/graphql/sdk.py,sha256=BhCGmDtc4sNnH8CxbQSJyFwOZ-ZSqMtjsxMB3JRBhPw,1456
21
21
  primitive/hardware/actions.py,sha256=Ea3_2E3F_3WapV60g_mOIcpXhadoknwihR7slXyUWtk,18840
22
22
  primitive/hardware/commands.py,sha256=QE7LLeFdfOqlvz3JwdwJJRZAY3fHI1zB9kYmmDajpq0,1477
23
- primitive/jobs/actions.py,sha256=ftVWIB10PxTrFsDmSd9C3WV3puiQftTLBKFIjcS_StI,7562
23
+ primitive/jobs/actions.py,sha256=JsoVh7XVPX7AVb73fKwxY_UsMcFt4YWgMhveyJf56oc,7622
24
24
  primitive/jobs/commands.py,sha256=MxPCkBEYW_eLNqgCRYeyj7ZcLOFAWfpVZlqDR2Y_S0o,830
25
- primitive/lint/actions.py,sha256=PDw0fkIkI5hHHjoHaAiXvQUocwHZkgB2mfI-LGMl6TI,2267
25
+ primitive/lint/actions.py,sha256=fGnNlcD_B-E0SvRUTvTdSlTm2kCQUTrlBLB0mt1sXKM,2268
26
26
  primitive/lint/commands.py,sha256=3CZvkOEMpJspJWmaQzA5bpPKx0_VCijQIXA9l-eTnZE,487
27
27
  primitive/organizations/actions.py,sha256=e0V4E1UK1IcBJsWWH6alHYUmArhzPrBqZ8WkHPIcLq0,2268
28
28
  primitive/organizations/commands.py,sha256=_dwgVEJCqMa5VgB_7P1wLPFc0AuT1p9dtyR9JRr4kpw,487
29
29
  primitive/projects/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
- primitive/projects/actions.py,sha256=L2M2SvFdDr2XnpoxFl61jXmIriD4T1vVMoBFgCy6SY4,2062
30
+ primitive/projects/actions.py,sha256=xhebDUMN9DXWvngWJyJkiijghbZwffy-JIPSsOg8agE,2061
31
31
  primitive/projects/commands.py,sha256=Fqqgpi4cm6zOgkHK--0F0hiiIj32BmgZ-h1MydmWwdE,464
32
- primitive/simulations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
33
- primitive/simulations/actions.py,sha256=YR0oxxd7_kuUIH77BWZLUs9rLRiSJztPPhpgDJU2PbY,1267
32
+ primitive/sim/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
33
+ primitive/sim/actions.py,sha256=R69ku4ZwaYokk-SSdZ8Yq50SkpAIGXAgYsnoPkRUiUE,6497
34
+ primitive/sim/commands.py,sha256=8PaOfL1MO6qxTn7mNVRnBU1X2wa3gk_mlbAhBW6MnI0,591
35
+ primitive/sim/vcd.py,sha256=mAbGnKWM0qzIUMkuSmO0p3sU25kOqbl31mvCsDSrXeM,22221
34
36
  primitive/utils/actions.py,sha256=HOFrmM3-0A_A3NS84MqrZ6JmQEiiPSoDqEeuu6b_qfQ,196
35
37
  primitive/utils/config.py,sha256=DlFM5Nglo22WPtbpZSVtH7NX-PTMaKYlcrUE7GPRG4c,1058
36
38
  primitive/utils/files.py,sha256=Yv__bQes3YIlzhOT9kVxtYhoA5CmUjPSvphl9PZ41k4,867
@@ -39,8 +41,8 @@ primitive/utils/memory_size.py,sha256=4xfha21kW82nFvOTtDFx9Jk2ZQoEhkfXii-PGNTpIU
39
41
  primitive/utils/printer.py,sha256=f1XUpqi5dkTL3GWvYRUGlSwtj2IxU1q745T4Fxo7Tn4,370
40
42
  primitive/utils/shell.py,sha256=-7UjQaBqSGHzEEyX8pNjeYFFP0P3lVnDV0OkgPz1qHU,1050
41
43
  primitive/utils/verible.py,sha256=QYczN1IvxODfj4jeq0nqjFuF0Oi0Zdx-Q32ySOJgcw8,2205
42
- primitive-0.1.14.dist-info/METADATA,sha256=L7Npr3zJ77e2VpLmfKrhJ7bb3OuI2LKLxIS7Nvak7SQ,1818
43
- primitive-0.1.14.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
44
- primitive-0.1.14.dist-info/entry_points.txt,sha256=p1K8DMCWka5FqLlqP1sPek5Uovy9jq8u51gUsP-z334,48
45
- primitive-0.1.14.dist-info/licenses/LICENSE.txt,sha256=B8kmQMJ2sxYygjCLBk770uacaMci4mPSoJJ8WoDBY_c,1098
46
- primitive-0.1.14.dist-info/RECORD,,
44
+ primitive-0.1.15.dist-info/METADATA,sha256=fB3RVrp4aJUM2OBiKjwUOOOht0RjS2ZDuEvj5wkIBXA,1818
45
+ primitive-0.1.15.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
46
+ primitive-0.1.15.dist-info/entry_points.txt,sha256=p1K8DMCWka5FqLlqP1sPek5Uovy9jq8u51gUsP-z334,48
47
+ primitive-0.1.15.dist-info/licenses/LICENSE.txt,sha256=B8kmQMJ2sxYygjCLBk770uacaMci4mPSoJJ8WoDBY_c,1098
48
+ primitive-0.1.15.dist-info/RECORD,,
@@ -1,48 +0,0 @@
1
- from gql import gql
2
-
3
-
4
- from primitive.utils.actions import BaseAction
5
-
6
-
7
- class Simulations(BaseAction):
8
- def trace_create(
9
- self,
10
- id_code: str,
11
- module: str,
12
- var_type: str,
13
- var_size: int,
14
- reference: str,
15
- bit_index: str,
16
- timescale_unit: str,
17
- timescale_magnitude: int,
18
- organization: str,
19
- file: str,
20
- job_run: str,
21
- ):
22
- mutation = gql(
23
- """
24
- mutation createTrace($input: TraceCreateInput!) {
25
- traceCreate(input: $input) {
26
- ... on Trace {
27
- id
28
- }
29
- }
30
- }
31
- """
32
- )
33
- input = {
34
- "idCode": id_code,
35
- "module": module,
36
- "varType": var_type,
37
- "varSize": var_size,
38
- "reference": reference,
39
- "bitIndex": bit_index,
40
- "timescaleUnit": timescale_unit,
41
- "timescaleMagnitude": timescale_magnitude,
42
- "organization": organization,
43
- "file": file,
44
- "jobRun": job_run,
45
- }
46
- variables = {"input": input}
47
- result = self.primitive.session.execute(mutation, variable_values=variables)
48
- return result
File without changes