fleet-python 0.2.97__tar.gz → 0.2.98__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.
Files changed (118) hide show
  1. {fleet_python-0.2.97/fleet_python.egg-info → fleet_python-0.2.98}/PKG-INFO +1 -1
  2. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/__init__.py +1 -1
  3. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/_async/__init__.py +1 -1
  4. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/_async/base.py +1 -1
  5. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/base.py +1 -1
  6. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/resources/sqlite.py +40 -29
  7. {fleet_python-0.2.97 → fleet_python-0.2.98/fleet_python.egg-info}/PKG-INFO +1 -1
  8. {fleet_python-0.2.97 → fleet_python-0.2.98}/pyproject.toml +1 -1
  9. {fleet_python-0.2.97 → fleet_python-0.2.98}/LICENSE +0 -0
  10. {fleet_python-0.2.97 → fleet_python-0.2.98}/README.md +0 -0
  11. {fleet_python-0.2.97 → fleet_python-0.2.98}/examples/diff_example.py +0 -0
  12. {fleet_python-0.2.97 → fleet_python-0.2.98}/examples/dsl_example.py +0 -0
  13. {fleet_python-0.2.97 → fleet_python-0.2.98}/examples/example.py +0 -0
  14. {fleet_python-0.2.97 → fleet_python-0.2.98}/examples/exampleResume.py +0 -0
  15. {fleet_python-0.2.97 → fleet_python-0.2.98}/examples/example_account.py +0 -0
  16. {fleet_python-0.2.97 → fleet_python-0.2.98}/examples/example_action_log.py +0 -0
  17. {fleet_python-0.2.97 → fleet_python-0.2.98}/examples/example_client.py +0 -0
  18. {fleet_python-0.2.97 → fleet_python-0.2.98}/examples/example_mcp_anthropic.py +0 -0
  19. {fleet_python-0.2.97 → fleet_python-0.2.98}/examples/example_mcp_openai.py +0 -0
  20. {fleet_python-0.2.97 → fleet_python-0.2.98}/examples/example_sync.py +0 -0
  21. {fleet_python-0.2.97 → fleet_python-0.2.98}/examples/example_task.py +0 -0
  22. {fleet_python-0.2.97 → fleet_python-0.2.98}/examples/example_tasks.py +0 -0
  23. {fleet_python-0.2.97 → fleet_python-0.2.98}/examples/example_verifier.py +0 -0
  24. {fleet_python-0.2.97 → fleet_python-0.2.98}/examples/export_tasks.py +0 -0
  25. {fleet_python-0.2.97 → fleet_python-0.2.98}/examples/fetch_tasks.py +0 -0
  26. {fleet_python-0.2.97 → fleet_python-0.2.98}/examples/gemini_example.py +0 -0
  27. {fleet_python-0.2.97 → fleet_python-0.2.98}/examples/import_tasks.py +0 -0
  28. {fleet_python-0.2.97 → fleet_python-0.2.98}/examples/iterate_verifiers.py +0 -0
  29. {fleet_python-0.2.97 → fleet_python-0.2.98}/examples/json_tasks_example.py +0 -0
  30. {fleet_python-0.2.97 → fleet_python-0.2.98}/examples/nova_act_example.py +0 -0
  31. {fleet_python-0.2.97 → fleet_python-0.2.98}/examples/openai_example.py +0 -0
  32. {fleet_python-0.2.97 → fleet_python-0.2.98}/examples/openai_simple_example.py +0 -0
  33. {fleet_python-0.2.97 → fleet_python-0.2.98}/examples/query_builder_example.py +0 -0
  34. {fleet_python-0.2.97 → fleet_python-0.2.98}/examples/quickstart.py +0 -0
  35. {fleet_python-0.2.97 → fleet_python-0.2.98}/examples/test_cdp_logging.py +0 -0
  36. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/_async/client.py +0 -0
  37. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/_async/env/__init__.py +0 -0
  38. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/_async/env/client.py +0 -0
  39. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/_async/exceptions.py +0 -0
  40. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/_async/global_client.py +0 -0
  41. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/_async/instance/__init__.py +0 -0
  42. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/_async/instance/base.py +0 -0
  43. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/_async/instance/client.py +0 -0
  44. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/_async/models.py +0 -0
  45. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/_async/resources/__init__.py +0 -0
  46. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/_async/resources/api.py +0 -0
  47. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/_async/resources/base.py +0 -0
  48. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/_async/resources/browser.py +0 -0
  49. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/_async/resources/mcp.py +0 -0
  50. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/_async/resources/sqlite.py +0 -0
  51. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/_async/tasks.py +0 -0
  52. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/_async/verifiers/__init__.py +0 -0
  53. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/_async/verifiers/bundler.py +0 -0
  54. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/_async/verifiers/verifier.py +0 -0
  55. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/agent/__init__.py +0 -0
  56. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/agent/gemini_cua/Dockerfile +0 -0
  57. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/agent/gemini_cua/__init__.py +0 -0
  58. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/agent/gemini_cua/agent.py +0 -0
  59. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/agent/gemini_cua/mcp/main.py +0 -0
  60. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/agent/gemini_cua/mcp_server/__init__.py +0 -0
  61. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/agent/gemini_cua/mcp_server/main.py +0 -0
  62. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/agent/gemini_cua/mcp_server/tools.py +0 -0
  63. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/agent/gemini_cua/requirements.txt +0 -0
  64. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/agent/gemini_cua/start.sh +0 -0
  65. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/agent/orchestrator.py +0 -0
  66. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/agent/types.py +0 -0
  67. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/agent/utils.py +0 -0
  68. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/cli.py +0 -0
  69. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/client.py +0 -0
  70. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/config.py +0 -0
  71. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/env/__init__.py +0 -0
  72. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/env/client.py +0 -0
  73. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/eval/__init__.py +0 -0
  74. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/eval/uploader.py +0 -0
  75. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/exceptions.py +0 -0
  76. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/global_client.py +0 -0
  77. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/instance/__init__.py +0 -0
  78. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/instance/base.py +0 -0
  79. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/instance/client.py +0 -0
  80. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/instance/models.py +0 -0
  81. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/models.py +0 -0
  82. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/proxy/__init__.py +0 -0
  83. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/proxy/proxy.py +0 -0
  84. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/proxy/whitelist.py +0 -0
  85. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/resources/__init__.py +0 -0
  86. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/resources/api.py +0 -0
  87. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/resources/base.py +0 -0
  88. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/resources/browser.py +0 -0
  89. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/resources/mcp.py +0 -0
  90. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/tasks.py +0 -0
  91. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/types.py +0 -0
  92. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/utils/__init__.py +0 -0
  93. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/utils/http_logging.py +0 -0
  94. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/utils/logging.py +0 -0
  95. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/utils/playwright.py +0 -0
  96. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/verifiers/__init__.py +0 -0
  97. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/verifiers/bundler.py +0 -0
  98. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/verifiers/code.py +0 -0
  99. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/verifiers/db.py +0 -0
  100. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/verifiers/decorator.py +0 -0
  101. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/verifiers/parse.py +0 -0
  102. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/verifiers/sql_differ.py +0 -0
  103. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet/verifiers/verifier.py +0 -0
  104. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet_python.egg-info/SOURCES.txt +0 -0
  105. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet_python.egg-info/dependency_links.txt +0 -0
  106. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet_python.egg-info/entry_points.txt +0 -0
  107. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet_python.egg-info/requires.txt +0 -0
  108. {fleet_python-0.2.97 → fleet_python-0.2.98}/fleet_python.egg-info/top_level.txt +0 -0
  109. {fleet_python-0.2.97 → fleet_python-0.2.98}/scripts/fix_sync_imports.py +0 -0
  110. {fleet_python-0.2.97 → fleet_python-0.2.98}/scripts/unasync.py +0 -0
  111. {fleet_python-0.2.97 → fleet_python-0.2.98}/setup.cfg +0 -0
  112. {fleet_python-0.2.97 → fleet_python-0.2.98}/tests/__init__.py +0 -0
  113. {fleet_python-0.2.97 → fleet_python-0.2.98}/tests/test_app_method.py +0 -0
  114. {fleet_python-0.2.97 → fleet_python-0.2.98}/tests/test_expect_only.py +0 -0
  115. {fleet_python-0.2.97 → fleet_python-0.2.98}/tests/test_instance_dispatch.py +0 -0
  116. {fleet_python-0.2.97 → fleet_python-0.2.98}/tests/test_sqlite_resource_dual_mode.py +0 -0
  117. {fleet_python-0.2.97 → fleet_python-0.2.98}/tests/test_sqlite_shared_memory_behavior.py +0 -0
  118. {fleet_python-0.2.97 → fleet_python-0.2.98}/tests/test_verifier_from_string.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fleet-python
