dbos 0.5.0a5__py3-none-any.whl → 0.5.0a11__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/context.py CHANGED
@@ -8,13 +8,10 @@ from types import TracebackType
8
8
  from typing import TYPE_CHECKING, List, Literal, Optional, Type, TypedDict
9
9
 
10
10
  from opentelemetry.trace import Span, Status, StatusCode
11
-
12
- if TYPE_CHECKING:
13
- from .fastapi import Request
14
-
15
11
  from sqlalchemy.orm import Session
16
12
 
17
13
  from .logger import dbos_logger
14
+ from .request import Request
18
15
  from .tracer import dbos_tracer
19
16
 
20
17
 
dbos/dbos.py CHANGED
@@ -52,7 +52,7 @@ from .tracer import dbos_tracer
52
52
 
53
53
  if TYPE_CHECKING:
54
54
  from fastapi import FastAPI
55
- from .fastapi import Request
55
+ from .request import Request
56
56
 
57
57
  from sqlalchemy.orm import Session
58
58
 
@@ -72,7 +72,7 @@ from dbos.error import DBOSException, DBOSNonExistentWorkflowError
72
72
 
73
73
  from .application_database import ApplicationDatabase
74
74
  from .dbos_config import ConfigFile, load_config, set_env_vars
75
- from .logger import config_logger, dbos_logger, init_logger
75
+ from .logger import add_otlp_to_all_loggers, config_logger, dbos_logger, init_logger
76
76
  from .system_database import SystemDatabase
77
77
 
78
78
  # Most DBOS functions are just any callable F, so decorators / wrappers work on F
@@ -202,9 +202,9 @@ class DBOS:
202
202
 
203
203
  def __new__(
204
204
  cls: Type[DBOS],
205
- fastapi: Optional["FastAPI"] = None,
205
+ *,
206
206
  config: Optional[ConfigFile] = None,
207
- launch: bool = True,
207
+ fastapi: Optional["FastAPI"] = None,
208
208
  ) -> DBOS:
209
209
  global _dbos_global_instance
210
210
  global _dbos_global_registry
@@ -219,7 +219,7 @@ class DBOS:
219
219
  )
220
220
  config = _dbos_global_registry.config
221
221
  _dbos_global_instance = super().__new__(cls)
222
- _dbos_global_instance.__init__(fastapi=fastapi, config=config, launch=launch) # type: ignore
222
+ _dbos_global_instance.__init__(fastapi=fastapi, config=config) # type: ignore
223
223
  else:
