# Naming


Setup: the demo's `Orders` schema, which every example on this page uses

``` python
import dagster as dg
import dataframely as dy
import polars as pl

import dagster_dataframely as dd
from dagster_dataframely_demo.schema import Orders
```


This page covers four things this package sets: the asset's description, each check's description, the name of the asset's op, and the namespaces of the names and keys it generates.


# The description comes from the schema

`dd.asset` takes the asset's description from the first of these you set: the `description=` argument, the schema's docstring, and the decorated function's docstring, which is Dagster's own default.


``` python
class Orders(dy.Schema):
    """A customer order line: one row per product on one order."""


@dd.asset(Orders)
def orders() -> pl.DataFrame:
    """Joins the two extracts and drops the test accounts."""
    ...
```


The catalog shows `A customer order line: one row per product on one order.` as the description. The schema's docstring takes precedence over the function's, because it describes the table, not the code. `dd.asset` reads the schema's own docstring only, and ignores one inherited from a parent class, including `dy.Schema`'s.

The spec from [quarantine_spec](../reference/quarantine_spec.md#dagster_dataframely.quarantine_spec) has its own description: `Invalid rows from <asset>, with one column per rule.`


# Where a check's text comes from

The Dagster UI shows a schema's rules in three places, and each has its own fallback order.

| place | first choice | then | then |
|----|----|----|----|
| Columns tab | the rendered column constraint | the rule name | never the docstring |
| check name | `dy_rule__<rule>` at `rule` granularity, `dy_col__<column>` or `dy_schema__rules` at the others | never the docstring |  |
| check description | the rule's docstring | `<column> <rendered constraint>` | the whole name Dataframely reports, column part included |
| collapsed check description | each rule's rendered constraint | the rule name | never the docstring |

The package renders a rule's value, such as the bound of `min`, in the column constraint and in each check result's `dy_rule__expr` metadata, but never in the check name. So changing a bound does not rename the check or start a new check history.

The package renders column constraints as operators: `>= 0.0`, `length <= 64 bytes`, `matches ^[a-z]+$`, `in (a, b)`, `aligned to 1h`. It omits `nullability` and `unique`, because Dagster has separate fields for them on the column. It omits `inf` and `nan` too, because Dataframely adds them to every float column by default. All four still appear in the check name and the check description.

A `check=` with a bare lambda has no name, so the package renders it as `custom check` everywhere. Name your checks with a dict, such as `check={"lowercase": ...}`, and the package shows the key instead.


# Dagster names the op after the whole asset key

`@dd.asset(Orders, key_prefix="sales", name="orders")` creates an op named `sales__orders`, the same way `@dg.asset` names its op. An op name must be unique within a code location, and an asset name need not be. With the asset name alone, two assets named `orders` under different prefixes would have two ops with the same name.

The op name is also the step key and the key under `ops:` in run config, so both use the whole asset key:

``` yaml
ops:
  sales__orders:
    config:
      threshold: 4
```


# The reserved namespaces

There are three.

**`dy_`** starts every check name, every rule column in the quarantine, and every check metadata key except `dataframely/quarantine_address`. These are the column-schema check `dy_schema__columns`, each rule's check `dy_rule__<rule>`, the collapsed checks `dy_col__<column>` and `dy_schema__rules`, and the metadata keys `dy_rule`, `dy_rule__expr`, `dy_rules`, `dy_failed_count`, `dy_failed_sample` and `dy_schema__errors`. A check name becomes an op output name, which Dagster checks against `^[A-Za-z0-9_]+$`. So a check name cannot contain a slash.

A rule's check name is `dy_rule__` plus the name Dataframely gives the rule, with `|` replaced by `__`: `amount|min` becomes `dy_rule__amount__min`. `dd.wiring.check_name("amount|min")` returns that check name, so a test does not have to build it by hand.

**`dataframely/`** starts every materialization metadata key this package names. A metadata key can contain a slash, so this namespace has the same form as Dagster's own `dagster/`. This package's keys then sort together, apart from Dagster's keys and your IO manager's.

`dataframely/quarantine_address` is the one `dataframely/` key that also appears on check results. When every row fails validation, the run raises [NoValidRowsError](../reference/errors.NoValidRowsError.md#dagster_dataframely.errors.NoValidRowsError) and has no materialization. So each check result has the quarantine address under that key.

Every public function that takes a schema raises three errors for names this package cannot use ([ADR-0008](https://github.com/ozanozbeker/dagster-dataframely/blob/main/docs/pre-1.0.md#adr-0008-every-public-function-that-takes-a-schema-validates-it)). It raises [ReservedColumnError](../reference/errors.ReservedColumnError.md#dagster_dataframely.errors.ReservedColumnError) for a column name that starts with `dy_`. It raises [InvalidColumnNameError](../reference/errors.InvalidColumnNameError.md#dagster_dataframely.errors.InvalidColumnNameError) for a column name with characters Dagster does not allow in a check name, which is anything outside `A-Za-z0-9_`. It raises [CheckNameCollisionError](../reference/errors.CheckNameCollisionError.md#dagster_dataframely.errors.CheckNameCollisionError) for two rules that produce the same check name. So `dd.asset` raises them where you declare the asset, and a hand-wired asset raises them in the first of these functions it calls.

A column name with such characters almost always comes from `dy.Column(alias=...)`, which Dataframely provides for a name that is not a Python identifier. A `|` is one of them, and Dataframely also uses it to separate a column from its rule name.

**`<name>_quarantine`** is the third: the asset key a writer writes the invalid rows under. You also choose asset keys in this key space, so another asset can already have this key. Before the decorated function runs, a run of an asset with `quarantine=True` checks that no other asset in the code location materializes this key. If one does, the run raises [QuarantineKeyCollisionError](../reference/errors.QuarantineKeyCollisionError.md#dagster_dataframely.errors.QuarantineKeyCollisionError).

No setting changes any of the three, so they are the same in every project.
