fleet-python 0.2.61__py3-none-any.whl → 0.2.62__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.

Potentially problematic release.


This version of fleet-python might be problematic. Click here for more details.

fleet/_async/tasks.py CHANGED
@@ -289,10 +289,12 @@ def verifier_from_string(
289
289
  # Execute the verifier code in the namespace
290
290
  exec(verifier_func, globals(), local_namespace)
291
291
 
292
- # Find the function that was defined
292
+ # Find the function that was defined (not imported)
293
+ # Functions defined via exec have co_filename == '<string>'
294
+ # Imported functions have their actual module file path
293
295
  func_obj = None
294
296
  for name, obj in local_namespace.items():
295
- if inspect.isfunction(obj):
297
+ if inspect.isfunction(obj) and obj.__code__.co_filename == "<string>":
296
298
  func_obj = obj
297
299
  break
298
300
 
fleet/tasks.py CHANGED
@@ -286,10 +286,12 @@ def verifier_from_string(
286
286
  # Execute the verifier code in the namespace
287
287
  exec(verifier_func, exec_globals, local_namespace)
288
288
 
289
- # Find the function that was defined
289
+ # Find the function that was defined (not imported)
290
+ # Functions defined via exec have co_filename == '<string>'
291
+ # Imported functions have their actual module file path
290
292
  func_obj = None
291
293
  for name, obj in local_namespace.items():
292
- if inspect.isfunction(obj):
294
+ if inspect.isfunction(obj) and obj.__code__.co_filename == "<string>":
293
295
  func_obj = obj
294
296
  break
295
297
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fleet-python
3
- Version: 0.2.61
3
+ Version: 0.2.62
4
4
  Summary: Python SDK for Fleet environments
5
5
  Author-email: Fleet AI <nic@fleet.so>
6
6
  License: Apache-2.0
@@ -28,7 +28,7 @@ fleet/config.py,sha256=uY02ZKxVoXqVDta-0IMWaYJeE1CTXF_fA9NI6QUutmU,319
28
28
  fleet/exceptions.py,sha256=fUmPwWhnT8SR97lYsRq0kLHQHKtSh2eJS0VQ2caSzEI,5055
29
29
  fleet/global_client.py,sha256=frrDAFNM2ywN0JHLtlm9qbE1dQpnQJsavJpb7xSR_bU,1072
30
30
  fleet/models.py,sha256=-8WG10SMkXUljRqD_8pfajCr4PS9qYDVLAZ8RxdJMb0,13392
31
- fleet/tasks.py,sha256=DcdubsXohptHqGAA-vkUyo8O0La50Wbgi2X8kUgcCqE,16724
31
+ fleet/tasks.py,sha256=STR34InS14rMq1DFPsCeAASarsvUqf_ODK_jitT8ORU,16914
32
32
  fleet/types.py,sha256=L4Y82xICf1tzyCLqhLYUgEoaIIS5h9T05TyFNHSWs3s,652
33
33
  fleet/_async/__init__.py,sha256=Wi8Tjj-Lfnxi4cPOkAxh2lynnpEBNni6mI6Iq80uOro,8054
34
34
  fleet/_async/base.py,sha256=oisVTQsx0M_yTmyQJc3oij63uKZ97MHz-xYFsWXxQE8,9202
@@ -36,7 +36,7 @@ fleet/_async/client.py,sha256=Uu9o8LpUce8vOoFxqG0RDe4VtJpu1BJGIFbVecuO71g,32947
36
36
  fleet/_async/exceptions.py,sha256=fUmPwWhnT8SR97lYsRq0kLHQHKtSh2eJS0VQ2caSzEI,5055
37
37
  fleet/_async/global_client.py,sha256=4WskpLHbsDEgWW7hXMD09W-brkp4euy8w2ZJ88594rQ,1103
38
38
  fleet/_async/models.py,sha256=6x3IPYuWz1v6zWjujqgzK2CpR2HB5Rme4LQFLyvUDXE,13164
39
- fleet/_async/tasks.py,sha256=QB6rzQPx6RbavE-BMwMyMMIPpCjapj87tqkrHGTZ8dE,16752
39
+ fleet/_async/tasks.py,sha256=LiDDIrmT9mIqlUna-I903n4yVpLxbmxUSv4lPdaBcDE,16942
40
40
  fleet/_async/env/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
41
41
  fleet/_async/env/client.py,sha256=C5WG5Ir_McXaFPZNdkQjj0w4V7xMIcw3QyVP5g-3kVk,1237
42
42
  fleet/_async/instance/__init__.py,sha256=PtmJq8J8bh0SOQ2V55QURz5GJfobozwtQoqhaOk3_tI,515
@@ -69,10 +69,12 @@ fleet/verifiers/decorator.py,sha256=nAP3O8szXu7md_kpwpz91hGSUNEVLYjwZQZTkQlV1DM,
69
69
  fleet/verifiers/parse.py,sha256=qz9AfJrTbjlg-LU-lE8Ciqi7Yt2a8-cs17FdpjTLhMk,8550
70
70
  fleet/verifiers/sql_differ.py,sha256=TqTLWyK3uOyLbitT6HYzYEzuSFC39wcyhgk3rcm__k8,6525
71
71
  fleet/verifiers/verifier.py,sha256=_lcxXVm8e0xRrK2gNJy9up7pW1zOkPRY5n5lQ85S8jg,14197
72
- fleet_python-0.2.61.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
72
+ fleet_python-0.2.62.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
73
73
  scripts/fix_sync_imports.py,sha256=X9fWLTpiPGkSHsjyQUDepOJkxOqw1DPj7nd8wFlFqLQ,8368
74
74
  scripts/unasync.py,sha256=vWVQxRWX8SRZO5cmzEhpvnG_REhCWXpidIGIpWmEcvI,696
75
- fleet_python-0.2.61.dist-info/METADATA,sha256=P4SV6i9skVoxetYm96_p0hAtq3XFK1eV5e9NpW9WbDM,3304
76
- fleet_python-0.2.61.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
77
- fleet_python-0.2.61.dist-info/top_level.txt,sha256=_3DSmTohvSDf3AIP_BYfGzhwO1ECFwuzg83X-wHCx3Y,23
78
- fleet_python-0.2.61.dist-info/RECORD,,
75
+ tests/__init__.py,sha256=Re1SdyxH8NfyL1kjhi7SQkGP1mYeWB-D6UALqdIMd8I,35
76
+ tests/test_verifier_from_string.py,sha256=Lxi3TpFHFb-hG4-UhLKZJkqo84ax9YJY8G6beO-1erM,13581
77
+ fleet_python-0.2.62.dist-info/METADATA,sha256=pkly98qTBJVHA9jkevvz_v3UKZyGa2-_RFVlbVGLlSg,3304
78
+ fleet_python-0.2.62.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
79
+ fleet_python-0.2.62.dist-info/top_level.txt,sha256=qb1zIbtEktyhRFZdqVytwg54l64qtoZL0wjHB4bUg3c,29
80
+ fleet_python-0.2.62.dist-info/RECORD,,
@@ -1,3 +1,4 @@
1
1
  examples
2
2
  fleet
3
3
  scripts
4
+ tests
tests/__init__.py ADDED
@@ -0,0 +1 @@
1
+ """Tests package for fleet-sdk."""
@@ -0,0 +1,420 @@
1
+ """Comprehensive tests for verifier_from_string function.
2
+
3
+ Tests both sync (fleet/tasks.py) and async (fleet/_async/tasks.py) versions.
4
+ """
5
+
6
+ import pytest
7
+ from fleet.tasks import verifier_from_string as sync_verifier_from_string
8
+ from fleet._async.tasks import verifier_from_string as async_verifier_from_string
9
+
10
+
11
+ class TestSyncVerifierFromString:
12
+ """Tests for sync version of verifier_from_string."""
13
+
14
+ def test_basic_verifier_without_imports(self):
15
+ """Test basic verifier function without any imports."""
16
+ code = """
17
+ def my_verifier(env):
18
+ return 1.0
19
+ """
20
+ verifier = sync_verifier_from_string(
21
+ verifier_func=code,
22
+ verifier_id="test-verifier",
23
+ verifier_key="test-key",
24
+ sha256="test-sha",
25
+ )
26
+ assert verifier is not None
27
+ assert verifier.key == "test-key"
28
+ assert verifier.func.__name__ == "my_verifier"
29
+
30
+ def test_verifier_with_from_fleet_import_verifier(self):
31
+ """Test the bug case: 'from fleet import verifier' should not be selected."""
32
+ code = """
33
+ from fleet import verifier
34
+
35
+ def my_actual_verifier(env):
36
+ return 1.0
37
+ """
38
+ verifier = sync_verifier_from_string(
39
+ verifier_func=code,
40
+ verifier_id="test-verifier",
41
+ verifier_key="test-key",
42
+ sha256="test-sha",
43
+ )
44
+ assert verifier is not None
45
+ # The function name should be 'my_actual_verifier', not 'verifier'
46
+ assert verifier.func.__name__ == "my_actual_verifier"
47
+
48
+ def test_verifier_with_from_fleet_verifiers_import_verifier(self):
49
+ """Test bug case: 'from fleet.verifiers import verifier'."""
50
+ code = """
51
+ from fleet.verifiers import verifier
52
+
53
+ def check_something(env):
54
+ return 1.0
55
+ """
56
+ verifier = sync_verifier_from_string(
57
+ verifier_func=code,
58
+ verifier_id="test-verifier",
59
+ verifier_key="test-key",
60
+ sha256="test-sha",
61
+ )
62
+ assert verifier is not None
63
+ assert verifier.func.__name__ == "check_something"
64
+
65
+ def test_verifier_with_from_fleet_verifiers_verifier_import_verifier(self):
66
+ """Test bug case: 'from fleet.verifiers.verifier import verifier'."""
67
+ code = """
68
+ from fleet.verifiers.verifier import verifier
69
+
70
+ def validate_task(env):
71
+ return 0.5
72
+ """
73
+ verifier = sync_verifier_from_string(
74
+ verifier_func=code,
75
+ verifier_id="test-verifier",
76
+ verifier_key="test-key",
77
+ sha256="test-sha",
78
+ )
79
+ assert verifier is not None
80
+ assert verifier.func.__name__ == "validate_task"
81
+
82
+ def test_verifier_with_multiple_imports(self):
83
+ """Test verifier with multiple import statements."""
84
+ code = """
85
+ from fleet import verifier
86
+ from fleet.verifiers.db import IgnoreConfig
87
+ import json
88
+
89
+ def complex_verifier(env):
90
+ data = json.dumps({"status": "ok"})
91
+ return 1.0
92
+ """
93
+ verifier = sync_verifier_from_string(
94
+ verifier_func=code,
95
+ verifier_id="test-verifier",
96
+ verifier_key="test-key",
97
+ sha256="test-sha",
98
+ )
99
+ assert verifier is not None
100
+ assert verifier.func.__name__ == "complex_verifier"
101
+
102
+ def test_verifier_with_legitimate_imports(self):
103
+ """Test verifier with legitimate imports (not fleet-related)."""
104
+ code = """
105
+ import json
106
+ import os
107
+
108
+ def my_verifier(env):
109
+ return 1.0
110
+ """
111
+ verifier = sync_verifier_from_string(
112
+ verifier_func=code,
113
+ verifier_id="test-verifier",
114
+ verifier_key="test-key",
115
+ sha256="test-sha",
116
+ )
117
+ assert verifier is not None
118
+ assert verifier.func.__name__ == "my_verifier"
119
+
120
+ def test_verifier_using_task_scores(self):
121
+ """Test verifier that uses TASK_SUCCESSFUL_SCORE and TASK_FAILED_SCORE."""
122
+ code = """
123
+ def my_verifier(env):
124
+ if True:
125
+ return TASK_SUCCESSFUL_SCORE
126
+ return TASK_FAILED_SCORE
127
+ """
128
+ verifier = sync_verifier_from_string(
129
+ verifier_func=code,
130
+ verifier_id="test-verifier",
131
+ verifier_key="test-key",
132
+ sha256="test-sha",
133
+ )
134
+ assert verifier is not None
135
+ assert verifier.func.__name__ == "my_verifier"
136
+
137
+ def test_verifier_using_ignore_config(self):
138
+ """Test verifier that uses IgnoreConfig."""
139
+ code = """
140
+ def my_verifier(env):
141
+ config = IgnoreConfig()
142
+ return 1.0
143
+ """
144
+ verifier = sync_verifier_from_string(
145
+ verifier_func=code,
146
+ verifier_id="test-verifier",
147
+ verifier_key="test-key",
148
+ sha256="test-sha",
149
+ )
150
+ assert verifier is not None
151
+ assert verifier.func.__name__ == "my_verifier"
152
+
153
+ def test_no_function_defined_raises_error(self):
154
+ """Test that code with no function raises ValueError."""
155
+ code = """
156
+ x = 1
157
+ y = 2
158
+ """
159
+ with pytest.raises(ValueError, match="No function found in verifier code"):
160
+ sync_verifier_from_string(
161
+ verifier_func=code,
162
+ verifier_id="test-verifier",
163
+ verifier_key="test-key",
164
+ sha256="test-sha",
165
+ )
166
+
167
+ def test_only_imports_no_function_raises_error(self):
168
+ """Test that code with only imports and no function raises ValueError."""
169
+ code = """
170
+ from fleet import verifier
171
+ import json
172
+ """
173
+ with pytest.raises(ValueError, match="No function found in verifier code"):
174
+ sync_verifier_from_string(
175
+ verifier_func=code,
176
+ verifier_id="test-verifier",
177
+ verifier_key="test-key",
178
+ sha256="test-sha",
179
+ )
180
+
181
+ def test_verifier_with_multiple_functions(self):
182
+ """Test that first user-defined function is selected when multiple exist."""
183
+ code = """
184
+ def helper_function():
185
+ return "helper"
186
+
187
+ def my_verifier(env):
188
+ return 1.0
189
+ """
190
+ verifier = sync_verifier_from_string(
191
+ verifier_func=code,
192
+ verifier_id="test-verifier",
193
+ verifier_key="test-key",
194
+ sha256="test-sha",
195
+ )
196
+ assert verifier is not None
197
+ # Should pick the first function (order depends on dict iteration)
198
+ assert verifier.func.__name__ in ["helper_function", "my_verifier"]
199
+
200
+ def test_verifier_with_decorator_usage(self):
201
+ """Test verifier that would use @verifier decorator in normal usage."""
202
+ code = """
203
+ from fleet.verifiers.verifier import verifier
204
+
205
+ def actual_verifier_function(env, project_key: str = "TEST"):
206
+ # This is the function that should be selected
207
+ return 1.0
208
+ """
209
+ verifier = sync_verifier_from_string(
210
+ verifier_func=code,
211
+ verifier_id="test-verifier",
212
+ verifier_key="test-key",
213
+ sha256="test-sha",
214
+ )
215
+ assert verifier is not None
216
+ assert verifier.func.__name__ == "actual_verifier_function"
217
+
218
+ def test_verifier_metadata_stored_correctly(self):
219
+ """Test that verifier metadata is stored correctly."""
220
+ code = """
221
+ def my_verifier(env):
222
+ return 1.0
223
+ """
224
+ sha256_val = "abcd1234"
225
+ verifier = sync_verifier_from_string(
226
+ verifier_func=code,
227
+ verifier_id="test-verifier-id",
228
+ verifier_key="test-key",
229
+ sha256=sha256_val,
230
+ )
231
+ assert verifier.key == "test-key"
232
+ assert verifier.verifier_id == "test-verifier-id"
233
+ assert verifier._sha256 == sha256_val
234
+ assert verifier._verifier_code == code
235
+
236
+
237
+ class TestAsyncVerifierFromString:
238
+ """Tests for async version of verifier_from_string."""
239
+
240
+ def test_basic_async_verifier_without_imports(self):
241
+ """Test basic async verifier function without any imports."""
242
+ code = """
243
+ async def my_async_verifier(env):
244
+ return 1.0
245
+ """
246
+ verifier = async_verifier_from_string(
247
+ verifier_func=code,
248
+ verifier_id="test-verifier",
249
+ verifier_key="test-key",
250
+ sha256="test-sha",
251
+ )
252
+ assert verifier is not None
253
+
254
+ def test_async_verifier_with_from_fleet_import_verifier(self):
255
+ """Test async bug case: 'from fleet import verifier'."""
256
+ code = """
257
+ from fleet import verifier
258
+
259
+ async def my_actual_async_verifier(env):
260
+ return 1.0
261
+ """
262
+ verifier = async_verifier_from_string(
263
+ verifier_func=code,
264
+ verifier_id="test-verifier",
265
+ verifier_key="test-key",
266
+ sha256="test-sha",
267
+ )
268
+ assert verifier is not None
269
+ assert verifier.func.__name__ == "my_actual_async_verifier"
270
+
271
+ def test_async_verifier_with_multiple_imports(self):
272
+ """Test async verifier with multiple import statements."""
273
+ code = """
274
+ from fleet import verifier
275
+ from fleet.verifiers.db import IgnoreConfig
276
+ import asyncio
277
+
278
+ async def complex_async_verifier(env):
279
+ await asyncio.sleep(0)
280
+ return 1.0
281
+ """
282
+ verifier = async_verifier_from_string(
283
+ verifier_func=code,
284
+ verifier_id="test-verifier",
285
+ verifier_key="test-key",
286
+ sha256="test-sha",
287
+ )
288
+ assert verifier is not None
289
+ assert verifier.func.__name__ == "complex_async_verifier"
290
+
291
+ def test_sync_function_in_async_module(self):
292
+ """Test that sync functions also work in async module."""
293
+ code = """
294
+ from fleet import verifier
295
+
296
+ def sync_verifier_in_async_module(env):
297
+ return 1.0
298
+ """
299
+ verifier = async_verifier_from_string(
300
+ verifier_func=code,
301
+ verifier_id="test-verifier",
302
+ verifier_key="test-key",
303
+ sha256="test-sha",
304
+ )
305
+ assert verifier is not None
306
+ assert verifier.func.__name__ == "sync_verifier_in_async_module"
307
+
308
+ def test_async_no_function_defined_raises_error(self):
309
+ """Test that async code with no function raises ValueError."""
310
+ code = """
311
+ x = 1
312
+ y = 2
313
+ """
314
+ with pytest.raises(ValueError, match="No function found in verifier code"):
315
+ async_verifier_from_string(
316
+ verifier_func=code,
317
+ verifier_id="test-verifier",
318
+ verifier_key="test-key",
319
+ sha256="test-sha",
320
+ )
321
+
322
+ def test_async_only_imports_raises_error(self):
323
+ """Test that async code with only imports raises ValueError."""
324
+ code = """
325
+ from fleet import verifier
326
+ import asyncio
327
+ """
328
+ with pytest.raises(ValueError, match="No function found in verifier code"):
329
+ async_verifier_from_string(
330
+ verifier_func=code,
331
+ verifier_id="test-verifier",
332
+ verifier_key="test-key",
333
+ sha256="test-sha",
334
+ )
335
+
336
+
337
+ class TestRealWorldScenarios:
338
+ """Test real-world scenarios from the bug report."""
339
+
340
+ def test_original_bug_report_scenario(self):
341
+ """Test the exact scenario from the bug report."""
342
+ code = """from fleet import verifier
343
+ def blahblah():
344
+ return 1.0
345
+ """
346
+ verifier = sync_verifier_from_string(
347
+ verifier_func=code,
348
+ verifier_id="test-verifier",
349
+ verifier_key="test-key",
350
+ sha256="test-sha",
351
+ )
352
+ # Should select 'blahblah', not the imported 'verifier'
353
+ assert verifier.func.__name__ == "blahblah"
354
+
355
+ def test_example_verifier_pattern(self):
356
+ """Test a pattern similar to example_verifier.py."""
357
+ code = """import fleet
358
+ from fleet.verifiers.verifier import verifier
359
+ from fleet.verifiers.db import IgnoreConfig
360
+
361
+
362
+ def validate_finish_blue_green_deployment(
363
+ env, final_answer: str = None
364
+ ) -> int:
365
+ '''Validate that DEBT-722 and DEBT-720 are marked as Done'''
366
+ return 1.0
367
+ """
368
+ verifier = sync_verifier_from_string(
369
+ verifier_func=code,
370
+ verifier_id="test-verifier",
371
+ verifier_key="test-key",
372
+ sha256="test-sha",
373
+ )
374
+ assert verifier.func.__name__ == "validate_finish_blue_green_deployment"
375
+
376
+ def test_example_task_pattern_sync(self):
377
+ """Test a pattern similar to example_task.py sync verifier."""
378
+ code = """from fleet.verifiers.verifier import verifier
379
+ from fleet.verifiers.code import TASK_SUCCESSFUL_SCORE, TASK_FAILED_SCORE
380
+
381
+
382
+ def create_bug_issue_sync(
383
+ env, project_key: str = "SCRUM", issue_title: str = "Sample Bug"
384
+ ) -> float:
385
+ '''Synchronous verifier for remote execution.'''
386
+ try:
387
+ return TASK_SUCCESSFUL_SCORE
388
+ except Exception as e:
389
+ return TASK_FAILED_SCORE
390
+ """
391
+ verifier = sync_verifier_from_string(
392
+ verifier_func=code,
393
+ verifier_id="test-verifier",
394
+ verifier_key="test-key",
395
+ sha256="test-sha",
396
+ )
397
+ assert verifier.func.__name__ == "create_bug_issue_sync"
398
+
399
+ def test_example_task_pattern_async(self):
400
+ """Test a pattern similar to example_task.py async verifier."""
401
+ code = """from fleet.verifiers.verifier import verifier
402
+ from fleet.verifiers.code import TASK_SUCCESSFUL_SCORE, TASK_FAILED_SCORE
403
+
404
+
405
+ async def create_bug_issue_async(
406
+ env, project_key: str = "SCRUM", issue_title: str = "Sample Bug"
407
+ ) -> float:
408
+ '''Async verifier for local execution with async environments.'''
409
+ try:
410
+ return TASK_SUCCESSFUL_SCORE
411
+ except Exception as e:
412
+ return TASK_FAILED_SCORE
413
+ """
414
+ verifier = async_verifier_from_string(
415
+ verifier_func=code,
416
+ verifier_id="test-verifier",
417
+ verifier_key="test-key",
418
+ sha256="test-sha",
419
+ )
420
+ assert verifier.func.__name__ == "create_bug_issue_async"