224
224
  if (config is not None and _dbos_global_instance.config is not config) or (
225
225
  _dbos_global_instance.fastapi is not fastapi
@@ -240,9 +240,9 @@ class DBOS:
240
240
 
241
241
  def __init__(
242
242
  self,
243
- fastapi: Optional["FastAPI"] = None,
243
+ *,
244
244
  config: Optional[ConfigFile] = None,
245
- launch: bool = True,
245
+ fastapi: Optional["FastAPI"] = None,
246
246
  ) -> None:
247
247
  if hasattr(self, "_initialized") and self._initialized:
248
248
  return
@@ -269,8 +269,8 @@ class DBOS:
269
269
  from dbos.fastapi import setup_fastapi_middleware
270
270
 
271
271
  setup_fastapi_middleware(self.fastapi)
272
- self.fastapi.on_event("startup")(self.launch)
273
- launch = False
272
+ self.fastapi.on_event("startup")(self._launch)
273
+ self.fastapi.on_event("shutdown")(self._destroy)
274
274
 
275
275
  # Register send_stub as a workflow
276
276
  def send_temp_workflow(
@@ -283,8 +283,8 @@ class DBOS:
283
283
  set_temp_workflow_type(send_temp_workflow, "send")
284
284
  self._registry.register_wf_function(TEMP_SEND_WF_NAME, temp_send_wf)
285
285
 
286
- if launch:
287
- self.launch()
286
+ for handler in dbos_logger.handlers:
287
+ handler.flush()
288
288
 
289
289
  @property
290
290
  def executor(self) -> ThreadPoolExecutor:
@@ -316,7 +316,12 @@ class DBOS:
316
316
  rv: AdminServer = self._admin_server
317
317
  return rv
318
318
 
319
- def launch(self) -> None:
319
+ @classmethod
320
+ def launch(cls) -> None:
321
+ if _dbos_global_instance is not None:
322
+ _dbos_global_instance._launch()
323
+
324
+ def _launch(self) -> None:
320
325
  if self._launched:
321
326
  dbos_logger.warning(f"DBOS was already launched")
322
327
  return
@@ -342,9 +347,13 @@ class DBOS:
342
347
  self.executor.submit(func, *args, **kwargs)
343
348
  self._registry.pollers = []
344
349
 
345
- dbos_logger.info("DBOS initialized")
350
+ dbos_logger.info("DBOS launched")
351
+
352
+ # Flush handlers and add OTLP to all loggers if enabled
353
+ # to enable their export in DBOS Cloud
346
354
  for handler in dbos_logger.handlers:
347
355
  handler.flush()
356
+ add_otlp_to_all_loggers()
348
357
 
349
358
  def _destroy(self) -> None:
350
359
  self._initialized = False
@@ -650,7 +659,7 @@ class DBOS:
650
659
 
651
660
  @classproperty
652
661
  def request(cls) -> Optional["Request"]:
653
- """Return the FastAPI `Request`, if any, associated with the current context."""
662
+ """Return the HTTP `Request`, if any, associated with the current context."""
654
663
  ctx = assert_current_dbos_context()
655
664
  return ctx.request
656
665
 
dbos/fastapi.py CHANGED
@@ -11,6 +11,7 @@ from .context import (
11
11
  TracedAttributes,
12
12
  assert_current_dbos_context,
13
13
  )
14
+ from .request import Address, Request
14
15
 
15
16
  request_id_header = "x-request-id"
16
17
 
@@ -23,31 +24,17 @@ def get_or_generate_request_id(request: FastAPIRequest) -> str:
23
24
  return str(uuid.uuid4())
24
25
 
25
26
 
26
- class Request:
27
- """
28
- Serializable subset of the FastAPI Request object.
29
-
30
- Attributes:
31
- base_url(URL): Base of URL requested, as in application code
32
- client(Address): HTTP Client
33
- cookies(Dict[str, str]): HTTP Cookies
34
- headers(Headers): HTTP headers
35
- method(str): HTTP verb
36
- path_params(Dict[str,Any]): Parameters extracted from URL path sections
37
- query_params(QueryParams): URL query string parameters
38
- url(URL): Full URL accessed
39
-
40
- """
41
-
42
- def __init__(self, req: FastAPIRequest):
43
- self.headers = req.headers
44
- self.path_params = req.path_params
45
- self.query_params = req.query_params
46
- self.url = req.url
47
- self.base_url = req.base_url
48
- self.client = req.client
49
- self.cookies = req.cookies
50
- self.method = req.method
27
+ def make_request(request: FastAPIRequest) -> Request:
28
+ return Request(
29
+ headers=request.headers,
30
+ path_params=request.path_params,
31
+ query_params=request.query_params,
32
+ url=str(request.url),
33
+ base_url=str(request.base_url),
34
+ client=Address(*request.client) if request.client is not None else None,
35
+ cookies=request.cookies,
36
+ method=request.method,
37
+ )
51
38
 
52
39
 
53
40
  def setup_fastapi_middleware(app: FastAPI) -> None:
@@ -65,7 +52,7 @@ def setup_fastapi_middleware(app: FastAPI) -> None:
65
52
  }
66
53
  with EnterDBOSHandler(attributes):
67
54
  ctx = assert_current_dbos_context()
68
- ctx.request = Request(request)
55
+ ctx.request = make_request(request)
69
56
  workflow_id = request.headers.get("dbos-idempotency-key", "")
70
57
  with SetWorkflowID(workflow_id):
71
58
  response = await call_next(request)
dbos/logger.py CHANGED
@@ -12,6 +12,7 @@ if TYPE_CHECKING:
12
12
  from dbos.dbos_config import ConfigFile
13
13
 
14
14
  dbos_logger = logging.getLogger("dbos")
15
+ otlp_handler, otlp_transformer = None, None
15
16
 
16
17
 
17
18
  class DBOSLogTransformer(logging.Filter):
@@ -73,25 +74,28 @@ def config_logger(config: "ConfigFile") -> None:
73
74
  export_timeout_millis=5000,
74
75
  )
