From 8364348513f971744f647f59f946999bda310509 Mon Sep 17 00:00:00 2001 From: Tatiana Al-Chueyr Date: Tue, 5 Sep 2023 09:51:15 +0100 Subject: [PATCH 01/10] Run MyPy as part of the CI (cherry picked from commit 06c483d8bff403a552518a9944572d26aa684039) --- .github/workflows/test.yml | 17 +++++++++++++++++ docs/configuration/lineage.rst | 2 +- pyproject.toml | 11 ++++++++--- 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0963dba8e4..d4f6080469 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -21,6 +21,23 @@ jobs: steps: - run: true + Type-Check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v3 + with: + python-version: '3.10' + architecture: 'x64' + - uses: actions/cache@v3 + with: + path: | + ~/.cache/pip + .nox + key: ${{ runner.os }}-${{ hashFiles('python-sdk/pyproject.toml') }} + - run: pip3 install nox + - run: nox -s type_check + Run-Unit-Tests: runs-on: ubuntu-latest strategy: diff --git a/docs/configuration/lineage.rst b/docs/configuration/lineage.rst index 767deaaa32..99c37ae681 100644 --- a/docs/configuration/lineage.rst +++ b/docs/configuration/lineage.rst @@ -20,7 +20,7 @@ Installation If using Airflow 2.7, no other dependency is required. -Otherwise, install the Python package ``openlineage-airflow``. +Otherwise, install the Python package ``astronomer-cosmos[openlineage]`` or ``openlineage-airflow``. Namespace configuration diff --git a/pyproject.toml b/pyproject.toml index c9bace3109..3ca59c8abb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -42,7 +42,6 @@ dependencies = [ "typing-extensions; python_version < '3.8'", "virtualenv", "openlineage-integration-common", - "openlineage-airflow" ] [project.optional-dependencies] @@ -76,7 +75,7 @@ dbt-snowflake = [ dbt-spark = [ "dbt-spark<=1.5.4", ] -lineage = [ +openlineage = [ "openlineage-airflow", ] all = [ @@ -132,7 +131,12 @@ dependencies = [ "astronomer-cosmos[tests]", "apache-airflow-providers-docker>=3.5.0", "apache-airflow-providers-cncf-kubernetes>=5.1.1,<7.3.0", - "openlineage-airflow" + "types-PyYAML", + "types-attrs", + "attrs", + "types-requests", + "types-python-dateutil", + "apache-airflow" ] [[tool.hatch.envs.tests.matrix]] @@ -150,6 +154,7 @@ matrix.airflow.dependencies = [ [tool.hatch.envs.tests.scripts] freeze = "pip freeze" +type-check = "mypy cosmos" test = 'pytest -vv --durations=0 . -m "not integration" --ignore=tests/test_example_dags.py --ignore=tests/test_example_dags_no_connections.py' test-cov = 'pytest -vv --cov=cosmos --cov-report=term-missing --cov-report=xml --durations=0 -m "not integration" --ignore=tests/test_example_dags.py --ignore=tests/test_example_dags_no_connections.py' # we install using the following workaround to overcome installation conflicts, such as: From a0f7553ab276ebd224d4232e1b430606749bd263 Mon Sep 17 00:00:00 2001 From: Tatiana Al-Chueyr Date: Tue, 5 Sep 2023 10:29:06 +0100 Subject: [PATCH 02/10] Enable MyPy checks in the CI, since pre-commit reached quota limit As observed in: https://github.com/astronomer/astronomer-cosmos/pull/482 The following exception was raised: build of https://github.com/pre-commit/mirrors-mypy:types-PyYAML,types-attrs,attrs,types-requests,types-python-dateutil,apache-airflow@v1.5.1 for python@python3 exceeds tier max size 250MiB: 262.2MiB So we disabled MyPy checks as part of https://github.com/astronomer/astronomer-cosmos/pull/485 --- .github/workflows/test.yml | 7 +++---- .pre-commit-config.yaml | 2 +- cosmos/operators/docker.py | 10 +++++----- cosmos/operators/kubernetes.py | 12 +++++++----- pyproject.toml | 2 ++ 5 files changed, 18 insertions(+), 15 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d4f6080469..97ea9cd39c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -33,10 +33,9 @@ jobs: with: path: | ~/.cache/pip - .nox - key: ${{ runner.os }}-${{ hashFiles('python-sdk/pyproject.toml') }} - - run: pip3 install nox - - run: nox -s type_check + key: ${{ runner.os }}-${{ hashFiles('pyproject.toml') }} + - run: pip3 install hatch mypy + - run: hatch run tests.py3.9-2.7:type-check Run-Unit-Tests: runs-on: ubuntu-latest diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a7a33789be..635c394ec0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -70,7 +70,7 @@ repos: rev: 'v1.5.1' hooks: - id: mypy - name: mypy-python-sdk + name: mypy-python additional_dependencies: [types-PyYAML, types-attrs, attrs, types-requests, types-python-dateutil, apache-airflow] files: ^cosmos diff --git a/cosmos/operators/docker.py b/cosmos/operators/docker.py index 0dd724153c..fb2e1c90c7 100644 --- a/cosmos/operators/docker.py +++ b/cosmos/operators/docker.py @@ -20,13 +20,13 @@ ) -class DbtDockerBaseOperator(DockerOperator, DbtBaseOperator): # type: ignore[misc] # ignores subclass MyPy error +class DbtDockerBaseOperator(DockerOperator, DbtBaseOperator): # type: ignore """ Executes a dbt core cli command in a Docker container. """ - template_fields: Sequence[str] = DbtBaseOperator.template_fields + DockerOperator.template_fields + template_fields: Sequence[str] = tuple(list(DbtBaseOperator.template_fields) + list(DockerOperator.template_fields)) intercept_flag = False @@ -39,7 +39,7 @@ def __init__( def build_and_run_cmd(self, context: Context, cmd_flags: list[str] | None = None) -> Any: self.build_command(context, cmd_flags) - self.log.info(f"Running command: {self.command}") # type: ignore[has-type] + self.log.info(f"Running command: {self.command}") result = super().execute(context) logger.info(result) @@ -50,8 +50,8 @@ def build_command(self, context: Context, cmd_flags: list[str] | None = None) -> self.dbt_executable_path = "dbt" dbt_cmd, env_vars = self.build_cmd(context=context, cmd_flags=cmd_flags) # set env vars - self.environment = {**env_vars, **self.environment} # type: ignore[has-type] - self.command = dbt_cmd + self.environment: dict[str, Any] = {**env_vars, **self.environment} + self.command: list[str] = dbt_cmd def execute(self, context: Context) -> None: self.build_and_run_cmd(context=context) diff --git a/cosmos/operators/kubernetes.py b/cosmos/operators/kubernetes.py index 94a3b8f53c..38ca474525 100644 --- a/cosmos/operators/kubernetes.py +++ b/cosmos/operators/kubernetes.py @@ -26,13 +26,15 @@ ) -class DbtKubernetesBaseOperator(KubernetesPodOperator, DbtBaseOperator): # type: ignore[misc] +class DbtKubernetesBaseOperator(KubernetesPodOperator, DbtBaseOperator): # type: ignore """ Executes a dbt core cli command in a Kubernetes Pod. """ - template_fields: Sequence[str] = DbtBaseOperator.template_fields + KubernetesPodOperator.template_fields + template_fields: Sequence[str] = tuple( + list(DbtBaseOperator.template_fields) + list(KubernetesPodOperator.template_fields) + ) intercept_flag = False @@ -41,12 +43,12 @@ def __init__(self, profile_config: ProfileConfig | None = None, **kwargs: Any) - super().__init__(**kwargs) def build_env_args(self, env: dict[str, str | bytes | PathLike[Any]]) -> None: - env_vars_dict = dict() + env_vars_dict: dict[str, str] = dict() - for env_var in self.env_vars: # type: ignore[has-type] + for env_var in self.env_vars: env_vars_dict[env_var.name] = env_var.value - self.env_vars = convert_env_vars({**env, **env_vars_dict}) + self.env_vars: list[Any] = convert_env_vars({**env, **env_vars_dict}) def build_and_run_cmd(self, context: Context, cmd_flags: list[str] | None = None) -> Any: self.build_kube_args(context, cmd_flags) diff --git a/pyproject.toml b/pyproject.toml index 3ca59c8abb..c86e6ad5ad 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -220,6 +220,8 @@ known_third_party = ["airflow", "jinja2"] [tool.mypy] strict = true +ignore_missing_imports = true +no_warn_unused_ignores = true [tool.ruff] line-length = 120 From ee361fb030ca537967cacc779d85dcdc6aca4444 Mon Sep 17 00:00:00 2001 From: Tatiana Al-Chueyr Date: Tue, 5 Sep 2023 11:23:59 +0100 Subject: [PATCH 03/10] Fix optinal openlineage imports --- cosmos/operators/local.py | 17 ++++++++++++----- pyproject.toml | 6 ++++-- tests/operators/test_local.py | 1 + 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/cosmos/operators/local.py b/cosmos/operators/local.py index 8c980d6928..ec30712ce0 100644 --- a/cosmos/operators/local.py +++ b/cosmos/operators/local.py @@ -7,6 +7,7 @@ from pathlib import Path from typing import Any, Callable, Literal, Sequence, TYPE_CHECKING +import airflow import yaml from airflow import DAG from airflow.compat.functools import cached_property @@ -43,17 +44,17 @@ ) logger = get_logger(__name__) -lineage_namespace = conf.get("openlineage", "namespace", fallback=os.getenv("OPENLINEAGE_NAMESPACE", "default")) try: from airflow.providers.openlineage.extractors.base import OperatorLineage -except (ImportError, ModuleNotFoundError): - from openlineage.airflow.extractors.base import OperatorLineage -except (ImportError, ModuleNotFoundError): + from openlineage.airflow.extractors.base import OperatorLineage # noqa +except (ImportError, ModuleNotFoundError) as error: logger.warning( - "To enable emitting Openlineage events, please, upgrade to Airflow 2.7 or install `openlineage-airflow`." + "To enable emitting Openlineage events. In order to use openlineage, upgrade to Airflow 2.7 or " + "install astronomer-cosmos[openlineage]." ) + logger.exception(error) is_openlineage_available = False @@ -248,6 +249,12 @@ def calculate_openlineage_events_completes( for key, value in env.items(): os.environ[key] = str(value) + lineage_namespace = os.getenv("OPENLINEAGE_NAMESPACE", "default") + try: + lineage_namespace = conf.get("openlineage", "namespace") + except airflow.exceptions.AirflowConfigException: + pass + openlineage_processor = DbtLocalArtifactProcessor( producer=OPENLINEAGE_PRODUCER, job_namespace=lineage_namespace, diff --git a/pyproject.toml b/pyproject.toml index c86e6ad5ad..2c72d4f872 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -80,7 +80,7 @@ openlineage = [ ] all = [ "astronomer-cosmos[dbt-all]", - "astronomer-cosmos[lineage]" + "astronomer-cosmos[openlineage]" ] docs =[ "sphinx", @@ -156,7 +156,9 @@ matrix.airflow.dependencies = [ freeze = "pip freeze" type-check = "mypy cosmos" test = 'pytest -vv --durations=0 . -m "not integration" --ignore=tests/test_example_dags.py --ignore=tests/test_example_dags_no_connections.py' -test-cov = 'pytest -vv --cov=cosmos --cov-report=term-missing --cov-report=xml --durations=0 -m "not integration" --ignore=tests/test_example_dags.py --ignore=tests/test_example_dags_no_connections.py' +test-cov = """rm -rf airflow.*; \ +airflow db init; \ +pytest -vv --cov=cosmos --cov-report=term-missing --cov-report=xml --durations=0 -m "not integration" --ignore=tests/test_example_dags.py --ignore=tests/test_example_dags_no_connections.py""" # we install using the following workaround to overcome installation conflicts, such as: # apache-airflow 2.3.0 and dbt-core [0.13.0 - 1.5.2] and jinja2>=3.0.0 because these package versions have conflicting dependencies test-integration-setup = """pip uninstall dbt-postgres dbt-databricks; \ diff --git a/tests/operators/test_local.py b/tests/operators/test_local.py index ffde94f1db..7d00d88c88 100644 --- a/tests/operators/test_local.py +++ b/tests/operators/test_local.py @@ -172,6 +172,7 @@ def test_run_operator_dataset_inlets_and_outlets(): assert test_operator.outlets == [] +@pytest.mark.integration def test_run_operator_emits_events(): class MockRun: facets = {"c": 3} From 4a3d9249a7f89053360d54c86b1c8230ecf5ced8 Mon Sep 17 00:00:00 2001 From: Tatiana Al-Chueyr Date: Tue, 5 Sep 2023 11:44:00 +0100 Subject: [PATCH 04/10] Fix tests dependencies --- pyproject.toml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 2c72d4f872..7d9dc02440 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -156,15 +156,13 @@ matrix.airflow.dependencies = [ freeze = "pip freeze" type-check = "mypy cosmos" test = 'pytest -vv --durations=0 . -m "not integration" --ignore=tests/test_example_dags.py --ignore=tests/test_example_dags_no_connections.py' -test-cov = """rm -rf airflow.*; \ -airflow db init; \ -pytest -vv --cov=cosmos --cov-report=term-missing --cov-report=xml --durations=0 -m "not integration" --ignore=tests/test_example_dags.py --ignore=tests/test_example_dags_no_connections.py""" +test-cov = """pytest -vv --cov=cosmos --cov-report=term-missing --cov-report=xml --durations=0 -m "not integration" --ignore=tests/test_example_dags.py --ignore=tests/test_example_dags_no_connections.py""" # we install using the following workaround to overcome installation conflicts, such as: # apache-airflow 2.3.0 and dbt-core [0.13.0 - 1.5.2] and jinja2>=3.0.0 because these package versions have conflicting dependencies test-integration-setup = """pip uninstall dbt-postgres dbt-databricks; \ rm -rf airflow.*; \ airflow db init; \ -pip install 'dbt-postgres<=1.5.4' 'dbt-databricks<=1.5.4'""" +pip install 'openlineage-airflow' 'dbt-postgres<=1.5.4' 'dbt-databricks<=1.5.4'""" test-integration = """rm -rf dbt/jaffle_shop/dbt_packages; pytest -vv \ --cov=cosmos \ From ff5fd374ba2ef81a35c79261f3e0577940d95185 Mon Sep 17 00:00:00 2001 From: Tatiana Al-Chueyr Date: Tue, 5 Sep 2023 12:00:38 +0100 Subject: [PATCH 05/10] Simplify pyproject --- pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 7d9dc02440..b0fb8dacae 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -170,7 +170,7 @@ pytest -vv \ --cov-report=xml \ --durations=0 \ -m integration \ --k 'not (example_cosmos_python_models or example_cosmos_python_models or example_virtualenv)' +-k 'not (example_cosmos_python_models or example_virtualenv)' """ test-integration-expensive = """rm -rf dbt/jaffle_shop/dbt_packages; pytest -vv \ @@ -179,7 +179,7 @@ pytest -vv \ --cov-report=xml \ --durations=0 \ -m integration \ --k 'example_cosmos_python_models or example_cosmos_python_models or example_virtualenv'""" +-k 'example_cosmos_python_models or example_virtualenv'""" [tool.pytest.ini_options] filterwarnings = [ From c8d2f879e9fb55d3cc2a442561c98b43d3a395b4 Mon Sep 17 00:00:00 2001 From: Tatiana Al-Chueyr Date: Tue, 5 Sep 2023 12:17:49 +0100 Subject: [PATCH 06/10] Fix import --- cosmos/operators/local.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/cosmos/operators/local.py b/cosmos/operators/local.py index ec30712ce0..5526a326ee 100644 --- a/cosmos/operators/local.py +++ b/cosmos/operators/local.py @@ -48,14 +48,16 @@ try: from airflow.providers.openlineage.extractors.base import OperatorLineage - from openlineage.airflow.extractors.base import OperatorLineage # noqa -except (ImportError, ModuleNotFoundError) as error: - logger.warning( - "To enable emitting Openlineage events. In order to use openlineage, upgrade to Airflow 2.7 or " - "install astronomer-cosmos[openlineage]." - ) - logger.exception(error) - is_openlineage_available = False +except (ImportError, ModuleNotFoundError): + try: + from openlineage.airflow.extractors.base import OperatorLineage + except (ImportError, ModuleNotFoundError) as error: + logger.warning( + "To enable emitting Openlineage events. In order to use openlineage, upgrade to Airflow 2.7 or " + "install astronomer-cosmos[openlineage]." + ) + logger.exception(error) + is_openlineage_available = False class DbtLocalBaseOperator(DbtBaseOperator): From b0f4cc1ff0602543c3a18edd6f606119ab2bd38c Mon Sep 17 00:00:00 2001 From: Tatiana Al-Chueyr Date: Tue, 5 Sep 2023 12:55:50 +0100 Subject: [PATCH 07/10] Fix CI issue --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index b0fb8dacae..66e8d2852d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -162,7 +162,7 @@ test-cov = """pytest -vv --cov=cosmos --cov-report=term-missing --cov-report=xml test-integration-setup = """pip uninstall dbt-postgres dbt-databricks; \ rm -rf airflow.*; \ airflow db init; \ -pip install 'openlineage-airflow' 'dbt-postgres<=1.5.4' 'dbt-databricks<=1.5.4'""" +pip install 'dbt-core==1.5.4' 'dbt-databricks<=1.5.4' 'dbt-postgres<=1.5.4' 'openlineage-airflow'""" test-integration = """rm -rf dbt/jaffle_shop/dbt_packages; pytest -vv \ --cov=cosmos \ From c399fd9550005bad33a66aebbc1574b7607c4ed0 Mon Sep 17 00:00:00 2001 From: Tatiana Al-Chueyr Date: Wed, 6 Sep 2023 12:17:33 +0100 Subject: [PATCH 08/10] Address Julian's feedback on docs --- docs/configuration/lineage.rst | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/docs/configuration/lineage.rst b/docs/configuration/lineage.rst index 99c37ae681..83bd3c5502 100644 --- a/docs/configuration/lineage.rst +++ b/docs/configuration/lineage.rst @@ -10,7 +10,7 @@ and virtualenv execution methods (read `execution modes <../getting_started/exec To emit lineage events, Cosmos can use one of the following: 1. Airflow 2.7 `built-in support to OpenLineage `_, or -2. The `openlineage-airflow `_ package +2. `Additional libraries `_. No change to the user DAG files is required to use OpenLineage. @@ -20,14 +20,22 @@ Installation If using Airflow 2.7, no other dependency is required. -Otherwise, install the Python package ``astronomer-cosmos[openlineage]`` or ``openlineage-airflow``. +Otherwise, install Cosmos using ``astronomer-cosmos[openlineage]``. -Namespace configuration ------------------------ +Configuration +------------- + +If using Airflow 2.7, follow `these instructions `_ on how to configure OpenLineage. + +Otherwise, follow `these instructions `_. + + +Namespace +......... Cosmos will use the Airflow ``[openlineage]`` ``namespace`` property as a namespace, `if available `_. Otherwise, it attempts to use the environment variable ``OPENLINEAGE_NAMESPACE`` as the namespace. -If not defined, it uses ``"default"`` as the namespace. +Finally, if neither are defined, it uses ``"default"`` as the namespace. From 6040e8efcbb491cfd71fd9d45c806158f203139e Mon Sep 17 00:00:00 2001 From: Tatiana Al-Chueyr Date: Wed, 6 Sep 2023 12:49:07 +0100 Subject: [PATCH 09/10] Fix issue with emitting openlineage events from unsupported provider. File "/home/runner/.local/share/hatch/env/virtual/astronomer-cosmos/Za_bFbg4/tests.py3.10-2.6/lib/python3.10/site-packages/openlineage/common/provider/dbt/processor.py", line 568, in extract_adapter_type raise NotImplementedError( NotImplementedError: Only `bigquery`,`snowflake`,`redshift`,`spark`,`***` adapters are supported right now. Passed databricks --- cosmos/operators/local.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cosmos/operators/local.py b/cosmos/operators/local.py index 5526a326ee..422812de97 100644 --- a/cosmos/operators/local.py +++ b/cosmos/operators/local.py @@ -270,7 +270,7 @@ def calculate_openlineage_events_completes( try: events = openlineage_processor.parse() self.openlineage_events_completes = events.completes - except FileNotFoundError as error: + except (FileNotFoundError, NotImplementedError) as error: logger.exception(error) def get_datasets(self, source: Literal["inputs", "outputs"]) -> list[Dataset]: From 9776dae8d627500567c1ed30157b313e8f2fd928 Mon Sep 17 00:00:00 2001 From: Tatiana Al-Chueyr Date: Wed, 6 Sep 2023 21:04:21 +0100 Subject: [PATCH 10/10] Rename default cosmos lineage namespace to cosmos, after CR feedback --- cosmos/constants.py | 2 ++ cosmos/operators/local.py | 4 ++-- docs/configuration/lineage.rst | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/cosmos/constants.py b/cosmos/constants.py index ec67f8cd2f..90037f14d2 100644 --- a/cosmos/constants.py +++ b/cosmos/constants.py @@ -12,6 +12,8 @@ DBT_TARGET_DIR_NAME = "target" DBT_LOG_FILENAME = "dbt.log" DBT_BINARY_NAME = "dbt" + +DEFAULT_OPENLINEAGE_NAMESPACE = "cosmos" OPENLINEAGE_PRODUCER = "https://github.com/astronomer/astronomer-cosmos/" diff --git a/cosmos/operators/local.py b/cosmos/operators/local.py index 422812de97..69bc1a78c0 100644 --- a/cosmos/operators/local.py +++ b/cosmos/operators/local.py @@ -31,7 +31,7 @@ from sqlalchemy.orm import Session -from cosmos.constants import OPENLINEAGE_PRODUCER +from cosmos.constants import DEFAULT_OPENLINEAGE_NAMESPACE, OPENLINEAGE_PRODUCER from cosmos.config import ProfileConfig from cosmos.log import get_logger from cosmos.operators.base import DbtBaseOperator @@ -251,7 +251,7 @@ def calculate_openlineage_events_completes( for key, value in env.items(): os.environ[key] = str(value) - lineage_namespace = os.getenv("OPENLINEAGE_NAMESPACE", "default") + lineage_namespace = os.getenv("OPENLINEAGE_NAMESPACE", DEFAULT_OPENLINEAGE_NAMESPACE) try: lineage_namespace = conf.get("openlineage", "namespace") except airflow.exceptions.AirflowConfigException: diff --git a/docs/configuration/lineage.rst b/docs/configuration/lineage.rst index 83bd3c5502..54f9ad46db 100644 --- a/docs/configuration/lineage.rst +++ b/docs/configuration/lineage.rst @@ -38,4 +38,4 @@ Cosmos will use the Airflow ``[openlineage]`` ``namespace`` property as a namesp Otherwise, it attempts to use the environment variable ``OPENLINEAGE_NAMESPACE`` as the namespace. -Finally, if neither are defined, it uses ``"default"`` as the namespace. +Finally, if neither are defined, it uses ``"cosmos"`` as the namespace.