0.9.5#

Released on April 4, 2025.

Security#

SSO#

The atoti.KerberosConfig.username_case_conversion and atoti.LdapConfig.username_case_conversion attributes have been added to coerce the name of users logging in to the expected case [1].

Not picking a case conversion is a source of confusion or bugs so leaving these attributes unset will raise a deprecation warning.

Database access#

atoti.tables.Tables.owners and atoti.tables.Tables.readers have been added [2].

Their impact is not limited to the Python API. For instance, atoti.tables.Tables.readers will also control whether end users are able to see tables in Atoti Admin UI Database tab.

Dependencies#

  • Upgraded Atoti UI and Atoti Admin UI to 5.2.7.

  • Upgraded Atoti Server to 6.1.6.

Fixed#

Data loading#

Cloud storage#

On Windows, passing a URL to atoti.CsvLoad.path or atoti.ParquetLoad.path raised an InvalidPathException [6].

Data modeling#

Conditions#

Creating logical conditions (i.e. boolean combinations of leaf conditions such as (level["Product"] == "Phone") | (level["Country"] == "Portugal")) with more than 508 leaves raised a ValidationError because it reached the maximum nesting depth supported by the runtime type checker [7].

This was fixed by allowing the internal representation of a logical condition to group more than 2 operands. For example, (a & b) | c | d | f | g (with a maximum depth of 2) replaces the old internal representation (((a & b) | c) | (d | f)) | g (with a maximum depth of 4).

Table columns#

Columns are strictly typed: a column with a "LocalDate" data_type can only store dates (or None if its default_value is None); it cannot store a "String" such as "NaN". Since Java has no equivalent of pandas.NaT, the only available values to represent a special null-restricted:

  • "LocalDate" are LocalDate.MIN and LocalDate.MAX,

  • "LocalDateTime" are LocalDateTime.MIN and LocalDateTime.MAX.

However, accessing default_value when it was set to one of these values raised a ValidationError [3]. This is fixed.

Measures#

  • The type annotation of filter()’s filter parameter never allowed inverted atoti.Level.isin() conditions (e.g. ~level.isin("foo", "bar")) but, by chance, these conditions actually behaved as expected at runtime.

    However, 0.9.4 introduced runtime validation of condition types which lead to the rejection of these conditions. This regression is fixed: the type annotation of filter accepts these conditions and they are supported at runtime [4].

  • Passing isnull conditions to atoti.where() raised an UnknownUnderlyingMeasureRuntimeException [5].

Internal issue tracker references