75
76
  )
77
+ global otlp_handler
76
78
  otlp_handler = LoggingHandler(logger_provider=log_provider)
77
79
 
78
80
  # Attach DBOS-specific attributes to all log entries.
81
+ global otlp_transformer
79
82
  otlp_transformer = DBOSLogTransformer()
80
83
 
81
- # Direct all logs to OTLP
82
- add_otlp_to_all_loggers(otlp_handler, otlp_transformer)
84
+ # Direct DBOS logs to OTLP
85
+ dbos_logger.addHandler(otlp_handler)
86
+ dbos_logger.addFilter(otlp_transformer)
83
87
 
84
88
 
85
- def add_otlp_to_all_loggers(
86
- otlp_handler: LoggingHandler, otlp_transformer: DBOSLogTransformer
87
- ) -> None:
88
- root = logging.root
89
+ def add_otlp_to_all_loggers() -> None:
90
+ if otlp_handler is not None and otlp_transformer is not None:
91
+ root = logging.root
89
92
 
90
- root.addHandler(otlp_handler)
91
- root.addFilter(otlp_transformer)
93
+ root.addHandler(otlp_handler)
94
+ root.addFilter(otlp_transformer)
92
95
 
93
- for logger_name in root.manager.loggerDict:
94
- logger = logging.getLogger(logger_name)
95
- if not logger.propagate:
96
- logger.addHandler(otlp_handler)
97
- logger.addFilter(otlp_transformer)
96
+ for logger_name in root.manager.loggerDict:
97
+ if logger_name != dbos_logger.name:
98
+ logger = logging.getLogger(logger_name)
99
+ if not logger.propagate:
100
+ logger.addHandler(otlp_handler)
101
+ logger.addFilter(otlp_transformer)
dbos/request.py ADDED
@@ -0,0 +1,32 @@
1
+ from dataclasses import dataclass
2
+ from typing import Any, Mapping, NamedTuple, Optional
3
+
4
+
5
+ class Address(NamedTuple):
6
+ hostname: str
7
+ port: int
8
+
9
+
10
+ @dataclass
11
+ class Request:
12
+ """
13
+ Serializable HTTP Request object.
14
+ Attributes:
15
+ base_url(str): Base of URL requested, as in application code
16
+ client(Optional[Address]): HTTP Client
17
+ cookies(Mapping[str, str]): HTTP Cookies
18
+ headers(Mapping[str, str]): HTTP headers
19
+ method(str): HTTP verb
20
+ path_params(Mapping[str, Any]): Parameters extracted from URL path sections
21
+ query_params(Mapping[str, str]): URL query string parameters
22
+ url(str): Full URL accessed
23
+ """
24
+
25
+ headers: Mapping[str, str]
26
+ path_params: Mapping[str, Any]
27
+ query_params: Mapping[str, str]
28
+ url: str
29
+ base_url: str
30
+ client: Optional[Address]
31
+ cookies: Mapping[str, str]
32
+ method: str
dbos/system_database.py CHANGED
@@ -734,7 +734,6 @@ class SystemDatabase:
734
734
  )
735
735
  notification_cursor = self.notification_conn.cursor()
736
736
 
737
- dbos_logger.info("Listening to notifications")
738
737
  notification_cursor.execute("LISTEN dbos_notifications_channel")
739
738
  notification_cursor.execute("LISTEN dbos_workflow_events_channel")
740
739
  while self._run_background_processes:
@@ -15,7 +15,7 @@ from dbos import DBOS
15
15
  from .schema import dbos_hello
16
16
 
17
17
  app = FastAPI()
18
- DBOS(app)
18
+ DBOS(fastapi=app)
19
19
 
20
20
  # Next, let's write a function that greets visitors.
21
21
  # To make it more interesting, we'll keep track of how
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dbos
3
- Version: 0.5.0a5
3
+ Version: 0.5.0a11
4
4
  Summary: A Python framework for backends that scale
5
5
  Author-Email: "DBOS, Inc." <contact@dbos.dev>
6
6
  License: MIT
