sentry-relay 0.9.22__tar.gz

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.
@@ -0,0 +1,19 @@
1
+ Metadata-Version: 2.4
2
+ Name: sentry-relay
3
+ Version: 0.9.22
4
+ Summary: A python library to access sentry relay functionality.
5
+ Author: Sentry
6
+ Author-email: hello@sentry.io
7
+ License: FSL-1.0-Apache-2.0
8
+ Platform: any
9
+ Requires-Python: >=3.10
10
+ Description-Content-Type: text/markdown
11
+ Requires-Dist: milksnake>=0.1.6
12
+ Dynamic: author
13
+ Dynamic: author-email
14
+ Dynamic: description-content-type
15
+ Dynamic: license
16
+ Dynamic: platform
17
+ Dynamic: requires-dist
18
+ Dynamic: requires-python
19
+ Dynamic: summary
File without changes
Binary file
@@ -0,0 +1,23 @@
1
+ __all__ = []
2
+
3
+
4
+ def _import_all():
5
+ import pkgutil
6
+
7
+ glob = globals()
8
+ for _, modname, _ in pkgutil.iter_modules(__path__):
9
+ if modname[:1] == "_":
10
+ continue
11
+ mod = __import__("sentry_relay.%s" % modname, glob, glob, ["__name__"])
12
+ if not hasattr(mod, "__all__"):
13
+ continue
14
+ __all__.extend(mod.__all__)
15
+ for name in mod.__all__:
16
+ obj = getattr(mod, name)
17
+ if hasattr(obj, "__module__"):
18
+ obj.__module__ = "sentry_relay"
19
+ glob[name] = obj
20
+
21
+
22
+ _import_all()
23
+ del _import_all
@@ -0,0 +1,149 @@
1
+ import json
2
+ import uuid
3
+ from typing import Callable, Any
4
+
5
+ from sentry_relay._lowlevel import lib
6
+ from sentry_relay.utils import (
7
+ RustObject,
8
+ encode_str,
9
+ decode_str,
10
+ decode_uuid,
11
+ rustcall,
12
+ make_buf,
13
+ )
14
+ from sentry_relay.exceptions import UnpackErrorBadSignature
15
+
16
+
17
+ __all__ = [
18
+ "PublicKey",
19
+ "SecretKey",
20
+ "generate_key_pair",
21
+ "create_register_challenge",
22
+ "validate_register_response",
23
+ "is_version_supported",
24
+ ]
25
+
26
+
27
+ class PublicKey(RustObject):
28
+ __dealloc_func__ = lib.relay_publickey_free
29
+
30
+ @classmethod
31
+ def parse(cls, string):
32
+ s = encode_str(string)
33
+ ptr = rustcall(lib.relay_publickey_parse, s)
34
+ return cls._from_objptr(ptr)
35
+
36
+ def verify(self, buf, sig, max_age=None):
37
+ buf = make_buf(buf)
38
+ sig = encode_str(sig)
39
+ if max_age is None:
40
+ return self._methodcall(lib.relay_publickey_verify, buf, sig)
41
+ return self._methodcall(lib.relay_publickey_verify_timestamp, buf, sig, max_age)
42
+
43
+ def unpack(
44
+ self,
45
+ buf,
46
+ sig,
47
+ max_age=None,
48
+ json_loads: Callable[[str | bytes], Any] = json.loads,
49
+ ):
50
+ if not self.verify(buf, sig, max_age):
51
+ raise UnpackErrorBadSignature("invalid signature")
52
+ return json_loads(buf)
53
+
54
+ def __str__(self):
55
+ return decode_str(self._methodcall(lib.relay_publickey_to_string), free=True)
56
+
57
+ def __repr__(self):
58
+ return f"<{self.__class__.__name__} {str(self)!r}>"
59
+
60
+
61
+ class SecretKey(RustObject):
62
+ __dealloc_func__ = lib.relay_secretkey_free
63
+
64
+ @classmethod
65
+ def parse(cls, string):
66
+ s = encode_str(string)
67
+ ptr = rustcall(lib.relay_secretkey_parse, s)
68
+ return cls._from_objptr(ptr)
69
+
70
+ def sign(self, value):
71
+ buf = make_buf(value)
72
+ return decode_str(self._methodcall(lib.relay_secretkey_sign, buf), free=True)
73
+
74
+ def pack(self, data):
75
+ # TODO(@anonrig): Look into separators requirement
76
+ packed = json.dumps(data, separators=(",", ":")).encode()
77
+ return packed, self.sign(packed)
78
+
79
+ def __str__(self):
80
+ return decode_str(self._methodcall(lib.relay_secretkey_to_string), free=True)
81
+
82
+ def __repr__(self):
83
+ return f"<{self.__class__.__name__} {str(self)!r}>"
84
+
85
+
86
+ def generate_key_pair():
87
+ rv = rustcall(lib.relay_generate_key_pair)
88
+ return (
89
+ SecretKey._from_objptr(rv.secret_key),
90
+ PublicKey._from_objptr(rv.public_key),
91
+ )
92
+
93
+
94
+ def generate_relay_id():
95
+ return decode_uuid(rustcall(lib.relay_generate_relay_id))
96
+
97
+
98
+ def create_register_challenge(
99
+ data,
100
+ signature,
101
+ secret,
102
+ max_age=60,
103
+ json_loads: Callable[[str | bytes], Any] = json.loads,
104
+ ):
105
+ challenge_json = rustcall(
106
+ lib.relay_create_register_challenge,
107
+ make_buf(data),
108
+ encode_str(signature),
109
+ encode_str(secret),
110
+ max_age,
111
+ )
112
+
113
+ challenge = json_loads(decode_str(challenge_json, free=True))
114
+ return {
115
+ "relay_id": uuid.UUID(challenge["relay_id"]),
116
+ "token": challenge["token"],
117
+ }
118
+
119
+
120
+ def validate_register_response(
121
+ data,
122
+ signature,
123
+ secret,
124
+ max_age=60,
125
+ json_loads: Callable[[str | bytes], Any] = json.loads,
126
+ ):
127
+ response_json = rustcall(
128
+ lib.relay_validate_register_response,
129
+ make_buf(data),
130
+ encode_str(signature),
131
+ encode_str(secret),
132
+ max_age,
133
+ )
134
+
135
+ response = json_loads(decode_str(response_json, free=True))
136
+ return {
137
+ "relay_id": uuid.UUID(response["relay_id"]),
138
+ "token": response["token"],
139
+ "public_key": response["public_key"],
140
+ "version": response["version"],
141
+ }
142
+
143
+
144
+ def is_version_supported(version):
145
+ """
146
+ Checks if the provided Relay version is still compatible with this library. The version can be
147
+ ``None``, in which case a legacy Relay is assumed.
148
+ """
149
+ return rustcall(lib.relay_version_supported, encode_str(version or ""))
@@ -0,0 +1,142 @@
1
+ import sys
2
+ from enum import IntEnum
3
+
4
+ from sentry_relay._lowlevel import lib
5
+ from sentry_relay.utils import decode_str, encode_str
6
+
7
+ __all__ = ["DataCategory", "SPAN_STATUS_CODE_TO_NAME", "SPAN_STATUS_NAME_TO_CODE"]
8
+
9
+
10
+ class DataCategory(IntEnum):
11
+ # See _check_generated below.
12
+ # begin generated
13
+ DEFAULT = 0
14
+ ERROR = 1
15
+ TRANSACTION = 2
16
+ SECURITY = 3
17
+ ATTACHMENT = 4
18
+ SESSION = 5
19
+ PROFILE = 6
20
+ REPLAY = 7
21
+ TRANSACTION_PROCESSED = 8
22
+ TRANSACTION_INDEXED = 9
23
+ MONITOR = 10
24
+ PROFILE_INDEXED = 11
25
+ SPAN = 12
26
+ MONITOR_SEAT = 13
27
+ USER_REPORT_V2 = 14
28
+ METRIC_BUCKET = 15
29
+ SPAN_INDEXED = 16
30
+ PROFILE_DURATION = 17
31
+ PROFILE_CHUNK = 18
32
+ METRIC_SECOND = 19
33
+ DO_NOT_USE_REPLAY_VIDEO = 20
34
+ UPTIME = 21
35
+ ATTACHMENT_ITEM = 22
36
+ LOG_ITEM = 23
37
+ LOG_BYTE = 24
38
+ PROFILE_DURATION_UI = 25
39
+ PROFILE_CHUNK_UI = 26
40
+ SEER_AUTOFIX = 27
41
+ SEER_SCANNER = 28
42
+ PREVENT_USER = 29
43
+ PREVENT_REVIEW = 30
44
+ SIZE_ANALYSIS = 31
45
+ INSTALLABLE_BUILD = 32
46
+ TRACE_METRIC = 33
47
+ SEER_USER = 34
48
+ UNKNOWN = -1
49
+ # end generated
50
+
51
+ @classmethod
52
+ def parse(cls, name):
53
+ """
54
+ Parses a `DataCategory` from its API name.
55
+ """
56
+ category = DataCategory(lib.relay_data_category_parse(encode_str(name or "")))
57
+ if category == DataCategory.UNKNOWN:
58
+ return None # Unknown is a Rust-only value, replace with None
59
+ return category
60
+
61
+ @classmethod
62
+ def from_event_type(cls, event_type):
63
+ """
64
+ Parses a `DataCategory` from an event type.
65
+ """
66
+ s = encode_str(event_type or "")
67
+ return DataCategory(lib.relay_data_category_from_event_type(s))
68
+
69
+ @classmethod
70
+ def event_categories(cls):
71
+ """
72
+ Returns categories that count as events, including transactions.
73
+ """
74
+ return [
75
+ DataCategory.DEFAULT,
76
+ DataCategory.ERROR,
77
+ DataCategory.TRANSACTION,
78
+ DataCategory.SECURITY,
79
+ DataCategory.USER_REPORT_V2,
80
+ ]
81
+
82
+ @classmethod
83
+ def error_categories(cls):
84
+ """
85
+ Returns categories that count as traditional error tracking events.
86
+ """
87
+ return [DataCategory.DEFAULT, DataCategory.ERROR, DataCategory.SECURITY]
88
+
89
+ def api_name(self):
90
+ """
91
+ Returns the API name of the given `DataCategory`.
92
+ """
93
+ return decode_str(lib.relay_data_category_name(self.value), free=True)
94
+
95
+
96
+ def _check_generated():
97
+ prefix = "RELAY_DATA_CATEGORY_"
98
+
99
+ attrs = {}
100
+ for attr in dir(lib):
101
+ if attr.startswith(prefix):
102
+ category_name = attr[len(prefix) :]
103
+ attrs[category_name] = getattr(lib, attr)
104
+
105
+ if attrs != DataCategory.__members__:
106
+ values = sorted(
107
+ attrs.items(), key=lambda kv: sys.maxsize if kv[1] == -1 else kv[1]
108
+ )
109
+ generated = "".join(f" {k} = {v}\n" for k, v in values)
110
+ raise AssertionError(
111
+ f"DataCategory enum does not match source!\n\n"
112
+ f"Paste this into `class DataCategory` in py/sentry_relay/consts.py:\n\n"
113
+ f"{generated}"
114
+ )
115
+
116
+
117
+ _check_generated()
118
+
119
+ SPAN_STATUS_CODE_TO_NAME = {}
120
+ SPAN_STATUS_NAME_TO_CODE = {}
121
+
122
+
123
+ def _make_span_statuses():
124
+ prefix = "RELAY_SPAN_STATUS_"
125
+
126
+ for attr in dir(lib):
127
+ if not attr.startswith(prefix):
128
+ continue
129
+
130
+ status_name = attr[len(prefix) :].lower()
131
+ status_code = getattr(lib, attr)
132
+
133
+ SPAN_STATUS_CODE_TO_NAME[status_code] = status_name
134
+ SPAN_STATUS_NAME_TO_CODE[status_name] = status_code
135
+
136
+ # Legacy alias
137
+ SPAN_STATUS_NAME_TO_CODE["unknown_error"] = SPAN_STATUS_NAME_TO_CODE[
138
+ "internal_error"
139
+ ]
140
+
141
+
142
+ _make_span_statuses()
@@ -0,0 +1,65 @@
1
+ from __future__ import annotations
2
+ from typing import TYPE_CHECKING
3
+ from sentry_relay._lowlevel import lib
4
+
5
+
6
+ __all__ = ["RelayError"]
7
+ exceptions_by_code = {}
8
+
9
+
10
+ class RelayError(Exception):
11
+ code = None
12
+
13
+ def __init__(self, msg):
14
+ Exception.__init__(self)
15
+ self.message = msg
16
+ self.rust_info = None
17
+
18
+ def __str__(self):
19
+ rv = self.message
20
+ if self.rust_info is not None:
21
+ return f"{rv}\n\n{self.rust_info}"
22
+ return rv
23
+
24
+
25
+ def _make_error(error_name, base=RelayError, code=None):
26
+ class Exc(base):
27
+ pass
28
+
29
+ Exc.__name__ = error_name
30
+ Exc.__qualname__ = error_name
31
+ if code is not None:
32
+ Exc.code = code
33
+ globals()[Exc.__name__] = Exc
34
+ __all__.append(Exc.__name__)
35
+ return Exc
36
+
37
+
38
+ def _get_error_base(error_name):
39
+ pieces = error_name.split("Error", 1)
40
+ if len(pieces) == 2 and pieces[0] and pieces[1]:
41
+ base_error_name = pieces[0] + "Error"
42
+ base_class = globals().get(base_error_name)
43
+ if base_class is None:
44
+ base_class = _make_error(base_error_name)
45
+ return base_class
46
+ return RelayError
47
+
48
+
49
+ def _make_exceptions():
50
+ prefix = "RELAY_ERROR_CODE_"
51
+ for attr in dir(lib):
52
+ if not attr.startswith(prefix):
53
+ continue
54
+
55
+ error_name = attr[len(prefix) :].title().replace("_", "")
56
+ base = _get_error_base(error_name)
57
+ exc = _make_error(error_name, base=base, code=getattr(lib, attr))
58
+ exceptions_by_code[exc.code] = exc
59
+
60
+
61
+ _make_exceptions()
62
+
63
+ if TYPE_CHECKING:
64
+ # treat unknown attribute names as exception types
65
+ def __getattr__(name: str) -> type[RelayError]: ...