Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,15 +99,20 @@ from bruin import context
| Property | Type | Env Var | Description |
|----------|------|---------|-------------|
| `context.start_date` | `date \| None` | `BRUIN_START_DATE` | Pipeline run start date |
| `context.end_date` | `date \| None` | `BRUIN_END_DATE` | Pipeline run end date |
| `context.start_datetime` | `datetime \| None` | `BRUIN_START_DATETIME` | Start date with time |
| `context.start_timestamp` | `datetime \| None` | `BRUIN_START_TIMESTAMP` | Start timestamp with timezone |
| `context.end_date` | `date \| None` | `BRUIN_END_DATE` | Pipeline run end date |
| `context.end_datetime` | `datetime \| None` | `BRUIN_END_DATETIME` | End date with time |
| `context.end_timestamp` | `datetime \| None` | `BRUIN_END_TIMESTAMP` | End timestamp with timezone |
| `context.execution_date` | `date \| None` | `BRUIN_EXECUTION_DATE` | Execution date |
| `context.execution_datetime` | `datetime \| None` | `BRUIN_EXECUTION_DATETIME` | Execution date with time |
| `context.execution_timestamp` | `datetime \| None` | `BRUIN_EXECUTION_TIMESTAMP` | Execution timestamp with timezone |
| `context.run_id` | `str \| None` | `BRUIN_RUN_ID` | Unique run identifier |
| `context.pipeline` | `str \| None` | `BRUIN_PIPELINE` | Pipeline name |
| `context.asset_name` | `str \| None` | `BRUIN_ASSET` | Current asset name |
| `context.connection` | `str \| None` | `BRUIN_CONNECTION` | Asset's default connection |
| `context.is_full_refresh` | `bool` | `BRUIN_FULL_REFRESH` | `True` when `--full-refresh` flag is set |
| `context.commit_hash` | `str \| None` | `BRUIN_COMMIT_HASH` | Git commit hash of the pipeline's repository |
| `context.vars` | `dict` | `BRUIN_VARS` | Pipeline variables (types preserved from JSON Schema) |

All properties return `None` when the corresponding env var is missing (except `is_full_refresh` which returns `False`, and `vars` which returns `{}`).
Expand Down
32 changes: 25 additions & 7 deletions src/bruin/_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,7 @@ def _parse_datetime(env_var: str) -> "datetime.datetime | None":
try:
return datetime.datetime.fromisoformat(val)
except ValueError:
raise BruinError(
f"Invalid {env_var} value '{val}': expected ISO-8601 datetime (YYYY-MM-DDThh:mm:ss)."
)
raise BruinError(f"Invalid {env_var} value '{val}': expected ISO-8601 datetime.")


def _coerce_value(value, type_def: dict):
Expand Down Expand Up @@ -95,22 +93,38 @@ class _BruinContext:
def start_date(self) -> "datetime.date | None":
return _parse_date("BRUIN_START_DATE")

@property
def end_date(self) -> "datetime.date | None":
return _parse_date("BRUIN_END_DATE")

@property
def start_datetime(self) -> "datetime.datetime | None":
return _parse_datetime("BRUIN_START_DATETIME")

@property
def start_timestamp(self) -> "datetime.datetime | None":
return _parse_datetime("BRUIN_START_TIMESTAMP")

@property
def end_date(self) -> "datetime.date | None":
return _parse_date("BRUIN_END_DATE")

@property
def end_datetime(self) -> "datetime.datetime | None":
return _parse_datetime("BRUIN_END_DATETIME")

@property
def end_timestamp(self) -> "datetime.datetime | None":
return _parse_datetime("BRUIN_END_TIMESTAMP")

@property
def execution_date(self) -> "datetime.date | None":
return _parse_date("BRUIN_EXECUTION_DATE")

@property
def execution_datetime(self) -> "datetime.datetime | None":
return _parse_datetime("BRUIN_EXECUTION_DATETIME")

@property
def execution_timestamp(self) -> "datetime.datetime | None":
return _parse_datetime("BRUIN_EXECUTION_TIMESTAMP")

@property
def run_id(self) -> "str | None":
return os.environ.get("BRUIN_RUN_ID")
Expand All @@ -131,6 +145,10 @@ def connection(self) -> "str | None":
def is_full_refresh(self) -> bool:
return os.environ.get("BRUIN_FULL_REFRESH") == "1"