@@ -1,20 +1,20 @@
1
- dbos-0.5.0a5.dist-info/METADATA,sha256=WRKFIucNawLOnizlrKNBp1cTcYW2BcuIcaYaQjXq1FU,5420
2
- dbos-0.5.0a5.dist-info/WHEEL,sha256=rSwsxJWe3vzyR5HCwjWXQruDgschpei4h_giTm0dJVE,90
3
- dbos-0.5.0a5.dist-info/entry_points.txt,sha256=3PmOPbM4FYxEmggRRdJw0oAsiBzKR8U0yx7bmwUmMOM,39
4
- dbos-0.5.0a5.dist-info/licenses/LICENSE,sha256=VGZit_a5-kdw9WT6fY5jxAWVwGQzgLFyPWrcVVUhVNU,1067
1
+ dbos-0.5.0a11.dist-info/METADATA,sha256=zWq1cPyLlyR1-qJrUhSwbURRvDUOI9LfxIg3M0kVFC0,5421
2
+ dbos-0.5.0a11.dist-info/WHEEL,sha256=rSwsxJWe3vzyR5HCwjWXQruDgschpei4h_giTm0dJVE,90
3
+ dbos-0.5.0a11.dist-info/entry_points.txt,sha256=3PmOPbM4FYxEmggRRdJw0oAsiBzKR8U0yx7bmwUmMOM,39
4
+ dbos-0.5.0a11.dist-info/licenses/LICENSE,sha256=VGZit_a5-kdw9WT6fY5jxAWVwGQzgLFyPWrcVVUhVNU,1067
5
5
  dbos/__init__.py,sha256=X1LdP36NomDtvPfFwoMNtgXf81TO05jj7vltsp79UUw,787
6
6
  dbos/admin_sever.py,sha256=KtzH6aKyskCm4h3yulpy9jb5PIqRlYI2sjctw5mvaKY,3395
7
7
  dbos/application_database.py,sha256=1K3kE96BgGi_QWOd2heXluyNTwFAwlUVuAR6JKKUqf0,5659
8
8
  dbos/cli.py,sha256=QnbGtZ8S963q3iyFvXNBcL4DB35r4SFMarlb5DRqN6M,7915
9
- dbos/context.py,sha256=JZMV2RtSpTK7lnyyWxeBmGPwrZSB00XZEP6R6MT9ygQ,15690
9
+ dbos/context.py,sha256=qAVj_pAIV4YBOAbI0WCv-Roq7aNwPzAoj3CeQaVqlrU,15666
10
10
  dbos/core.py,sha256=HfKnPpIaQqIBAHzP2hD67aSIchTHp87NgD21CcujKkE,28300
11
11
  dbos/dbos-config.schema.json,sha256=azpfmoDZg7WfSy3kvIsk9iEiKB_-VZt03VEOoXJAkqE,5331
12
- dbos/dbos.py,sha256=HngS2BUWSbWPmloXGr-KE81BQ8dpZtlvOXM4tx4_Qhg,26246
12
+ dbos/dbos.py,sha256=bDp-m25V_CkaR4hsr4VmCFCZbDQ4V99Um9bDUxahpag,26561
13
13
  dbos/dbos_config.py,sha256=EkO0c0xaIM7_vAAqqnvNNEAKG5fOJbmmalqnZvaKYZA,5312
14
14
  dbos/decorators.py,sha256=lbPefsLK6Cya4cb7TrOcLglOpGT3pc6qjZdsQKlfZLg,629
15
15
  dbos/error.py,sha256=nBdLC4hxGO_K9V26YbDGOo7xi1CKuN4PsE_cBv7K8Cc,3798
16
- dbos/fastapi.py,sha256=ZFcMizyv3pizo5zf0sSF6U4GoR3rQH8LxGipkQIGHfU,2282
17
- dbos/logger.py,sha256=cfybbu6F1zsgYLEPW8D8V6h033u-YedLXnGMnQQM6-4,3341
16
+ dbos/fastapi.py,sha256=LkARLITiN_NuQh4g2QL7sfK0oG1GvJjj2tvU7WWO8f8,1898
17
+ dbos/logger.py,sha256=D-aFSZUCHBP34J1IZ5YNkTrJW-rDiH3py_v9jLU4Yrk,3565
18
18
  dbos/migrations/env.py,sha256=38SIGVbmn_VV2x2u1aHLcPOoWgZ84eCymf3g_NljmbU,1626