3
- Version: 0.2.97
3
+ Version: 0.2.98
4
4
  Summary: Python SDK for Fleet environments
5
5
  Author-email: Fleet AI <nic@fleet.so>
6
6
  License: Apache-2.0
@@ -73,7 +73,7 @@ from . import env
73
73
  from . import global_client as _global_client
74
74
  from ._async import global_client as _async_global_client
75
75
 
76
- __version__ = "0.2.96"
76
+ __version__ = "0.2.98"
77
77
 
78
78
  __all__ = [
79
79
  # Core classes
@@ -44,7 +44,7 @@ from ..types import VerifierFunction
44
44
  from .. import env
45
45
  from . import global_client as _async_global_client
46
46
 
47
- __version__ = "0.2.96"
47
+ __version__ = "0.2.98"
48
48
 
49
49
  __all__ = [
50
50
  # Core classes
@@ -26,7 +26,7 @@ from .exceptions import (
26
26
  try:
27
27
  from .. import __version__
28
28
  except ImportError:
29
- __version__ = "0.2.96"
29
+ __version__ = "0.2.98"
30
30
 
31
31
  logger = logging.getLogger(__name__)
32
32
 
@@ -27,7 +27,7 @@ from .exceptions import (
27
27
  try:
28
28
  from . import __version__
29
29
  except ImportError:
30
- __version__ = "0.2.96"
30
+ __version__ = "0.2.98"
31
31
 
32
32
  logger = logging.getLogger(__name__)
33
33
 
@@ -22,6 +22,17 @@ from fleet.verifiers.db import (
22
22
  )
23
23
 
24
24
 
25
+ def _quote_identifier(identifier: str) -> str:
26
+ """Quote an identifier (table or column name) for SQLite.
27
+
28
+ SQLite uses double quotes for identifiers and escapes internal quotes by doubling them.
29
+ This handles reserved keywords like 'order', 'table', etc.
30
+ """
31
+ # Escape any double quotes in the identifier by doubling them
32
+ escaped = identifier.replace('"', '""')
33
+ return f'"{escaped}"'
34
+
35
+
25
36
  class SyncDatabaseSnapshot:
26
37
  """Lazy database snapshot that fetches data on-demand through API."""
27
38
 
@@ -56,12 +67,12 @@ class SyncDatabaseSnapshot:
56
67
  return
57
68
 
58
69
  # Get table schema
59
- schema_response = self.resource.query(f"PRAGMA table_info({table})")
70
+ schema_response = self.resource.query(f"PRAGMA table_info({_quote_identifier(table)})")
60
71
  if schema_response.rows:
61
72
  self._schemas[table] = [row[1] for row in schema_response.rows] # Column names
62
73
 
63
74
  # Get all data for this table
64
- data_response = self.resource.query(f"SELECT * FROM {table}")
75
+ data_response = self.resource.query(f"SELECT * FROM {_quote_identifier(table)}")
65
76
  if data_response.rows and data_response.columns:
66
77
  self._data[table] = [
67
78
  dict(zip(data_response.columns, row)) for row in data_response.rows
@@ -122,23 +133,23 @@ class SyncSnapshotQueryBuilder:
122
133
  where_parts = []
123
134
  for col, op, val in self._conditions:
124
135
  if op == "=" and val is None:
125
- where_parts.append(f"{col} IS NULL")
136
+ where_parts.append(f"{_quote_identifier(col)} IS NULL")
126
137
  elif op == "IS":
127
- where_parts.append(f"{col} IS NULL")
138
+ where_parts.append(f"{_quote_identifier(col)} IS NULL")
128
139
  elif op == "IS NOT":
129
- where_parts.append(f"{col} IS NOT NULL")
140
+ where_parts.append(f"{_quote_identifier(col)} IS NOT NULL")
130
141
  elif op == "=":
131
142
  if isinstance(val, str):
132
143
  escaped_val = val.replace("'", "''")
133
- where_parts.append(f"{col} = '{escaped_val}'")
144
+ where_parts.append(f"{_quote_identifier(col)} = '{escaped_val}'")
134
145
  else:
135
- where_parts.append(f"{col} = '{val}'")
146
+ where_parts.append(f"{_quote_identifier(col)} = '{val}'")
136
147
 
137
148
  where_clause = " AND ".join(where_parts)
138
149
 
139
150
  # Build full query
140
151
  cols = ", ".join(self._select_cols)
141
- query = f"SELECT {cols} FROM {self._table} WHERE {where_clause}"
152
+ query = f"SELECT {cols} FROM {_quote_identifier(self._table)} WHERE {where_clause}"
142
153
 
143
154
  if self._order_by:
144
155
  query += f" ORDER BY {self._order_by}"
@@ -270,7 +281,7 @@ class SyncSnapshotDiff:
270
281
  def _get_primary_key_columns(self, table: str) -> List[str]:
271
282
  """Get primary key columns for a table."""
272
283
  # Try to get from schema
273
- schema_response = self.after.resource.query(f"PRAGMA table_info({table})")
284
+ schema_response = self.after.resource.query(f"PRAGMA table_info({_quote_identifier(table)})")
274
285
  if not schema_response.rows:
275
286
  return ["id"] # Default fallback
276
287
 
@@ -409,18 +420,18 @@ class SyncSnapshotDiff:
409
420
  return f"'{val}'"
410
421
 
411
422
  if len(pk_columns) == 1:
412
- return f"{pk_columns[0]} = {escape_value(pk_value)}"
423
+ return f"{_quote_identifier(pk_columns[0])} = {escape_value(pk_value)}"
413
424
  else:
414
425
  # Composite key
415
426
  if isinstance(pk_value, tuple):
416
427
  conditions = [
417
- f"{col} = {escape_value(val)}"
428
+ f"{_quote_identifier(col)} = {escape_value(val)}"
418
429
  for col, val in zip(pk_columns, pk_value)
419
430
  ]
420
431
  return " AND ".join(conditions)
421
432
  else:
422
433
  # Shouldn't happen if data is consistent
423
- return f"{pk_columns[0]} = {escape_value(pk_value)}"
434
+ return f"{_quote_identifier(pk_columns[0])} = {escape_value(pk_value)}"
424
435
 
425
436
  def _expect_no_changes(self):
426
437
  """Efficiently verify that no changes occurred between snapshots using row counts."""
@@ -470,7 +481,7 @@ class SyncSnapshotDiff:
470
481
 
471
482
  if table in before_tables:
472
483
  before_count_response = self.before.resource.query(
473
- f"SELECT COUNT(*) FROM {table}"
484
+ f"SELECT COUNT(*) FROM {_quote_identifier(table)}"
474
485
  )
475
486
  before_count = (
476
487
  before_count_response.rows[0][0]
@@ -480,7 +491,7 @@ class SyncSnapshotDiff:
480
491
 
481
492
  if table in after_tables:
482
493
  after_count_response = self.after.resource.query(
483
- f"SELECT COUNT(*) FROM {table}"
494
+ f"SELECT COUNT(*) FROM {_quote_identifier(table)}"
484
495
  )
485
496
  after_count = (
486
497
  after_count_response.rows[0][0]
@@ -559,10 +570,10 @@ class SyncSnapshotDiff:
559
570
  order_by = ", ".join(pk_columns) if pk_columns else "rowid"
560
571
 
561
572
  before_response = self.before.resource.query(
562
- f"SELECT * FROM {table} ORDER BY {order_by}"
573
+ f"SELECT * FROM {_quote_identifier(table)} ORDER BY {order_by}"
563
574
  )
564
575
  after_response = self.after.resource.query(
565
- f"SELECT * FROM {table} ORDER BY {order_by}"
576
+ f"SELECT * FROM {_quote_identifier(table)} ORDER BY {order_by}"
566
577
  )
567
578
 
568
579
  # Quick check: if column counts differ, there's a schema change
@@ -646,7 +657,7 @@ class SyncSnapshotDiff:
646
657
  where_sql = self._build_pk_where_clause(pk_columns, pk)
647
658
 
648
659
  # Query before snapshot
649
- before_query = f"SELECT * FROM {table} WHERE {where_sql}"
660
+ before_query = f"SELECT * FROM {_quote_identifier(table)} WHERE {where_sql}"
650
661
  before_response = self.before.resource.query(before_query)
651
662
  before_row = (
652
663
  dict(zip(before_response.columns, before_response.rows[0]))
@@ -743,7 +754,7 @@ class SyncSnapshotDiff:
743
754
  try:
744
755
  # For tables with no allowed changes, just check row counts
745
756
  before_count_response = self.before.resource.query(
746
- f"SELECT COUNT(*) FROM {table}"
757
+ f"SELECT COUNT(*) FROM {_quote_identifier(table)}"
747
758
  )
748
759
  before_count = (
749
760
  before_count_response.rows[0][0]
@@ -752,7 +763,7 @@ class SyncSnapshotDiff:
752
763
  )
753
764
 
754
765
  after_count_response = self.after.resource.query(
755
- f"SELECT COUNT(*) FROM {table}"
766
+ f"SELECT COUNT(*) FROM {_quote_identifier(table)}"
756
767
  )
757
768
  after_count = (
758
769
  after_count_response.rows[0][0] if after_count_response.rows else 0
@@ -1132,7 +1143,7 @@ class SyncSnapshotDiff:
1132
1143
  where_sql = self._build_pk_where_clause(pk_columns, pk)
1133
1144
 
1134
1145
  # Query before snapshot
1135
- before_query = f"SELECT * FROM {table} WHERE {where_sql}"
1146
+ before_query = f"SELECT * FROM {_quote_identifier(table)} WHERE {where_sql}"
1136
1147
  before_response = self.before.resource.query(before_query)
1137
1148
  before_row = (
1138
1149
  dict(zip(before_response.columns, before_response.rows[0]))
@@ -1219,7 +1230,7 @@ class SyncSnapshotDiff:
1219
1230
  try:
1220
1231
  # For tables with no allowed changes, just check row counts
1221
1232
  before_count_response = self.before.resource.query(
1222
- f"SELECT COUNT(*) FROM {table}"
1233
+ f"SELECT COUNT(*) FROM {_quote_identifier(table)}"
1223
1234
  )
1224
1235
  before_count = (
1225
1236
  before_count_response.rows[0][0]
@@ -1228,7 +1239,7 @@ class SyncSnapshotDiff:
1228
1239
  )
1229
1240
 
1230
1241
  after_count_response = self.after.resource.query(
1231
- f"SELECT COUNT(*) FROM {table}"
1242
+ f"SELECT COUNT(*) FROM {_quote_identifier(table)}"
1232
1243
  )
1233
1244
  after_count = (
1234
1245
  after_count_response.rows[0][0] if after_count_response.rows else 0
@@ -1996,13 +2007,13 @@ class SyncQueryBuilder:
1996
2007
  # Compile to SQL
1997
2008
  def _compile(self) -> Tuple[str, List[Any]]:
1998
2009
  cols = ", ".join(self._select_cols)
1999
- sql = [f"SELECT {cols} FROM {self._table}"]
2010
+ sql = [f"SELECT {cols} FROM {_quote_identifier(self._table)}"]
2000
2011
  params: List[Any] = []
2001
2012
 
2002
2013
  # Joins
2003
2014
  for tbl, onmap in self._joins:
2004
- join_clauses = [f"{self._table}.{l} = {tbl}.{r}" for l, r in onmap.items()]
2005
- sql.append(f"JOIN {tbl} ON {' AND '.join(join_clauses)}")
2015
+ join_clauses = [f"{_quote_identifier(self._table)}.{_quote_identifier(l)} = {_quote_identifier(tbl)}.{_quote_identifier(r)}" for l, r in onmap.items()]
2016
+ sql.append(f"JOIN {_quote_identifier(tbl)} ON {' AND '.join(join_clauses)}")
2006
2017
 
2007
2018
  # WHERE
2008
2019
  if self._conditions:
@@ -2010,12 +2021,12 @@ class SyncQueryBuilder:
2010
2021
  for col, op, val in self._conditions:
2011
2022
  if op in ("IN", "NOT IN") and isinstance(val, tuple):
2012
2023
  ph = ", ".join(["?" for _ in val])
2013
- placeholders.append(f"{col} {op} ({ph})")
2024
+ placeholders.append(f"{_quote_identifier(col)} {op} ({ph})")
2014
2025
  params.extend(val)
2015
2026
  elif op in ("IS", "IS NOT"):
2016
- placeholders.append(f"{col} {op} NULL")
2027
+ placeholders.append(f"{_quote_identifier(col)} {op} NULL")
2017
2028
  else:
2018
- placeholders.append(f"{col} {op} ?")
2029
+ placeholders.append(f"{_quote_identifier(col)} {op} ?")
2019
2030
  params.append(val)
2020
2031
  sql.append("WHERE " + " AND ".join(placeholders))
2021
2032
 
@@ -2167,7 +2178,7 @@ class SQLiteResource(Resource):
2167
2178
  tables = []
2168
2179
  for table_name in table_names:
2169
2180
  # Get table info
2170
- cursor.execute(f"PRAGMA table_info({table_name})")
2181
+ cursor.execute(f"PRAGMA table_info({_quote_identifier(table_name)})")
2171
2182
  columns = cursor.fetchall()
2172
2183
 
2173
2184
  # Get CREATE TABLE SQL
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fleet-python
3
- Version: 0.2.97
3
+ Version: 0.2.98
4
4
  Summary: Python SDK for Fleet environments
5
5
  Author-email: Fleet AI <nic@fleet.so>
6
6
  License: Apache-2.0
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
5
5
  [project]
6
6
  name = "fleet-python"
7
7
 
8
- version = "0.2.97"
8
+ version = "0.2.98"
9
9
  description = "Python SDK for Fleet environments"
10
10
  authors = [
11
11
  {name = "Fleet AI", email = "nic@fleet.so"},
File without changes
File without changes
File without changes