From f45ddbd0fee509f7495c021874044e0391bebd2e Mon Sep 17 00:00:00 2001 From: Brad Heller Date: Wed, 14 Jan 2026 19:11:30 +0000 Subject: [PATCH 1/5] Add some new utility and info functions to Tower SDK --- src/tower/__init__.py | 11 +++ src/tower/_utils.py | 41 ++++++++++ src/tower/info/__init__.py | 159 +++++++++++++++++++++++++++++++++++++ 3 files changed, 211 insertions(+) create mode 100644 src/tower/_utils.py create mode 100644 src/tower/info/__init__.py diff --git a/src/tower/__init__.py b/src/tower/__init__.py index 44d6ebad..0a3c9d77 100644 --- a/src/tower/__init__.py +++ b/src/tower/__init__.py @@ -19,6 +19,13 @@ wait_for_runs, ) +from ._utils import( + param, + parameter, + secret, +) + + from ._features import override_get_attr, get_available_features, is_feature_enabled if TYPE_CHECKING: @@ -26,6 +33,10 @@ from ._llms import llms as llms from ._dbt import dbt as dbt +# +# Sub-packages to expose +# +from . import info def __getattr__(name: str): """ diff --git a/src/tower/_utils.py b/src/tower/_utils.py new file mode 100644 index 00000000..1e760239 --- /dev/null +++ b/src/tower/_utils.py @@ -0,0 +1,41 @@ +def secret(name: str, default: str =""): + """ + Retrieve a secret value from environment variables. + + Args: + name (str): The name of the secret (environment variable). + default (str, optional): The default value to return if the secret is not found. Defaults to "". + + Returns: + str: The value of the secret or the default value. + """ + import os + return os.getenv(name, default) + +def param(name: str, default: str =""): + """ + Retrieve a parameter value from this invocation. In this implementation, + it fetches the value from environment variables. + + Args: + name (str): The name of the parameter (environment variable). + default (str, optional): The default value to return if the parameter is not found. Defaults to "". + + Returns: + str: The value of the parameter or the default value. + """ + import os + return os.getenv(name, default) + +def parameter(name: str, default: str =""): + """ + Alias for param function to retrieve a parameter value from environment variables. + + Args: + name (str): The name of the parameter (environment variable). + default (str, optional): The default value to return if the parameter is not found. Defaults to "". + + Returns: + str: The value of the parameter or the default value. + """ + return param(name, default) diff --git a/src/tower/info/__init__.py b/src/tower/info/__init__.py new file mode 100644 index 00000000..f2ca8f18 --- /dev/null +++ b/src/tower/info/__init__.py @@ -0,0 +1,159 @@ +from typing import Optional + +def schedule_name() -> Optional[str]: + """ + Retrieve the name of the schedule that invoked this run from the runtime + environment. + + Returns: + Optional[str]: The name of the schedule if set, otherwise None. + """ + return _get_runtime_env_variable("SCHEDULE_NAME", None) + +def is_scheduled_run() -> bool: + """ + Check if the current run is a scheduled run based on environment variables. + Returns: + bool: True if it is a scheduled run, otherwise False. + """ + return schedule_name() is not None + +def schedule_id() -> Optional[str]: + """ + Retrieve the ID of the schedule that invoked this run from the runtime + environment. + + Returns: + Optional[str]: The ID of the schedule if set, otherwise None. + """ + return _get_runtime_env_variable("SCHEDULE_ID", None) + +def run_id() -> Optional[str]: + """ + Retrieve the ID of the current run from the runtime environment. + + Returns: + Optional[str]: The ID of the run if set, otherwise None. + """ + return _get_runtime_env_variable("RUN_ID", None) + +def hostname() -> Optional[str]: + """ + Retrieve the hostname of the current run assigned by the runtime + environment. + + Returns: + Optional[str]: The hostname if set, otherwise None. + """ + return _get_env_variable("TOWER__HOST", None) + +def port() -> Optional[int]: + """ + Retrieve the port number assigned to this run by the current runtime + environment. + + Returns: + Optional[int]: The port number if set, otherwise None. + """ + port_str = _get_env_variable("TOWER__PORT", None) + return int(port_str) if port_str is not None else None + +def is_cloud_run() -> bool: + """ + Check if the current run is executing in the Tower cloud environment. + + Returns: + bool: True if running in Tower cloud, otherwise False. + """ + val = _get_runtime_env_variable("IS_TOWER_MANAGED", "") + return _strtobool(val) + +def runner_name() -> str: + """ Retrieve the name of the runner executing this run from the runtime. If the + name is unknown, an empty string is returned. + + Returns: + str: The name of the runner or an empty string if unknown. + """ + return _get_runtime_env_variable("RUNNER_NAME", "") + +def runner_id() -> str: + """ + Retrieve the ID of the runner executing this run from the runtime. If the + name is unknown, an empty string is returned. + + Returns: + str: The ID of the runner or an empty string if unknown. + """ + return _get_runtime_env_variable("RUNNER_ID", "") + +def app_name() -> str: + """ + Retrieve the name of the app being executed in this run from the runtime. + + Returns: + str: The name of the app or an empty string if unknown. + """ + return _get_runtime_env_variable("APP_NAME") + +def account_name() -> str: + """ + Retrieve the name of the account associated with this run from the runtime. + + Returns: + str: The name of the account or an empty string if unknown. + """ + return _get_runtime_env_variable("ACCOUNT_NAME", "") + +def environment() -> str: + """ + Retrieve the name of the environment this run is running in. + + Returns: + str: The name of the environment or an empty string if unknown. + """ + return _get_runtime_env_variable("ENVIRONMENT_NAME", "") + +def is_manual_run() -> bool: + """ + Check if the current run was manually triggered. + + Returns: + bool: True if the run was manually triggered, otherwise False. + """ + val = _get_runtime_env_variable("IS_MANUAL_RUN", "false") + return _strtobool(val) + +def _get_runtime_env_variable(name: str, default: Optional[str] = "") -> Optional[str]: + """ + Helper function to retrieve a runtime environment variable. + + Args: + name (str): The name of the runtime environment variable. + + Returns: + Optional[str]: The value of the runtime environment variable if set, otherwise None. + """ + return _get_env_variable(f"TOWER__RUNTIME__{name}", default) + +def _get_env_variable(var_name: str, default: Optional[str] = "") -> Optional[str]: + """ + Helper function to retrieve an environment variable. + + Args: + var_name (str): The name of the environment variable. + + Returns: + Optional[str]: The value of the environment variable if set, otherwise None. + """ + import os + return os.getenv(var_name, default) + +def _strtobool(val: str) -> bool: + val = val.lower() + if val in ('y', 'yes', 't', 'true', 'on', '1'): + return True + elif val in ('n', 'no', 'f', 'false', 'off', '0'): + return False + else: + raise ValueError(f"invalid truth value {val!r}") From f4a02bab678cbf6405e254ac176ab2cbc87fe05e Mon Sep 17 00:00:00 2001 From: Brad Heller Date: Wed, 14 Jan 2026 19:30:30 +0000 Subject: [PATCH 2/5] chore: Add run_number() helper function --- src/tower/info/__init__.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/tower/info/__init__.py b/src/tower/info/__init__.py index f2ca8f18..aaf61747 100644 --- a/src/tower/info/__init__.py +++ b/src/tower/info/__init__.py @@ -37,6 +37,16 @@ def run_id() -> Optional[str]: """ return _get_runtime_env_variable("RUN_ID", None) +def run_number() -> Optional[int]: + """ + Retrieve the number of the current run from the runtime environment. + + Returns: + Optional[int]: The run number if set, otherwise None. + """ + run_number_str = _get_runtime_env_variable("RUN_NUMBER", None) + return int(run_number_str) if run_number_str is not None else None + def hostname() -> Optional[str]: """ Retrieve the hostname of the current run assigned by the runtime From 62e55b2e1ca748619e8d553e7fe3326f013f0775 Mon Sep 17 00:00:00 2001 From: Brad Heller Date: Wed, 14 Jan 2026 19:42:34 +0000 Subject: [PATCH 3/5] Shoudl be team, not team, in the info module --- src/tower/info/__init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tower/info/__init__.py b/src/tower/info/__init__.py index aaf61747..bef2e635 100644 --- a/src/tower/info/__init__.py +++ b/src/tower/info/__init__.py @@ -106,14 +106,14 @@ def app_name() -> str: """ return _get_runtime_env_variable("APP_NAME") -def account_name() -> str: +def team_name() -> str: """ - Retrieve the name of the account associated with this run from the runtime. + Retrieve the name of the team associated with this run from the runtime. Returns: - str: The name of the account or an empty string if unknown. + str: The name of the team or an empty string if unknown. """ - return _get_runtime_env_variable("ACCOUNT_NAME", "") + return _get_runtime_env_variable("TEAM_NAME", "") def environment() -> str: """ From 7233d66adf8d17197059e6065e59f915fe1b82f5 Mon Sep 17 00:00:00 2001 From: Brad Heller Date: Wed, 14 Jan 2026 19:48:24 +0000 Subject: [PATCH 4/5] Fix linting errors --- src/tower/__init__.py | 3 ++- src/tower/_utils.py | 10 +++++++--- src/tower/info/__init__.py | 24 +++++++++++++++++++++--- 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/src/tower/__init__.py b/src/tower/__init__.py index 0a3c9d77..9fa4c0fd 100644 --- a/src/tower/__init__.py +++ b/src/tower/__init__.py @@ -19,7 +19,7 @@ wait_for_runs, ) -from ._utils import( +from ._utils import ( param, parameter, secret, @@ -38,6 +38,7 @@ # from . import info + def __getattr__(name: str): """ Dynamic attribute lookup for optional features. diff --git a/src/tower/_utils.py b/src/tower/_utils.py index 1e760239..6a93aaf2 100644 --- a/src/tower/_utils.py +++ b/src/tower/_utils.py @@ -1,4 +1,4 @@ -def secret(name: str, default: str =""): +def secret(name: str, default: str = ""): """ Retrieve a secret value from environment variables. @@ -10,9 +10,11 @@ def secret(name: str, default: str =""): str: The value of the secret or the default value. """ import os + return os.getenv(name, default) -def param(name: str, default: str =""): + +def param(name: str, default: str = ""): """ Retrieve a parameter value from this invocation. In this implementation, it fetches the value from environment variables. @@ -25,9 +27,11 @@ def param(name: str, default: str =""): str: The value of the parameter or the default value. """ import os + return os.getenv(name, default) -def parameter(name: str, default: str =""): + +def parameter(name: str, default: str = ""): """ Alias for param function to retrieve a parameter value from environment variables. diff --git a/src/tower/info/__init__.py b/src/tower/info/__init__.py index bef2e635..9d5e9890 100644 --- a/src/tower/info/__init__.py +++ b/src/tower/info/__init__.py @@ -1,5 +1,6 @@ from typing import Optional + def schedule_name() -> Optional[str]: """ Retrieve the name of the schedule that invoked this run from the runtime @@ -10,6 +11,7 @@ def schedule_name() -> Optional[str]: """ return _get_runtime_env_variable("SCHEDULE_NAME", None) + def is_scheduled_run() -> bool: """ Check if the current run is a scheduled run based on environment variables. @@ -18,6 +20,7 @@ def is_scheduled_run() -> bool: """ return schedule_name() is not None + def schedule_id() -> Optional[str]: """ Retrieve the ID of the schedule that invoked this run from the runtime @@ -28,6 +31,7 @@ def schedule_id() -> Optional[str]: """ return _get_runtime_env_variable("SCHEDULE_ID", None) + def run_id() -> Optional[str]: """ Retrieve the ID of the current run from the runtime environment. @@ -37,6 +41,7 @@ def run_id() -> Optional[str]: """ return _get_runtime_env_variable("RUN_ID", None) + def run_number() -> Optional[int]: """ Retrieve the number of the current run from the runtime environment. @@ -47,6 +52,7 @@ def run_number() -> Optional[int]: run_number_str = _get_runtime_env_variable("RUN_NUMBER", None) return int(run_number_str) if run_number_str is not None else None + def hostname() -> Optional[str]: """ Retrieve the hostname of the current run assigned by the runtime @@ -57,6 +63,7 @@ def hostname() -> Optional[str]: """ return _get_env_variable("TOWER__HOST", None) + def port() -> Optional[int]: """ Retrieve the port number assigned to this run by the current runtime @@ -68,6 +75,7 @@ def port() -> Optional[int]: port_str = _get_env_variable("TOWER__PORT", None) return int(port_str) if port_str is not None else None + def is_cloud_run() -> bool: """ Check if the current run is executing in the Tower cloud environment. @@ -78,8 +86,9 @@ def is_cloud_run() -> bool: val = _get_runtime_env_variable("IS_TOWER_MANAGED", "") return _strtobool(val) + def runner_name() -> str: - """ Retrieve the name of the runner executing this run from the runtime. If the + """Retrieve the name of the runner executing this run from the runtime. If the name is unknown, an empty string is returned. Returns: @@ -87,6 +96,7 @@ def runner_name() -> str: """ return _get_runtime_env_variable("RUNNER_NAME", "") + def runner_id() -> str: """ Retrieve the ID of the runner executing this run from the runtime. If the @@ -97,6 +107,7 @@ def runner_id() -> str: """ return _get_runtime_env_variable("RUNNER_ID", "") + def app_name() -> str: """ Retrieve the name of the app being executed in this run from the runtime. @@ -106,6 +117,7 @@ def app_name() -> str: """ return _get_runtime_env_variable("APP_NAME") + def team_name() -> str: """ Retrieve the name of the team associated with this run from the runtime. @@ -115,6 +127,7 @@ def team_name() -> str: """ return _get_runtime_env_variable("TEAM_NAME", "") + def environment() -> str: """ Retrieve the name of the environment this run is running in. @@ -124,6 +137,7 @@ def environment() -> str: """ return _get_runtime_env_variable("ENVIRONMENT_NAME", "") + def is_manual_run() -> bool: """ Check if the current run was manually triggered. @@ -134,6 +148,7 @@ def is_manual_run() -> bool: val = _get_runtime_env_variable("IS_MANUAL_RUN", "false") return _strtobool(val) + def _get_runtime_env_variable(name: str, default: Optional[str] = "") -> Optional[str]: """ Helper function to retrieve a runtime environment variable. @@ -146,6 +161,7 @@ def _get_runtime_env_variable(name: str, default: Optional[str] = "") -> Optiona """ return _get_env_variable(f"TOWER__RUNTIME__{name}", default) + def _get_env_variable(var_name: str, default: Optional[str] = "") -> Optional[str]: """ Helper function to retrieve an environment variable. @@ -157,13 +173,15 @@ def _get_env_variable(var_name: str, default: Optional[str] = "") -> Optional[st Optional[str]: The value of the environment variable if set, otherwise None. """ import os + return os.getenv(var_name, default) + def _strtobool(val: str) -> bool: val = val.lower() - if val in ('y', 'yes', 't', 'true', 'on', '1'): + if val in ("y", "yes", "t", "true", "on", "1"): return True - elif val in ('n', 'no', 'f', 'false', 'off', '0'): + elif val in ("n", "no", "f", "false", "off", "0"): return False else: raise ValueError(f"invalid truth value {val!r}") From 04e376880a3be49febd44eb66db0284fdf26c23b Mon Sep 17 00:00:00 2001 From: Brad Heller Date: Wed, 14 Jan 2026 21:03:55 +0100 Subject: [PATCH 5/5] Update src/tower/info/__init__.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/tower/info/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tower/info/__init__.py b/src/tower/info/__init__.py index 9d5e9890..8aede614 100644 --- a/src/tower/info/__init__.py +++ b/src/tower/info/__init__.py @@ -100,7 +100,7 @@ def runner_name() -> str: def runner_id() -> str: """ Retrieve the ID of the runner executing this run from the runtime. If the - name is unknown, an empty string is returned. + ID is unknown, an empty string is returned. Returns: str: The ID of the runner or an empty string if unknown.