19
19
  dbos/migrations/script.py.mako,sha256=MEqL-2qATlST9TAOeYgscMn1uy6HUS9NFvDgl93dMj8,635
20
20
  dbos/migrations/versions/5c361fc04708_added_system_tables.py,sha256=QMgFMb0aLgC25YicsvPSr6AHRCA6Zd66hyaRUhwKzrQ,6404
@@ -22,16 +22,17 @@ dbos/migrations/versions/a3b18ad34abe_added_triggers.py,sha256=Rv0ZsZYZ_WdgGEULY
22
22
  dbos/py.typed,sha256=QfzXT1Ktfk3Rj84akygc7_42z0lRpCq0Ilh8OXI6Zas,44
23
23
  dbos/recovery.py,sha256=xfwQFWNuD8DXg5HD5_-3tG7Neo9j-x1lrqiwtn5FSh8,2015
24
24
  dbos/registrations.py,sha256=gMI-u05tv5bpvyddQGtoUgCsqARx51aOY7p0JXPafQo,6539
25
+ dbos/request.py,sha256=FTjmgzqWwKKTSf6PKPdlQ4Ssp39PATQukYmMOW_xP7k,892
25
26
  dbos/roles.py,sha256=9u0z4CWmXPeqIKzQWEzaOKIlzOuaagBtMiB-swqjX_U,2291
26
27
  dbos/scheduler/croniter.py,sha256=hbhgfsHBqclUS8VeLnJ9PSE9Z54z6mi4nnrr1aUXn0k,47561
27
28
  dbos/scheduler/scheduler.py,sha256=uO4_9jmWW2rLv1ODL3lc1cE_37ZaVTgnvmFx_FAlN50,1472
28
29
  dbos/schemas/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
29
30
  dbos/schemas/application_database.py,sha256=q_Wr2XbiZNBYFkOtu7uKavo1T_cSOBblxKGHThYGGsY,962
30
31
  dbos/schemas/system_database.py,sha256=5V3vqnEzry0Hn7ZbVS9Gs_dJKia8uX8p7mGC82Ru8rk,4303
31
- dbos/system_database.py,sha256=SK24Avj10rbbWFilVUexdPX6VvOL8zC-CoWDhNQj6QM,39698
32
+ dbos/system_database.py,sha256=84c53iAel113SRb7DcgFJ8XQNWBhD4VrCRCb0s5Oe8Y,39635
32
33
  dbos/templates/hello/README.md,sha256=GhxhBj42wjTt1fWEtwNriHbJuKb66Vzu89G4pxNHw2g,930
33
34
  dbos/templates/hello/__package/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
34
- dbos/templates/hello/__package/main.py,sha256=hJgp3S14cseT7zWIZsPwjqdzwTCw1aLo8kPKsTvYz0Y,2976
35
+ dbos/templates/hello/__package/main.py,sha256=LFN48qBWt-xsf21Cg1llf5Grr0e7Il_7lcyi60sK4ec,2984
35
36
  dbos/templates/hello/__package/schema.py,sha256=XOSeq_vFG0vN1LxWPob-L9K65jq9OMCz2qOmvw5CKN8,235
36
37
  dbos/templates/hello/alembic.ini,sha256=VKBn4Gy8mMuCdY7Hip1jmo3wEUJ1VG1aW7EqY0_n-as,3695
37
38
  dbos/templates/hello/dbos-config.yaml.dbos,sha256=8wxCf_MIEFNWqMXj0nAHUwg1U3YaKz4xcUN6g51WkDE,603
@@ -42,4 +43,4 @@ dbos/templates/hello/start_postgres_docker.py,sha256=lQVLlYO5YkhGPEgPqwGc7Y8uDKs
42
43
  dbos/tracer.py,sha256=RPW9oxmX9tSc0Yq7O-FAhpQWBg1QT7Ni1Q06uwhtNDk,2237
43
44
  dbos/utils.py,sha256=hWj9iWDrby2cVEhb0pG-IdnrxLqP64NhkaWUXiLc8bA,402
44
45
  version/__init__.py,sha256=L4sNxecRuqdtSFdpUGX3TtBi9KL3k7YsZVIvv-fv9-A,1678
45
- dbos-0.5.0a5.dist-info/RECORD,,
46
+ dbos-0.5.0a11.dist-info/RECORD,,