primitive 0.1.1__py3-none-any.whl → 0.1.4__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 +4 -0
- primitive/__init__.py +3 -0
- primitive/auth/__init__.py +0 -0
- primitive/auth/actions.py +43 -0
- primitive/auth/commands.py +77 -0
- primitive/cli.py +60 -0
- primitive/client.py +66 -0
- primitive/files/actions.py +76 -0
- primitive/files/commands.py +28 -0
- primitive/graphql/__init__.py +0 -0
- primitive/graphql/sdk.py +53 -0
- primitive/hardware/actions.py +447 -0
- primitive/hardware/commands.py +53 -0
- primitive/lint/actions.py +67 -0
- primitive/lint/commands.py +17 -0
- primitive/projects/__init__.py +0 -0
- primitive/projects/actions.py +55 -0
- primitive/simulations/__init__.py +0 -0
- primitive/simulations/actions.py +48 -0
- primitive/utils/actions.py +9 -0
- primitive/utils/config.py +34 -0
- primitive/utils/files.py +16 -0
- primitive/utils/git.py +15 -0
- primitive/utils/memory_size.py +87 -0
- primitive/utils/printer.py +14 -0
- primitive/utils/shell.py +63 -0
- primitive/utils/verible.py +51 -0
- primitive-0.1.4.dist-info/METADATA +49 -0
- primitive-0.1.4.dist-info/RECORD +32 -0
- {primitive-0.1.1.dist-info → primitive-0.1.4.dist-info}/WHEEL +1 -1
- primitive-0.1.4.dist-info/entry_points.txt +2 -0
- primitive-0.1.4.dist-info/licenses/LICENSE.txt +9 -0
- primitive/main.py +0 -18
- primitive/scope/__init__.py +0 -2
- primitive/scope/simulator.py +0 -12
- primitive/scope/watch.py +0 -42
- primitive-0.1.1.dist-info/METADATA +0 -15
- primitive-0.1.1.dist-info/RECORD +0 -9
- primitive-0.1.1.dist-info/entry_points.txt +0 -3
primitive/__about__.py
ADDED
primitive/__init__.py
CHANGED
File without changes
|
@@ -0,0 +1,43 @@
|
|
1
|
+
from gql import gql
|
2
|
+
|
3
|
+
from ..utils.config import read_config_file, update_config_file
|
4
|
+
|
5
|
+
|
6
|
+
from primitive.utils.actions import BaseAction
|
7
|
+
|
8
|
+
|
9
|
+
class Auth(BaseAction):
|
10
|
+
def whoami(self):
|
11
|
+
query = gql(
|
12
|
+
"""
|
13
|
+
query whoami {
|
14
|
+
whoami {
|
15
|
+
username
|
16
|
+
}
|
17
|
+
}
|
18
|
+
"""
|
19
|
+
)
|
20
|
+
|
21
|
+
result = self.primitive.session.execute(query, get_execution_result=True)
|
22
|
+
|
23
|
+
return result
|
24
|
+
|
25
|
+
def setup_config(
|
26
|
+
self,
|
27
|
+
username: str,
|
28
|
+
token: str,
|
29
|
+
host: str = "api.primitive.tech",
|
30
|
+
transport: str = "https",
|
31
|
+
):
|
32
|
+
full_config = read_config_file()
|
33
|
+
new_host_config = {
|
34
|
+
"username": username,
|
35
|
+
"token": token,
|
36
|
+
"transport": transport,
|
37
|
+
}
|
38
|
+
|
39
|
+
if existing_host_config := full_config.get(host, None):
|
40
|
+
full_config[host] = {**existing_host_config, **new_host_config}
|
41
|
+
else:
|
42
|
+
full_config[host] = new_host_config
|
43
|
+
update_config_file(new_config=full_config)
|
@@ -0,0 +1,77 @@
|
|
1
|
+
import os
|
2
|
+
import webbrowser
|
3
|
+
import click
|
4
|
+
from .actions import Auth
|
5
|
+
from ..utils.config import PRIMITIVE_CREDENTIALS_FILEPATH
|
6
|
+
from ..utils.printer import print_result
|
7
|
+
|
8
|
+
import typing
|
9
|
+
|
10
|
+
if typing.TYPE_CHECKING:
|
11
|
+
from ..client import Primitive
|
12
|
+
|
13
|
+
|
14
|
+
@click.group()
|
15
|
+
@click.pass_context
|
16
|
+
def cli(context):
|
17
|
+
"""Authentication"""
|
18
|
+
pass
|
19
|
+
|
20
|
+
|
21
|
+
@cli.command("whoami")
|
22
|
+
@click.pass_context
|
23
|
+
def whoami_command(context):
|
24
|
+
"""Whoami"""
|
25
|
+
primitive: Primitive = context.obj.get("PRIMITIVE")
|
26
|
+
result = primitive.auth.whoami()
|
27
|
+
if context.obj["DEBUG"] or context.obj["JSON"]:
|
28
|
+
message = result.data
|
29
|
+
else:
|
30
|
+
message = f"Logged in as {result.data['whoami']['username']}"
|
31
|
+
print_result(message=message, context=context)
|
32
|
+
|
33
|
+
|
34
|
+
@cli.command("config")
|
35
|
+
@click.pass_context
|
36
|
+
@click.option(
|
37
|
+
"--username",
|
38
|
+
prompt=True,
|
39
|
+
default=lambda: os.environ.get("PRIMITIVE_USER", ""),
|
40
|
+
show_default="current user",
|
41
|
+
help="Username for Primitive API",
|
42
|
+
)
|
43
|
+
@click.option(
|
44
|
+
"--transport",
|
45
|
+
required=False,
|
46
|
+
default="https",
|
47
|
+
show_default="https",
|
48
|
+
help="Transport protocol for Primitive API",
|
49
|
+
)
|
50
|
+
def config_command(context, username: str, transport: str):
|
51
|
+
"""Configure the CLI"""
|
52
|
+
token = os.environ.get("PRIMITIVE_TOKEN", "")
|
53
|
+
if not token and context.obj.get("YES"):
|
54
|
+
raise click.ClickException(
|
55
|
+
"PRIMITIVE_TOKEN environment variable is required for non-interactive mode"
|
56
|
+
)
|
57
|
+
host = context.obj.get("HOST", "api.primitive.tech")
|
58
|
+
while not token:
|
59
|
+
account_settings_url = (
|
60
|
+
f"{transport}://{host.replace('api', 'app')}/account/tokens"
|
61
|
+
)
|
62
|
+
if "localhost" in host:
|
63
|
+
account_settings_url = "http://localhost:3000/account/tokens"
|
64
|
+
click.secho(
|
65
|
+
f"You can find or create a Primitive API token at {account_settings_url}",
|
66
|
+
fg="yellow",
|
67
|
+
)
|
68
|
+
|
69
|
+
webbrowser.open_new_tab(account_settings_url)
|
70
|
+
token = click.prompt(
|
71
|
+
"Please enter your Primitive API token", hide_input=True, type=str
|
72
|
+
)
|
73
|
+
|
74
|
+
auth = Auth(primitive=None)
|
75
|
+
auth.setup_config(username=username, token=token, host=host, transport=transport)
|
76
|
+
message = f"Config created at '{PRIMITIVE_CREDENTIALS_FILEPATH}' for user '{username}' on host '{host}'" # noqa
|
77
|
+
print_result(message=message, context=context, fg="green")
|
primitive/cli.py
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
import os
|
2
|
+
import sys
|
3
|
+
import click
|
4
|
+
from .__about__ import __version__
|
5
|
+
from .client import Primitive
|
6
|
+
from .auth.commands import config_command, whoami_command
|
7
|
+
from .files.commands import cli as file_commands
|
8
|
+
from .hardware.commands import cli as hardware_commands
|
9
|
+
from .lint.commands import cli as lint_commands
|
10
|
+
|
11
|
+
|
12
|
+
@click.group()
|
13
|
+
@click.option(
|
14
|
+
"--host",
|
15
|
+
required=False,
|
16
|
+
default=lambda: os.environ.get("PRIMITIVE_HOST", "api.primitive.tech"),
|
17
|
+
show_default="api.primitive.tech",
|
18
|
+
help="Environment of Primitive API",
|
19
|
+
)
|
20
|
+
@click.option(
|
21
|
+
"--yes", is_flag=True, show_default=True, default=False, help="Skip interactions."
|
22
|
+
)
|
23
|
+
@click.option(
|
24
|
+
"--debug", is_flag=True, show_default=True, default=False, help="Enable debug mode."
|
25
|
+
)
|
26
|
+
@click.option(
|
27
|
+
"--json",
|
28
|
+
is_flag=True,
|
29
|
+
show_default=True,
|
30
|
+
default=False,
|
31
|
+
help="Turn all outputs into JSON.",
|
32
|
+
)
|
33
|
+
@click.option(
|
34
|
+
"-v",
|
35
|
+
"--verbose",
|
36
|
+
count=True,
|
37
|
+
help="Verbosity of output levels.",
|
38
|
+
)
|
39
|
+
@click.version_option(__version__)
|
40
|
+
@click.pass_context
|
41
|
+
def cli(context, host, yes, debug, json, verbose):
|
42
|
+
"""primitive - a CLI tool for https://primitive.design"""
|
43
|
+
context.ensure_object(dict)
|
44
|
+
context.obj["YES"] = yes
|
45
|
+
context.obj["DEBUG"] = debug
|
46
|
+
context.obj["JSON"] = json
|
47
|
+
context.obj["VERBOSE"] = verbose
|
48
|
+
context.obj["HOST"] = host
|
49
|
+
if "config" not in sys.argv:
|
50
|
+
context.obj["PRIMITIVE"] = Primitive(host=host, DEBUG=debug, JSON=json)
|
51
|
+
|
52
|
+
|
53
|
+
cli.add_command(config_command, "config")
|
54
|
+
cli.add_command(whoami_command, "whoami")
|
55
|
+
cli.add_command(file_commands, "files")
|
56
|
+
cli.add_command(hardware_commands, "hardware")
|
57
|
+
cli.add_command(lint_commands, "lint")
|
58
|
+
|
59
|
+
if __name__ == "__main__":
|
60
|
+
cli(obj={})
|
primitive/client.py
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
import sys
|
2
|
+
from .auth.actions import Auth
|
3
|
+
from .projects.actions import Projects
|
4
|
+
from .graphql.sdk import create_session
|
5
|
+
from .utils.config import read_config_file
|
6
|
+
from .files.actions import Files
|
7
|
+
from .simulations.actions import Simulations
|
8
|
+
from .hardware.actions import Hardware
|
9
|
+
from .lint.actions import Lint
|
10
|
+
|
11
|
+
from loguru import logger
|
12
|
+
|
13
|
+
logger.disable("primitive")
|
14
|
+
|
15
|
+
|
16
|
+
class Primitive:
|
17
|
+
def __init__(
|
18
|
+
self,
|
19
|
+
host: str = "api.primitive.tech",
|
20
|
+
DEBUG: bool = False,
|
21
|
+
JSON: bool = False,
|
22
|
+
token: str = None,
|
23
|
+
transport: str = None,
|
24
|
+
) -> None:
|
25
|
+
self.host = host
|
26
|
+
self.DEBUG = DEBUG
|
27
|
+
self.JSON = JSON
|
28
|
+
|
29
|
+
if self.DEBUG:
|
30
|
+
logger.enable("primitive")
|
31
|
+
logger.remove()
|
32
|
+
logger.add(
|
33
|
+
sink=sys.stderr,
|
34
|
+
serialize=self.JSON,
|
35
|
+
catch=True,
|
36
|
+
backtrace=True,
|
37
|
+
diagnose=True,
|
38
|
+
)
|
39
|
+
|
40
|
+
host = self.host
|
41
|
+
fingerprint = None
|
42
|
+
if not token and not transport:
|
43
|
+
self.get_host_config()
|
44
|
+
token = self.host_config.get("token")
|
45
|
+
transport = self.host_config.get("transport")
|
46
|
+
fingerprint = self.host_config.get("fingerprint")
|
47
|
+
else:
|
48
|
+
self.host_config = {"username": "", "token": token, "transport": transport}
|
49
|
+
|
50
|
+
self.session = create_session(
|
51
|
+
host=self.host, token=token, transport=transport, fingerprint=fingerprint
|
52
|
+
)
|
53
|
+
|
54
|
+
self.auth: Auth = Auth(self)
|
55
|
+
self.projects: Projects = Projects(self)
|
56
|
+
self.files: Files = Files(self)
|
57
|
+
self.simulations: Simulations = Simulations(self)
|
58
|
+
self.hardware: Hardware = Hardware(self)
|
59
|
+
self.lint: Lint = Lint(self)
|
60
|
+
|
61
|
+
def get_host_config(self):
|
62
|
+
self.full_config = read_config_file()
|
63
|
+
self.host_config = self.full_config.get(self.host)
|
64
|
+
|
65
|
+
if not self.host_config:
|
66
|
+
raise KeyError(f"Host {self.host} not found in config file.")
|
@@ -0,0 +1,76 @@
|
|
1
|
+
from pathlib import Path
|
2
|
+
from gql import gql
|
3
|
+
from primitive.graphql.sdk import create_requests_session
|
4
|
+
|
5
|
+
|
6
|
+
from primitive.utils.actions import BaseAction
|
7
|
+
|
8
|
+
|
9
|
+
class Files(BaseAction):
|
10
|
+
def trace_create(
|
11
|
+
self,
|
12
|
+
file_id: str,
|
13
|
+
signal_id: str,
|
14
|
+
signal_name: str,
|
15
|
+
module_name: str,
|
16
|
+
is_vector: bool,
|
17
|
+
size: int,
|
18
|
+
):
|
19
|
+
mutation = gql(
|
20
|
+
"""
|
21
|
+
mutation createTrace($input: TraceCreateInput!) {
|
22
|
+
traceCreate(input: $input) {
|
23
|
+
... on Trace {
|
24
|
+
id
|
25
|
+
signalId
|
26
|
+
signalName
|
27
|
+
}
|
28
|
+
}
|
29
|
+
}
|
30
|
+
"""
|
31
|
+
)
|
32
|
+
input = {
|
33
|
+
"fileId": file_id,
|
34
|
+
"signalId": signal_id,
|
35
|
+
"signalName": signal_name,
|
36
|
+
"moduleName": module_name,
|
37
|
+
"isVector": is_vector,
|
38
|
+
"size": size,
|
39
|
+
}
|
40
|
+
variables = {"input": input}
|
41
|
+
result = self.primitive.session.execute(mutation, variable_values=variables)
|
42
|
+
return result
|
43
|
+
|
44
|
+
def file_upload(self, path: Path, is_public: bool = False, key_prefix: str = ""):
|
45
|
+
file_path = str(path.resolve())
|
46
|
+
if path.exists() is False:
|
47
|
+
raise FileNotFoundError(f"File not found at {file_path}")
|
48
|
+
|
49
|
+
if is_public:
|
50
|
+
operations = (
|
51
|
+
"""{ "query": "mutation fileUpload($input: FileUploadInput!) { fileUpload(input: $input) { ... on File { id } } }", "variables": { "input": { "fileObject": null, "isPublic": true, "filePath": \""""
|
52
|
+
+ file_path
|
53
|
+
+ """\", "keyPrefix": \""""
|
54
|
+
+ key_prefix
|
55
|
+
+ """\" } } }"""
|
56
|
+
) # noqa
|
57
|
+
|
58
|
+
else:
|
59
|
+
operations = (
|
60
|
+
"""{ "query": "mutation fileUpload($input: FileUploadInput!) { fileUpload(input: $input) { ... on File { id } } }", "variables": { "input": { "fileObject": null, "isPublic": false, "filePath": \""""
|
61
|
+
+ file_path
|
62
|
+
+ """\", "keyPrefix": \""""
|
63
|
+
+ key_prefix
|
64
|
+
+ """\" } } }"""
|
65
|
+
) # noqa
|
66
|
+
body = {
|
67
|
+
"operations": ("", operations),
|
68
|
+
"map": ("", '{"fileObject": ["variables.input.fileObject"]}'),
|
69
|
+
"fileObject": (path.name, open(path, "rb")),
|
70
|
+
}
|
71
|
+
|
72
|
+
session = create_requests_session(self.primitive.host_config)
|
73
|
+
transport = self.primitive.host_config.get("transport")
|
74
|
+
url = f"{transport}://{self.primitive.host}/"
|
75
|
+
response = session.post(url, files=body)
|
76
|
+
return response
|
@@ -0,0 +1,28 @@
|
|
1
|
+
import click
|
2
|
+
from ..utils.printer import print_result
|
3
|
+
from pathlib import Path
|
4
|
+
import typing
|
5
|
+
|
6
|
+
if typing.TYPE_CHECKING:
|
7
|
+
from ..client import Primitive
|
8
|
+
|
9
|
+
|
10
|
+
@click.group("files")
|
11
|
+
@click.pass_context
|
12
|
+
def cli(context):
|
13
|
+
"""Files"""
|
14
|
+
pass
|
15
|
+
|
16
|
+
|
17
|
+
@cli.command("upload")
|
18
|
+
@click.pass_context
|
19
|
+
@click.argument("path", type=click.Path(exists=True))
|
20
|
+
@click.option("--public", "-p", help="Is this a Public file", is_flag=True)
|
21
|
+
@click.option("--key-prefix", "-k", help="Key Prefix", default="")
|
22
|
+
def file_upload_command(context, path, public, key_prefix):
|
23
|
+
"""File Upload"""
|
24
|
+
primitive: Primitive = context.obj.get("PRIMITIVE")
|
25
|
+
path = Path(path)
|
26
|
+
result = primitive.files.file_upload(path, is_public=public, key_prefix=key_prefix)
|
27
|
+
message = result.json()
|
28
|
+
print_result(message=message, context=context)
|
File without changes
|
primitive/graphql/sdk.py
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
from typing import Dict
|
2
|
+
from gql import Client
|
3
|
+
from gql.transport.aiohttp import AIOHTTPTransport
|
4
|
+
import requests
|
5
|
+
from primitive.__about__ import __version__
|
6
|
+
|
7
|
+
|
8
|
+
def create_session(
|
9
|
+
token: str,
|
10
|
+
host: str = "api.primitive.tech",
|
11
|
+
transport: str = "https",
|
12
|
+
fingerprint: str = None,
|
13
|
+
fetch_schema_from_transport: bool = False,
|
14
|
+
):
|
15
|
+
url = f"{transport}://{host}/"
|
16
|
+
headers = {
|
17
|
+
"Content-Type": "application/json",
|
18
|
+
"Accept": "application/json",
|
19
|
+
"x-primitive-agent": f"primitive@{__version__}",
|
20
|
+
}
|
21
|
+
if token:
|
22
|
+
headers["Authorization"] = f"Token {token}"
|
23
|
+
|
24
|
+
if fingerprint:
|
25
|
+
headers["x-primitive-fingerprint"] = fingerprint
|
26
|
+
|
27
|
+
transport = AIOHTTPTransport(url=url, headers=headers)
|
28
|
+
session = Client(
|
29
|
+
transport=transport,
|
30
|
+
fetch_schema_from_transport=fetch_schema_from_transport,
|
31
|
+
)
|
32
|
+
return session
|
33
|
+
|
34
|
+
|
35
|
+
def create_requests_session(
|
36
|
+
host_config: Dict,
|
37
|
+
):
|
38
|
+
token = host_config.get("token")
|
39
|
+
|
40
|
+
headers = {
|
41
|
+
# "Content-Type": "multipart/form-data", # DO NOT ADD THIS MIME TYPE IT BREAKS
|
42
|
+
"Accept": "application/json",
|
43
|
+
"x-primitive-agent": f"primitive@{__version__}",
|
44
|
+
}
|
45
|
+
if token:
|
46
|
+
headers["Authorization"] = f"Token {token}"
|
47
|
+
|
48
|
+
if fingerprint := host_config.get("fingerprint", None):
|
49
|
+
headers["x-primitive-fingerprint"] = fingerprint
|
50
|
+
|
51
|
+
session = requests.Session()
|
52
|
+
session.headers.update(headers)
|
53
|
+
return session
|