@property
def commit_hash(self) -> "str | None":
return os.environ.get("BRUIN_COMMIT_HASH")

@property
def vars(self) -> dict:
val = os.environ.get("BRUIN_VARS")
Expand Down
59 changes: 59 additions & 0 deletions tests/test_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,23 @@ def test_returns_none_when_missing(self, monkeypatch):
assert context.start_datetime is None


class TestStartTimestamp:
def test_returns_datetime_with_tz(self, monkeypatch):
monkeypatch.setenv("BRUIN_START_TIMESTAMP", "2024-01-15T10:30:00.000000+00:00")
result = context.start_timestamp
assert result == datetime.datetime(2024, 1, 15, 10, 30, 0, tzinfo=datetime.timezone.utc)

def test_returns_datetime_with_non_utc_tz(self, monkeypatch):
monkeypatch.setenv("BRUIN_START_TIMESTAMP", "2024-01-15T10:30:00.000000+05:30")
result = context.start_timestamp
tz = datetime.timezone(datetime.timedelta(hours=5, minutes=30))
assert result == datetime.datetime(2024, 1, 15, 10, 30, 0, tzinfo=tz)

def test_returns_none_when_missing(self, monkeypatch):
monkeypatch.delenv("BRUIN_START_TIMESTAMP", raising=False)
assert context.start_timestamp is None


class TestEndDatetime:
def test_returns_datetime(self, monkeypatch):
monkeypatch.setenv("BRUIN_END_DATETIME", "2024-06-30T23:59:59")
Expand All @@ -47,6 +64,17 @@ def test_returns_none_when_missing(self, monkeypatch):
assert context.end_datetime is None


class TestEndTimestamp:
def test_returns_datetime_with_tz(self, monkeypatch):
monkeypatch.setenv("BRUIN_END_TIMESTAMP", "2024-06-30T23:59:59.000000+00:00")
result = context.end_timestamp
assert result == datetime.datetime(2024, 6, 30, 23, 59, 59, tzinfo=datetime.timezone.utc)

def test_returns_none_when_missing(self, monkeypatch):
monkeypatch.delenv("BRUIN_END_TIMESTAMP", raising=False)
assert context.end_timestamp is None


class TestExecutionDate:
def test_returns_date(self, monkeypatch):
monkeypatch.setenv("BRUIN_EXECUTION_DATE", "2024-03-01")
Expand All @@ -57,6 +85,27 @@ def test_returns_none_when_missing(self, monkeypatch):
assert context.execution_date is None


class TestExecutionDatetime:
def test_returns_datetime(self, monkeypatch):
monkeypatch.setenv("BRUIN_EXECUTION_DATETIME", "2024-03-01T12:00:00")
assert context.execution_datetime == datetime.datetime(2024, 3, 1, 12, 0, 0)

def test_returns_none_when_missing(self, monkeypatch):
monkeypatch.delenv("BRUIN_EXECUTION_DATETIME", raising=False)
assert context.execution_datetime is None


class TestExecutionTimestamp:
def test_returns_datetime_with_tz(self, monkeypatch):
monkeypatch.setenv("BRUIN_EXECUTION_TIMESTAMP", "2024-03-01T12:00:00.000000+00:00")
result = context.execution_timestamp
assert result == datetime.datetime(2024, 3, 1, 12, 0, 0, tzinfo=datetime.timezone.utc)

def test_returns_none_when_missing(self, monkeypatch):
monkeypatch.delenv("BRUIN_EXECUTION_TIMESTAMP", raising=False)
assert context.execution_timestamp is None


class TestRunId:
def test_returns_string(self, monkeypatch):
monkeypatch.setenv("BRUIN_RUN_ID", "abc-123")
Expand Down Expand Up @@ -97,6 +146,16 @@ def test_returns_none_when_missing(self, monkeypatch):
assert context.connection is None


class TestCommitHash:
def test_returns_string(self, monkeypatch):
monkeypatch.setenv("BRUIN_COMMIT_HASH", "abc1234def5678")
assert context.commit_hash == "abc1234def5678"

def test_returns_none_when_missing(self, monkeypatch):
monkeypatch.delenv("BRUIN_COMMIT_HASH", raising=False)
assert context.commit_hash is None


class TestIsFullRefresh:
def test_true_when_set_to_1(self, monkeypatch):
monkeypatch.setenv("BRUIN_FULL_REFRESH", "1")
Expand Down
Loading