flamepy 0.3.0__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.
flamepy/service.py ADDED
@@ -0,0 +1,225 @@
1
+ """
2
+ Copyright 2025 The Flame Authors.
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+ http://www.apache.org/licenses/LICENSE-2.0
7
+ Unless required by applicable law or agreed to in writing, software
8
+ distributed under the License is distributed on an "AS IS" BASIS,
9
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
+ See the License for the specific language governing permissions and
11
+ limitations under the License.
12
+ """
13
+
14
+ import asyncio
15
+ import os
16
+ import grpc
17
+ from abc import ABC, abstractmethod
18
+ from typing import Optional, Dict, Any, Union
19
+ from dataclasses import dataclass
20
+ import logging
21
+
22
+ from .types import Shim, FlameError, FlameErrorCode
23
+ from .shim_pb2_grpc import GrpcShimServicer, add_GrpcShimServicer_to_server
24
+ from .types_pb2 import Result, EmptyRequest
25
+ from .shim_pb2 import TaskOutput as TaskOutputProto
26
+
27
+ logger = logging.getLogger(__name__)
28
+
29
+ @dataclass
30
+ class ApplicationContext:
31
+ """Context for an application."""
32
+ name: str
33
+ shim: Shim
34
+ url: Optional[str] = None
35
+ command: Optional[str] = None
36
+
37
+
38
+ @dataclass
39
+ class SessionContext:
40
+ """Context for a session."""
41
+ session_id: str
42
+ application: ApplicationContext
43
+ common_data: Optional[bytes] = None
44
+
45
+
46
+ @dataclass
47
+ class TaskContext:
48
+ """Context for a task."""
49
+ task_id: str
50
+ session_id: str
51
+ input: Optional[bytes] = None
52
+
53
+
54
+ @dataclass
55
+ class TaskOutput:
56
+ """Output from a task."""
57
+ data: Optional[bytes] = None
58
+
59
+
60
+ class FlameService:
61
+ """Base class for implementing Flame services."""
62
+
63
+ @abstractmethod
64
+ async def on_session_enter(self, context: SessionContext):
65
+ """
66
+ Called when entering a session.
67
+
68
+ Args:
69
+ context: Session context information
70
+
71
+ Returns:
72
+ True if successful, False otherwise
73
+ """
74
+ pass
75
+
76
+ @abstractmethod
77
+ async def on_task_invoke(self, context: TaskContext) -> TaskOutput:
78
+ """
79
+ Called when a task is invoked.
80
+
81
+ Args:
82
+ context: Task context information
83
+
84
+ Returns:
85
+ Task output
86
+ """
87
+ pass
88
+
89
+ @abstractmethod
90
+ async def on_session_leave(self):
91
+ """
92
+ Called when leaving a session.
93
+
94
+ Returns:
95
+ True if successful, False otherwise
96
+ """
97
+ pass
98
+
99
+
100
+ class GrpcShimServicer(GrpcShimServicer):
101
+ """gRPC servicer implementation for GrpcShim service."""
102
+
103
+ def __init__(self, service: FlameService):
104
+ self._service = service
105
+
106
+ async def OnSessionEnter(self, request, context):
107
+ """Handle OnSessionEnter RPC call."""
108
+ try:
109
+ # Convert protobuf request to SessionContext
110
+ app_context = ApplicationContext(
111
+ name=request.application.name,
112
+ shim=Shim(request.application.shim),
113
+ url=request.application.url,
114
+ command=request.application.command
115
+ )
116
+
117
+ session_context = SessionContext(
118
+ session_id=request.session_id,
119
+ application=app_context,
120
+ common_data=request.common_data
121
+ )
122
+
123
+ # Call the service implementation
124
+ await self._service.on_session_enter(session_context)
125
+
126
+ # Return result
127
+ return Result(
128
+ return_code=0,
129
+ )
130
+
131
+ except Exception as e:
132
+ return Result(
133
+ return_code=1,
134
+ message=f"Session enter error: {str(e)}"
135
+ )
136
+
137
+ async def OnTaskInvoke(self, request, context):
138
+ """Handle OnTaskInvoke RPC call."""
139
+ try:
140
+ # Convert protobuf request to TaskContext
141
+ task_context = TaskContext(
142
+ task_id=request.task_id,
143
+ session_id=request.session_id,
144
+ input=request.input
145
+ )
146
+
147
+ # Call the service implementation
148
+ output = await self._service.on_task_invoke(task_context)
149
+
150
+ # Return task output
151
+ return TaskOutputProto(data=output.data)
152
+
153
+ except Exception as e:
154
+ return TaskOutputProto(data=None)
155
+
156
+ async def OnSessionLeave(self, request, context):
157
+ """Handle OnSessionLeave RPC call."""
158
+ try:
159
+ # Call the service implementation
160
+ await self._service.on_session_leave()
161
+
162
+ # Return result
163
+ return Result(
164
+ return_code=0,
165
+ )
166
+
167
+ except Exception as e:
168
+ return Result(
169
+ return_code=1,
170
+ message=f"Session leave error: {str(e)}"
171
+ )
172
+
173
+ class GrpcShimServer:
174
+ """Server for gRPC shim services."""
175
+
176
+ def __init__(self, service: FlameService):
177
+ self._service = service
178
+ self._server = None
179
+
180
+ async def start(self):
181
+ """Start the gRPC server."""
182
+ try:
183
+ # Create gRPC server
184
+ self._server = grpc.aio.server()
185
+
186
+ # Add servicer to server
187
+ shim_servicer = GrpcShimServicer(self._service)
188
+ add_GrpcShimServicer_to_server(shim_servicer, self._server)
189
+
190
+ # Listen on Unix socket
191
+ socket_path = f"/tmp/flame/shim/fsi.sock"
192
+ self._server.add_insecure_port(f"unix://{socket_path}")
193
+
194
+ # Start server
195
+ await self._server.start()
196
+
197
+ print(f"Flame Python service started on Unix socket: {socket_path}")
198
+
199
+ # Keep server running
200
+ await self._server.wait_for_termination()
201
+
202
+ except Exception as e:
203
+ raise FlameError(
204
+ FlameErrorCode.INTERNAL,
205
+ f"Failed to start gRPC server: {str(e)}"
206
+ )
207
+
208
+ async def stop(self):
209
+ """Stop the gRPC server."""
210
+ if self._server:
211
+ await self._server.stop(grace=5)
212
+ print("gRPC shim server stopped")
213
+
214
+
215
+ async def run(service: FlameService):
216
+ """
217
+ Run a gRPC shim server.
218
+
219
+ Args:
220
+ service: The shim service implementation
221
+ """
222
+
223
+ server = GrpcShimServer(service)
224
+ await server.start()
225
+
flamepy/shim_pb2.py ADDED
@@ -0,0 +1,46 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
3
+ # NO CHECKED-IN PROTOBUF GENCODE
4
+ # source: shim.proto
5
+ # Protobuf Python Version: 6.31.1
6
+ """Generated protocol buffer code."""
7
+ from google.protobuf import descriptor as _descriptor
8
+ from google.protobuf import descriptor_pool as _descriptor_pool
9
+ from google.protobuf import runtime_version as _runtime_version
10
+ from google.protobuf import symbol_database as _symbol_database
11
+ from google.protobuf.internal import builder as _builder
12
+ _runtime_version.ValidateProtobufRuntimeVersion(
13
+ _runtime_version.Domain.PUBLIC,
14
+ 6,
15
+ 31,
16
+ 1,
17
+ '',
18
+ 'shim.proto'
19
+ )
20
+ # @@protoc_insertion_point(imports)
21
+
22
+ _sym_db = _symbol_database.Default()
23
+
24
+
25
+ import flamepy.types_pb2 as types__pb2
26
+
27
+
28
+ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\nshim.proto\x12\x05\x66lame\x1a\x0btypes.proto\"}\n\x12\x41pplicationContext\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x19\n\x04shim\x18\x02 \x01(\x0e\x32\x0b.flame.Shim\x12\x12\n\x05image\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x07\x63ommand\x18\x04 \x01(\tH\x01\x88\x01\x01\x42\x08\n\x06_imageB\n\n\x08_command\"~\n\x0eSessionContext\x12\x12\n\nsession_id\x18\x01 \x01(\t\x12.\n\x0b\x61pplication\x18\x02 \x01(\x0b\x32\x19.flame.ApplicationContext\x12\x18\n\x0b\x63ommon_data\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x42\x0e\n\x0c_common_data\"P\n\x0bTaskContext\x12\x0f\n\x07task_id\x18\x01 \x01(\t\x12\x12\n\nsession_id\x18\x02 \x01(\t\x12\x12\n\x05input\x18\x04 \x01(\x0cH\x00\x88\x01\x01\x42\x08\n\x06_input\"(\n\nTaskOutput\x12\x11\n\x04\x64\x61ta\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x42\x07\n\x05_data2\xb5\x01\n\x08GrpcShim\x12\x38\n\x0eOnSessionEnter\x12\x15.flame.SessionContext\x1a\r.flame.Result\"\x00\x12\x37\n\x0cOnTaskInvoke\x12\x12.flame.TaskContext\x1a\x11.flame.TaskOutput\"\x00\x12\x36\n\x0eOnSessionLeave\x12\x13.flame.EmptyRequest\x1a\r.flame.Result\"\x00\x42&Z$github.com/flame-sh/flame/sdk/go/rpcb\x06proto3')
29
+
30
+ _globals = globals()
31
+ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
32
+ _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'shim_pb2', _globals)
33
+ if not _descriptor._USE_C_DESCRIPTORS:
34
+ _globals['DESCRIPTOR']._loaded_options = None
35
+ _globals['DESCRIPTOR']._serialized_options = b'Z$github.com/flame-sh/flame/sdk/go/rpc'
36
+ _globals['_APPLICATIONCONTEXT']._serialized_start=34
37
+ _globals['_APPLICATIONCONTEXT']._serialized_end=159
38
+ _globals['_SESSIONCONTEXT']._serialized_start=161
39
+ _globals['_SESSIONCONTEXT']._serialized_end=287
40
+ _globals['_TASKCONTEXT']._serialized_start=289
41
+ _globals['_TASKCONTEXT']._serialized_end=369
42
+ _globals['_TASKOUTPUT']._serialized_start=371
43
+ _globals['_TASKOUTPUT']._serialized_end=411
44
+ _globals['_GRPCSHIM']._serialized_start=414
45
+ _globals['_GRPCSHIM']._serialized_end=595
46
+ # @@protoc_insertion_point(module_scope)
@@ -0,0 +1,184 @@
1
+ # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2
+ """Client and server classes corresponding to protobuf-defined services."""
3
+ import grpc
4
+ import warnings
5
+
6
+ import flamepy.shim_pb2 as shim__pb2
7
+ import flamepy.types_pb2 as types__pb2
8
+
9
+ GRPC_GENERATED_VERSION = '1.74.0'
10
+ GRPC_VERSION = grpc.__version__
11
+ _version_not_supported = False
12
+
13
+ try:
14
+ from grpc._utilities import first_version_is_lower
15
+ _version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION)
16
+ except ImportError:
17
+ _version_not_supported = True
18
+
19
+ if _version_not_supported:
20
+ raise RuntimeError(
21
+ f'The grpc package installed is at version {GRPC_VERSION},'
22
+ + f' but the generated code in shim_pb2_grpc.py depends on'
23
+ + f' grpcio>={GRPC_GENERATED_VERSION}.'
24
+ + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}'
25
+ + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.'
26
+ )
27
+
28
+
29
+ class GrpcShimStub(object):
30
+ """Missing associated documentation comment in .proto file."""
31
+
32
+ def __init__(self, channel):
33
+ """Constructor.
34
+
35
+ Args:
36
+ channel: A grpc.Channel.
37
+ """
38
+ self.OnSessionEnter = channel.unary_unary(
39
+ '/flame.GrpcShim/OnSessionEnter',
40
+ request_serializer=shim__pb2.SessionContext.SerializeToString,
41
+ response_deserializer=types__pb2.Result.FromString,
42
+ _registered_method=True)
43
+ self.OnTaskInvoke = channel.unary_unary(
44
+ '/flame.GrpcShim/OnTaskInvoke',
45
+ request_serializer=shim__pb2.TaskContext.SerializeToString,
46
+ response_deserializer=shim__pb2.TaskOutput.FromString,
47
+ _registered_method=True)
48
+ self.OnSessionLeave = channel.unary_unary(
49
+ '/flame.GrpcShim/OnSessionLeave',
50
+ request_serializer=types__pb2.EmptyRequest.SerializeToString,
51
+ response_deserializer=types__pb2.Result.FromString,
52
+ _registered_method=True)
53
+
54
+
55
+ class GrpcShimServicer(object):
56
+ """Missing associated documentation comment in .proto file."""
57
+
58
+ def OnSessionEnter(self, request, context):
59
+ """Missing associated documentation comment in .proto file."""
60
+ context.set_code(grpc.StatusCode.UNIMPLEMENTED)
61
+ context.set_details('Method not implemented!')
62
+ raise NotImplementedError('Method not implemented!')
63
+
64
+ def OnTaskInvoke(self, request, context):
65
+ """Missing associated documentation comment in .proto file."""
66
+ context.set_code(grpc.StatusCode.UNIMPLEMENTED)
67
+ context.set_details('Method not implemented!')
68
+ raise NotImplementedError('Method not implemented!')
69
+
70
+ def OnSessionLeave(self, request, context):
71
+ """Missing associated documentation comment in .proto file."""
72
+ context.set_code(grpc.StatusCode.UNIMPLEMENTED)
73
+ context.set_details('Method not implemented!')
74
+ raise NotImplementedError('Method not implemented!')
75
+
76
+
77
+ def add_GrpcShimServicer_to_server(servicer, server):
78
+ rpc_method_handlers = {
79
+ 'OnSessionEnter': grpc.unary_unary_rpc_method_handler(
80
+ servicer.OnSessionEnter,
81
+ request_deserializer=shim__pb2.SessionContext.FromString,
82
+ response_serializer=types__pb2.Result.SerializeToString,
83
+ ),
84
+ 'OnTaskInvoke': grpc.unary_unary_rpc_method_handler(
85
+ servicer.OnTaskInvoke,
86
+ request_deserializer=shim__pb2.TaskContext.FromString,
87
+ response_serializer=shim__pb2.TaskOutput.SerializeToString,
88
+ ),
89
+ 'OnSessionLeave': grpc.unary_unary_rpc_method_handler(
90
+ servicer.OnSessionLeave,
91
+ request_deserializer=types__pb2.EmptyRequest.FromString,
92
+ response_serializer=types__pb2.Result.SerializeToString,
93
+ ),
94
+ }
95
+ generic_handler = grpc.method_handlers_generic_handler(
96
+ 'flame.GrpcShim', rpc_method_handlers)
97
+ server.add_generic_rpc_handlers((generic_handler,))
98
+ server.add_registered_method_handlers('flame.GrpcShim', rpc_method_handlers)
99
+
100
+
101
+ # This class is part of an EXPERIMENTAL API.
102
+ class GrpcShim(object):
103
+ """Missing associated documentation comment in .proto file."""
104
+
105
+ @staticmethod
106
+ def OnSessionEnter(request,
107
+ target,
108
+ options=(),
109
+ channel_credentials=None,
110
+ call_credentials=None,
111
+ insecure=False,
112
+ compression=None,
113
+ wait_for_ready=None,
114
+ timeout=None,
115
+ metadata=None):
116
+ return grpc.experimental.unary_unary(
117
+ request,
118
+ target,
119
+ '/flame.GrpcShim/OnSessionEnter',
120
+ shim__pb2.SessionContext.SerializeToString,
121
+ types__pb2.Result.FromString,
122
+ options,
123
+ channel_credentials,
124
+ insecure,
125
+ call_credentials,
126
+ compression,
127
+ wait_for_ready,
128
+ timeout,
129
+ metadata,
130
+ _registered_method=True)
131
+
132
+ @staticmethod
133
+ def OnTaskInvoke(request,
134
+ target,
135
+ options=(),
136
+ channel_credentials=None,
137
+ call_credentials=None,
138
+ insecure=False,
139
+ compression=None,
140
+ wait_for_ready=None,
141
+ timeout=None,
142
+ metadata=None):
143
+ return grpc.experimental.unary_unary(
144
+ request,
145
+ target,
146
+ '/flame.GrpcShim/OnTaskInvoke',
147
+ shim__pb2.TaskContext.SerializeToString,
148
+ shim__pb2.TaskOutput.FromString,
149
+ options,
150
+ channel_credentials,
151
+ insecure,
152
+ call_credentials,
153
+ compression,
154
+ wait_for_ready,
155
+ timeout,
156
+ metadata,
157
+ _registered_method=True)
158
+
159
+ @staticmethod
160
+ def OnSessionLeave(request,
161
+ target,
162
+ options=(),
163
+ channel_credentials=None,
164
+ call_credentials=None,
165
+ insecure=False,
166
+ compression=None,
167
+ wait_for_ready=None,
168
+ timeout=None,
169
+ metadata=None):
170
+ return grpc.experimental.unary_unary(
171
+ request,
172
+ target,
173
+ '/flame.GrpcShim/OnSessionLeave',
174
+ types__pb2.EmptyRequest.SerializeToString,
175
+ types__pb2.Result.FromString,
176
+ options,
177
+ channel_credentials,
178
+ insecure,
179
+ call_credentials,
180
+ compression,
181
+ wait_for_ready,
182
+ timeout,
183
+ metadata,
184
+ _registered_method=True)
flamepy/test_flame.py ADDED
@@ -0,0 +1,195 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Tests for the Flame Python SDK.
4
+ """
5
+
6
+ import pytest
7
+ import asyncio
8
+ from unittest.mock import Mock, AsyncMock
9
+ from datetime import datetime
10
+
11
+ from flamepy import (
12
+ Connection, SessionAttributes, ApplicationAttributes,
13
+ Session, Task, Application, Shim, SessionState, TaskState,
14
+ ApplicationState, FlameError, FlameErrorCode, TaskInformer
15
+ )
16
+
17
+
18
+ class TestTypes:
19
+ """Test type definitions."""
20
+
21
+ def test_session_attributes(self):
22
+ """Test SessionAttributes."""
23
+ attrs = SessionAttributes(
24
+ application="test-app",
25
+ slots=2,
26
+ common_data=b"test data"
27
+ )
28
+ assert attrs.application == "test-app"
29
+ assert attrs.slots == 2
30
+ assert attrs.common_data == b"test data"
31
+
32
+ def test_application_attributes(self):
33
+ """Test ApplicationAttributes."""
34
+ attrs = ApplicationAttributes(
35
+ name="test-app",
36
+ shim=Shim.SHELL,
37
+ command="python",
38
+ arguments=["script.py"],
39
+ environments=["PATH=/usr/bin"],
40
+ working_directory="/tmp"
41
+ )
42
+ assert attrs.name == "test-app"
43
+ assert attrs.shim == Shim.SHELL
44
+ assert attrs.command == "python"
45
+ assert attrs.arguments == ["script.py"]
46
+ assert attrs.environments == ["PATH=/usr/bin"]
47
+ assert attrs.working_directory == "/tmp"
48
+
49
+ def test_session(self):
50
+ """Test Session."""
51
+ session = Session(
52
+ id="session-1",
53
+ application="test-app",
54
+ slots=2,
55
+ state=SessionState.OPEN,
56
+ creation_time=datetime.now(),
57
+ pending=1,
58
+ running=1,
59
+ succeed=0,
60
+ failed=0
61
+ )
62
+ assert session.id == "session-1"
63
+ assert session.application == "test-app"
64
+ assert session.slots == 2
65
+ assert session.state == SessionState.OPEN
66
+
67
+ def test_task(self):
68
+ """Test Task."""
69
+ task = Task(
70
+ id="task-1",
71
+ session_id="session-1",
72
+ state=TaskState.PENDING,
73
+ creation_time=datetime.now(),
74
+ input=b"test input"
75
+ )
76
+ assert task.id == "task-1"
77
+ assert task.session_id == "session-1"
78
+ assert task.state == TaskState.PENDING
79
+ assert task.input == b"test input"
80
+ assert not task.is_completed()
81
+
82
+ # Test completed task
83
+ completed_task = Task(
84
+ id="task-2",
85
+ session_id="session-1",
86
+ state=TaskState.SUCCEED,
87
+ creation_time=datetime.now()
88
+ )
89
+ assert completed_task.is_completed()
90
+
91
+ def test_application(self):
92
+ """Test Application."""
93
+ app = Application(
94
+ id="app-1",
95
+ name="test-app",
96
+ shim=Shim.SHELL,
97
+ state=ApplicationState.ENABLED,
98
+ creation_time=datetime.now(),
99
+ command="python",
100
+ arguments=["script.py"]
101
+ )
102
+ assert app.id == "app-1"
103
+ assert app.name == "test-app"
104
+ assert app.shim == Shim.SHELL
105
+ assert app.state == ApplicationState.ENABLED
106
+
107
+
108
+ class TestFlameError:
109
+ """Test FlameError."""
110
+
111
+ def test_flame_error(self):
112
+ """Test FlameError creation and string representation."""
113
+ error = FlameError(FlameErrorCode.INVALID_CONFIG, "Test error")
114
+ assert error.code == FlameErrorCode.INVALID_CONFIG
115
+ assert error.message == "Test error"
116
+ assert "Test error" in str(error)
117
+ assert "INVALID_CONFIG" in str(error)
118
+
119
+
120
+ class TestTaskInformer:
121
+ """Test TaskInformer."""
122
+
123
+ def test_task_informer_default(self):
124
+ """Test default TaskInformer implementation."""
125
+ informer = TaskInformer()
126
+ task = Task(
127
+ id="task-1",
128
+ session_id="session-1",
129
+ state=TaskState.PENDING,
130
+ creation_time=datetime.now()
131
+ )
132
+ error = FlameError(FlameErrorCode.INTERNAL, "Test error")
133
+
134
+ # Should not raise any exceptions
135
+ informer.on_update(task)
136
+ informer.on_error(error)
137
+
138
+
139
+ class TestConnection:
140
+ """Test Connection class."""
141
+
142
+ @pytest.mark.asyncio
143
+ async def test_connection_connect_empty_addr(self):
144
+ """Test connection with empty address."""
145
+ with pytest.raises(FlameError) as exc_info:
146
+ await Connection.connect("")
147
+ assert exc_info.value.code == FlameErrorCode.INVALID_CONFIG
148
+ assert "address cannot be empty" in exc_info.value.message
149
+
150
+ @pytest.mark.asyncio
151
+ async def test_connection_connect_invalid_addr(self):
152
+ """Test connection with invalid address."""
153
+ with pytest.raises(FlameError) as exc_info:
154
+ await Connection.connect("invalid://address")
155
+ assert exc_info.value.code == FlameErrorCode.INVALID_CONFIG
156
+
157
+
158
+ class TestEnums:
159
+ """Test enum values."""
160
+
161
+ def test_session_state_values(self):
162
+ """Test SessionState enum values."""
163
+ assert SessionState.OPEN == 0
164
+ assert SessionState.CLOSED == 1
165
+
166
+ def test_task_state_values(self):
167
+ """Test TaskState enum values."""
168
+ assert TaskState.PENDING == 0
169
+ assert TaskState.RUNNING == 1
170
+ assert TaskState.SUCCEED == 2
171
+ assert TaskState.FAILED == 3
172
+
173
+ def test_application_state_values(self):
174
+ """Test ApplicationState enum values."""
175
+ assert ApplicationState.ENABLED == 0
176
+ assert ApplicationState.DISABLED == 1
177
+
178
+ def test_shim_values(self):
179
+ """Test Shim enum values."""
180
+ assert Shim.LOG == 0
181
+ assert Shim.STDIO == 1
182
+ assert Shim.WASM == 2
183
+ assert Shim.SHELL == 3
184
+ assert Shim.GRPC == 4
185
+
186
+ def test_flame_error_code_values(self):
187
+ """Test FlameErrorCode enum values."""
188
+ assert FlameErrorCode.INVALID_CONFIG == 0
189
+ assert FlameErrorCode.INVALID_STATE == 1
190
+ assert FlameErrorCode.INVALID_ARGUMENT == 2
191
+ assert FlameErrorCode.INTERNAL == 3
192
+
193
+
194
+ if __name__ == "__main__":
195
+ pytest.main([__file__])