dbos 0.26.0a5__py3-none-any.whl → 0.26.0a7__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.
dbos/_app_db.py CHANGED
@@ -1,13 +1,16 @@
1
- from typing import Optional, TypedDict
1
+ from typing import List, Optional, TypedDict
2
2
 
3
3
  import sqlalchemy as sa
4
4
  import sqlalchemy.dialects.postgresql as pg
5
+ from sqlalchemy import inspect, text
5
6
  from sqlalchemy.exc import DBAPIError
6
7
  from sqlalchemy.orm import Session, sessionmaker
7
8
 
9
+ from . import _serialization
8
10
  from ._dbos_config import ConfigFile, DatabaseConfig
9
11
  from ._error import DBOSWorkflowConflictIDError
10
12
  from ._schemas.application_database import ApplicationSchema
13
+ from ._sys_db import StepInfo
11
14
 
12
15
 
13
16
  class TransactionResultInternal(TypedDict):
@@ -18,6 +21,7 @@ class TransactionResultInternal(TypedDict):
18
21
  txn_id: Optional[str]
19
22
  txn_snapshot: str
20
23
  executor_id: Optional[str]
24
+ function_name: Optional[str]
21
25
 
22
26
 
23
27
  class RecordedResult(TypedDict):
@@ -87,7 +91,30 @@ class ApplicationDatabase:
87
91
  f"CREATE SCHEMA IF NOT EXISTS {ApplicationSchema.schema}"
88
92
  )
89
93
  conn.execute(schema_creation_query)
90
- ApplicationSchema.metadata_obj.create_all(self.engine)
94
+
95
+ inspector = inspect(self.engine)
96
+ if not inspector.has_table(
97
+ "transaction_outputs", schema=ApplicationSchema.schema
98
+ ):
99
+ ApplicationSchema.metadata_obj.create_all(self.engine)
100
+ else:
101
+ columns = inspector.get_columns(
102
+ "transaction_outputs", schema=ApplicationSchema.schema
103
+ )
104
+ column_names = [col["name"] for col in columns]
105
+
106
+ if "function_name" not in column_names:
107
+ # Column missing, alter table to add it
108
+ with self.engine.connect() as conn:
109
+ conn.execute(
110
+ text(
111
+ f"""
112
+ ALTER TABLE {ApplicationSchema.schema}.transaction_outputs
113
+ ADD COLUMN function_name TEXT NOT NULL DEFAULT '';
114
+ """
115
+ )
116
+ )
117
+ conn.commit()
91
118
 
92
119
  def destroy(self) -> None:
93
120
  self.engine.dispose()
@@ -108,6 +135,7 @@ class ApplicationDatabase:
108
135
  executor_id=(
109
136
  output["executor_id"] if output["executor_id"] else None
110
137
  ),
138
+ function_name=output["function_name"],
111
139
  )
112
140
  )
113
141
  except DBAPIError as dbapi_error:
@@ -133,6 +161,7 @@ class ApplicationDatabase:
133
161
  executor_id=(
134
162
  output["executor_id"] if output["executor_id"] else None
135
163
  ),
164
+ function_name=output["function_name"],
136
165
  )
137
166
  )
138
167
  except DBAPIError as dbapi_error:
@@ -160,3 +189,33 @@ class ApplicationDatabase:
160
189
  "error": rows[0][1],
161
190
  }
162
191
  return result
192
+
193
+ def get_transactions(self, workflow_uuid: str) -> List[StepInfo]:
194
+ with self.engine.begin() as conn:
195
+ rows = conn.execute(
196
+ sa.select(
197
+ ApplicationSchema.transaction_outputs.c.function_id,
198
+ ApplicationSchema.transaction_outputs.c.function_name,
199
+ ApplicationSchema.transaction_outputs.c.output,
200
+ ApplicationSchema.transaction_outputs.c.error,
201
+ ).where(
202
+ ApplicationSchema.transaction_outputs.c.workflow_uuid
203
+ == workflow_uuid,
204
+ )
205
+ ).all()
206
+ return [
207
+ StepInfo(
208
+ function_id=row[0],
209
+ function_name=row[1],
210
+ output=(
211
+ _serialization.deserialize(row[2]) if row[2] is not None else row[2]
212
+ ),
213
+ error=(
214
+ _serialization.deserialize_exception(row[3])
215
+ if row[3] is not None
216
+ else row[3]
217
+ ),
218
+ child_workflow_id=None,
219
+ )
220
+ for row in rows
221
+ ]
@@ -254,6 +254,7 @@ class ConductorWebsocket(threading.Thread):
254
254
  try:
255
255
  step_info = list_workflow_steps(
256
256
  self.dbos._sys_db,
257
+ self.dbos._app_db,
257
258
  list_steps_message.workflow_id,
258
259
  )
259
260
  except Exception as e:
dbos/_core.py CHANGED
@@ -782,6 +782,9 @@ def decorate_transaction(
782
782
  dbosreg: "DBOSRegistry", isolation_level: "IsolationLevel" = "SERIALIZABLE"
783
783
  ) -> Callable[[F], F]:
784
784
  def decorator(func: F) -> F:
785
+
786
+ transactionName = func.__qualname__
787
+
785
788
  def invoke_tx(*args: Any, **kwargs: Any) -> Any:
786
789
  if dbosreg.dbos is None:
787
790
  raise DBOSException(
@@ -810,6 +813,7 @@ def decorate_transaction(
810
813
  "txn_snapshot": "", # TODO: add actual snapshot
811
814
  "executor_id": None,
812
815
  "txn_id": None,
816
+ "function_name": transactionName,
813
817
  }
814
818
  retry_wait_seconds = 0.001
815
819
  backoff_factor = 1.5
dbos/_dbos.py CHANGED
@@ -172,6 +172,12 @@ class DBOSRegistry:
172
172
  if name in self.function_type_map:
173
173
  if self.function_type_map[name] != functype:
174
174
  raise DBOSConflictingRegistrationError(name)
175
+ if name != TEMP_SEND_WF_NAME:
176
+ # Remove the `<temp>` prefix from the function name to avoid confusion
177
+ truncated_name = name.replace("<temp>.", "")
178
+ dbos_logger.warning(
179
+ f"Duplicate registration of function '{truncated_name}'. A function named '{truncated_name}' has already been registered with DBOS. All functions registered with DBOS must have unique names."
180
+ )
175
181
  self.function_type_map[name] = functype
176
182
  self.workflow_info_map[name] = wrapped_func
177
183
 
@@ -25,6 +25,7 @@ class ApplicationSchema:
25
25
  Column("txn_id", Text, nullable=True),
26
26
  Column("txn_snapshot", Text),
27
27
  Column("executor_id", Text, nullable=True),
28
+ Column("function_name", Text, nullable=False, server_default=""),
28
29
  Column(
29
30
  "created_at",
30
31
  BigInteger,
@@ -2,6 +2,7 @@ import json
2
2
  from typing import Any, List, Optional
3
3
 
4
4
  from . import _serialization
5
+ from ._app_db import ApplicationDatabase
5
6
  from ._sys_db import (
6
7
  GetQueuedWorkflowsInput,
7
8
  GetWorkflowsInput,
@@ -170,6 +171,11 @@ def get_workflow(
170
171
  return info
171
172
 
172
173
 
173
- def list_workflow_steps(sys_db: SystemDatabase, workflow_id: str) -> List[StepInfo]:
174
- output = sys_db.get_workflow_steps(workflow_id)
175
- return output
174
+ def list_workflow_steps(
175
+ sys_db: SystemDatabase, app_db: ApplicationDatabase, workflow_id: str
176
+ ) -> List[StepInfo]:
177
+ steps = sys_db.get_workflow_steps(workflow_id)
178
+ transactions = app_db.get_transactions(workflow_id)
179
+ merged_steps = steps + transactions
180
+ merged_steps.sort(key=lambda step: step["function_id"])
181
+ return merged_steps
dbos/cli/cli.py CHANGED
@@ -350,8 +350,11 @@ def steps(
350
350
  ) -> None:
351
351
  config = load_config(silent=True)
352
352
  sys_db = SystemDatabase(config["database"])
353
+ app_db = ApplicationDatabase(config["database"])
353
354
  print(
354
- jsonpickle.encode(list_workflow_steps(sys_db, workflow_id), unpicklable=False)
355
+ jsonpickle.encode(
356
+ list_workflow_steps(sys_db, app_db, workflow_id), unpicklable=False
357
+ )
355
358
  )
356
359
 
357
360
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dbos
3
- Version: 0.26.0a5
3
+ Version: 0.26.0a7
4
4
  Summary: Ultra-lightweight durable execution in Python
5
5
  Author-Email: "DBOS, Inc." <contact@dbos.dev>
6
6
  License: MIT
@@ -1,23 +1,23 @@
1
- dbos-0.26.0a5.dist-info/METADATA,sha256=2Nl_CWZemlvRczy9jLMeUe2qFV0GhvI-gOcs_Ydgydk,5553
2
- dbos-0.26.0a5.dist-info/WHEEL,sha256=tSfRZzRHthuv7vxpI4aehrdN9scLjk-dCJkPLzkHxGg,90
3
- dbos-0.26.0a5.dist-info/entry_points.txt,sha256=_QOQ3tVfEjtjBlr1jS4sHqHya9lI2aIEIWkz8dqYp14,58
4
- dbos-0.26.0a5.dist-info/licenses/LICENSE,sha256=VGZit_a5-kdw9WT6fY5jxAWVwGQzgLFyPWrcVVUhVNU,1067
1
+ dbos-0.26.0a7.dist-info/METADATA,sha256=PjY70KmfnbwPahIoEhiSsBEG6VLDOb972f59w0UWS3Q,5553
2
+ dbos-0.26.0a7.dist-info/WHEEL,sha256=tSfRZzRHthuv7vxpI4aehrdN9scLjk-dCJkPLzkHxGg,90
3
+ dbos-0.26.0a7.dist-info/entry_points.txt,sha256=_QOQ3tVfEjtjBlr1jS4sHqHya9lI2aIEIWkz8dqYp14,58
4
+ dbos-0.26.0a7.dist-info/licenses/LICENSE,sha256=VGZit_a5-kdw9WT6fY5jxAWVwGQzgLFyPWrcVVUhVNU,1067
5
5
  dbos/__init__.py,sha256=3NQfGlBiiUSM_v88STdVP3rNZvGkUL_9WbSotKb8Voo,873
6
6
  dbos/__main__.py,sha256=G7Exn-MhGrVJVDbgNlpzhfh8WMX_72t3_oJaFT9Lmt8,653
7
7
  dbos/_admin_server.py,sha256=vxPG_YJ6lYrkfPCSp42FiATVLBOij7Fm52Yngg5Z_tE,7027
8
- dbos/_app_db.py,sha256=R3sbh--84A4i-dTz8IXYmxO4b4s5VSwiPQvi_le52mg,6109
8
+ dbos/_app_db.py,sha256=XdjZgKJMezSVZQJkvxBNa9x4asLURl2O-QxdmLai7wA,8491
9
9
  dbos/_classproperty.py,sha256=f0X-_BySzn3yFDRKB2JpCbLYQ9tLwt1XftfshvY7CBs,626
10
10
  dbos/_client.py,sha256=fzW_Gagh-oyWyDYtREcQDBesoVl_LsEoMeJAsn5-C5s,7262
11
11
  dbos/_cloudutils/authentication.py,sha256=V0fCWQN9stCkhbuuxgPTGpvuQcDqfU3KAxPAh01vKW4,5007
12
12
  dbos/_cloudutils/cloudutils.py,sha256=YC7jGsIopT0KveLsqbRpQk2KlRBk-nIRC_UCgep4f3o,7797
13
13
  dbos/_cloudutils/databases.py,sha256=_shqaqSvhY4n2ScgQ8IP5PDZvzvcx3YBKV8fj-cxhSY,8543
14
- dbos/_conductor/conductor.py,sha256=7elKINsgl4s1Tg5DwrU-K7xQ5vQvmDAIfAvUgfwpGN0,16784
14
+ dbos/_conductor/conductor.py,sha256=PzUFCX_JXGHClTF-hqTLR0ssO4kXdet4ZwHhJtuevEM,16839
15
15
  dbos/_conductor/protocol.py,sha256=xN7pmooyF1pqbH1b6WhllU5718P7zSb_b0KCwA6bzcs,6716
16
16
  dbos/_context.py,sha256=I8sLkdKTTkZEz7wG-MjynaQB6XEF2bLXuwNksiauP7w,19430
17
- dbos/_core.py,sha256=9PM2LZcaMhqF628wMGEOLmZadqwo_ViuM6-1j36Qd6k,45645
17
+ dbos/_core.py,sha256=EA9X4lTTTlimN8oa_mFICtl6Ke2biCvPdHl6PABjgGI,45749
18
18
  dbos/_croniter.py,sha256=XHAyUyibs_59sJQfSNWkP7rqQY6_XrlfuuCxk4jYqek,47559
19
19
  dbos/_db_wizard.py,sha256=VnMa6OL87Lc-XPDD1RnXp8NjsJE8YgiQLj3wtWAXp-8,8252
20
- dbos/_dbos.py,sha256=9oZFA9ZbpMO76sPAa59xhTPik6ZaAEumhKZ7t9s44w0,45726
20
+ dbos/_dbos.py,sha256=je4LWR78S5uitxNzyf3k_x5KErEmXyu88TjqZnHtQXU,46183
21
21
  dbos/_dbos_config.py,sha256=rTn30Hgh-RzTxqHbnYh2pC3Ioo30eJV9K4YxhJd-Gj4,22718
22
22
  dbos/_debug.py,sha256=mmgvLkqlrljMBBow9wk01PPur9kUf2rI_11dTJXY4gw,1822
23
23
  dbos/_error.py,sha256=B6Y9XLS1f6yrawxB2uAEYFMxFwk9BHhdxPNddKco-Fw,5399
@@ -44,7 +44,7 @@ dbos/_request.py,sha256=cX1B3Atlh160phgS35gF1VEEV4pD126c9F3BDgBmxZU,929
44
44
  dbos/_roles.py,sha256=iOsgmIAf1XVzxs3gYWdGRe1B880YfOw5fpU7Jwx8_A8,2271
45
45
  dbos/_scheduler.py,sha256=SR1oRZRcVzYsj-JauV2LA8JtwTkt8mru7qf6H1AzQ1U,2027
46
46
  dbos/_schemas/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
47
- dbos/_schemas/application_database.py,sha256=KeyoPrF7hy_ODXV7QNike_VFSD74QBRfQ76D7QyE9HI,966
47
+ dbos/_schemas/application_database.py,sha256=SypAS9l9EsaBHFn9FR8jmnqt01M74d9AF1AMa4m2hhI,1040
48
48
  dbos/_schemas/system_database.py,sha256=W9eSpL7SZzQkxcEZ4W07BOcwkkDr35b9oCjUOgfHWek,5336
49
49
  dbos/_serialization.py,sha256=YCYv0qKAwAZ1djZisBC7khvKqG-5OcIv9t9EC5PFIog,1743
50
50
  dbos/_sys_db.py,sha256=VBYVyKqZrwlFbDJ5cFIkeS5WtDOKpkI3lWJbSd5rB2s,65362
@@ -60,11 +60,11 @@ dbos/_templates/dbos-db-starter/migrations/versions/2024_07_31_180642_init.py,sh
60
60
  dbos/_templates/dbos-db-starter/start_postgres_docker.py,sha256=lQVLlYO5YkhGPEgPqwGc7Y8uDKse9HsWv5fynJEFJHM,1681
61
61
  dbos/_tracer.py,sha256=dFDSFlta-rfA3-ahIRLYwnnoAOmlavdxAGllqwFgnCA,2440
62
62
  dbos/_utils.py,sha256=nFRUHzVjXG5AusF85AlYHikj63Tzi-kQm992ihsrAxA,201
63
- dbos/_workflow_commands.py,sha256=SYp2khc9RSf6tjllG9CqT1zjBQnFTFq33ePXpvmRwME,5892
63
+ dbos/_workflow_commands.py,sha256=w981c3rVvhbhYd6BBP268C0Q88ClmwBwnachBxfnRmU,6129
64
64
  dbos/cli/_github_init.py,sha256=Y_bDF9gfO2jB1id4FV5h1oIxEJRWyqVjhb7bNEa5nQ0,3224
65
65
  dbos/cli/_template_init.py,sha256=-WW3kbq0W_Tq4WbMqb1UGJG3xvJb3woEY5VspG95Srk,2857
66
- dbos/cli/cli.py,sha256=G55sZJxfmvUGvWr0hoIWwVZBy-fJdpCsTsZmuHT1CjA,16049
66
+ dbos/cli/cli.py,sha256=FnI5ZAo-kAic-ij5wBqNJ2EJiYoBK1Ot-tTMh1WcXEM,16132
67
67
  dbos/dbos-config.schema.json,sha256=4z2OXPfp7H0uNT1m5dKxjg31qbAfPyKkFXwHufuUMec,5910
68
68
  dbos/py.typed,sha256=QfzXT1Ktfk3Rj84akygc7_42z0lRpCq0Ilh8OXI6Zas,44
69
69
  version/__init__.py,sha256=L4sNxecRuqdtSFdpUGX3TtBi9KL3k7YsZVIvv-fv9-A,1678
70
- dbos-0.26.0a5.dist-info/RECORD,,
70
+ dbos-0.26.0a7.dist-info/RECORD,,