Skip to main content
This is a detailed list of all changes. For a higher level view, see the Release notes. Atoti Python SDK implements Atoti version policy in a specific manner to follow Semantic Versioning.

6.2.0b0

This is a preproduction release, see https://docs.activeviam.com/preproduction-releases
Released on Jul 03, 2026.

Added

Changed

  • Bumped atoti-jupyter’s minimum required JupyterLab version to 4.6.0.
  • The default error handling for atoti.CsvLoad.error_handling and atoti_jdbc.JdbcLoad.error_handling is now "fail" instead of "log" 3 4.
  • Upgraded minimum Python required version to 3.12 as per NEP 29 11.
  • Upgraded minimum Pandas required version to 3.0.3 7. Datetime and timedelta columns now use microsecond (us) precision instead of nanosecond (ns) precision, both when loading DataFrame objects into Atoti and when query results are returned as DataFrames. Arrow large_string columns are accepted on read in addition to the existing string type.
  • atoti_ai_openai.ConnectionConfig gained custom_headers, max_retries, and timeout while losing project_id. atoti_ai_openai.ChatConfig lost completions_path, http_headers, and its retry_* attributes. The connection’s base_url must now include the API path (such as /v1), and HTTP headers, retries, and timeouts are configured on the connection instead 6.
  • When AiConfig is set, the session’s MCP server now exposes an OAuth 2.1 flow by default, so MCP clients such as Claude Desktop or Claude Code can authenticate without a manually provisioned Bearer token. If the session delegates authentication to an OIDC provider (OidcConfig), the MCP server reuses it (external mode: clients authenticate against that provider, which already guards the MCP endpoint). Otherwise the MCP server acts as its own OAuth 2.1 authorization server (self-issued mode: a browser login and consent against the session’s own authentication). HTTP Basic authentication on the MCP endpoint keeps working, so programmatic and headless clients are unaffected. Override with the atoti.server.endpoint.mcp.oauth2.* server properties 5.
  • Setting atoti.Hierarchy.virtual to True on an existing hierarchy is now applied at runtime 8.
  • Upgraded Atoti UI and Atoti Admin UI to 5.2.24.
  • The Atoti UI app is served in development mode if __debug__ is True. See deployment setup.
  • AutoExplain and the chat feature are now provided by the new atoti[ai] extra (backed by the atoti-client-ai and atoti-server-ai packages). They are no longer included in the atoti[ai-amazon-bedrock] and atoti[ai-openai] extras 10. Users who use AutoExplain or chat with a provider extra must also install ai:
    - pip install "atoti[ai-amazon-bedrock]"
    + pip install "atoti[ai-amazon-bedrock,ai]"
    
  • atoti.AiConfig has moved to atoti_ai.AiConfig, provided by the new atoti-client-ai package included in the atoti[ai] extra. The ChatConfig and ConnectionConfig base classes have moved from atoti.config.ai to atoti_ai as well 10.
    - session = tt.Session(ai=tt.AiConfig())
    + from atoti_ai import AiConfig
    + session = tt.Session(ai=AiConfig())
    
  • The Parquet plugin is not installed by default anymore.

Deprecated

Removed

  • Publishing of Conda packages. Install the Python packages with uv (or pip) instead. It is still possible to use a Conda environment by installing the packages with pip:
      conda create --name my-env
      conda activate my-env
    - conda install atoti
    + pip install atoti
    
  • atoti_directquery_databricks.ConnectionConfig.array_sum_agg_function_name, atoti_directquery_databricks.ConnectionConfig.array_long_agg_function_name, atoti_directquery_databricks.ConnectionConfig.array_short_agg_function_name, and atoti_directquery_databricks.ConnectionConfig.array_sum_product_agg_function_name. Spark UDAFs API has been deprecated as it is not recommended by Databricks anymore, use :mod:array conversion <atoti.directquery.array_conversion> instead 19.
  • atoti_directquery_snowflake.ConnectionConfig.array_agg_wrapper_function_name. The built-in Snowflake ARRAY_AGG function is now faster, so the wrapper UDF is no longer needed 1.
  • atoti.QueryCube.unload_members_from_data_cube. Member unloading from a query cube has been removed 9.
  • Inferring the table name from the file path in atoti.Session.read_csv(). Pass a table_name argument instead.
  • The atoti-aws, atoti-azure, and atoti-gcp packages (and the atoti[aws], atoti[azure], and atoti[gcp] extras). Install atoti-storage-aws, atoti-storage-azure, or atoti-storage-gcp instead (i.e. replace atoti[aws] with atoti[storage-aws], atoti[azure] with atoti[storage-azure], and atoti[gcp] with atoti[storage-gcp]).
  • The atoti-server-aws, atoti-server-azure, and atoti-server-gcp packages. Install atoti-server-storage-aws, atoti-server-storage-azure, or atoti-server-storage-gcp instead.
  • Table.columns and ExternalTable.columns. Use list(table) instead.
  • table.append(rows). Use table.load(pd.DataFrame(rows, columns=list(table))) instead.
  • The Table data-loading shortcuts. Use atoti.Table.load() (or atoti.Table.stream() for Kafka) with the matching load/stream description instead:
    • table.load_csv(path)table.load(tt.CsvLoad(path))
    • table.load_pandas(dataframe)table.load(dataframe)
    • table.load_arrow(arrow_table)table.load(arrow_table)
    • table.load_numpy(array)table.load(pd.DataFrame(array, columns=list(table)))
    • table.load_parquet(path)table.load(atoti_parquet.ParquetLoad(path))
    • table.load_spark(dataframe)table.load(dataframe.toPandas())
    • table.load_kafka(...)table.stream(atoti_kafka.KafkaStream(...))
    • table.load_sql(query)table.load(atoti_jdbc.JdbcLoad(query))
  • Session.read_parquet(), Session.read_spark(), Session.read_numpy(), and Session.read_sql(). Use atoti.Session.create_table() together with atoti.Table.load() and the matching load description instead (e.g. atoti_parquet.ParquetLoad or atoti_jdbc.JdbcLoad); for Spark and NumPy, build a pandas.DataFrame and use atoti.Session.read_pandas().
  • The types parameter of atoti.Session.create_table(), read_csv(), read_pandas(), and read_arrow(). Use the data_types parameter instead.
  • The timeout parameter of atoti.Cube.query() and atoti.Session.query_mdx(). Pass a queriesTimeLimit in the context argument instead.
  • Hierarchy.levels. Iterate on the hierarchy instead.
  • The Hierarchy.visible setter. Use atoti.Hierarchy.viewers instead.
  • Deleting a Level (del cube.levels[...]). Redefine its Hierarchy instead.
  • Cube.aggregates_cache. Use atoti.Cube.aggregate_cache instead.
  • The atoti.Measure.description deleter (del measure.description). Set measure.description = "" instead.
  • Session.start_transaction(). Use session.tables.data_transaction() instead.
  • Session.port. Use atoti.Session.url instead (parsing it with urllib.parse.urlparse() if necessary).
  • session.security.restrictions. Use session.tables.restrictions instead.
  • security.basic. Use basic_authentication instead.
  • The method parameter of date_shift(). Use its fallback parameter instead.
  • The overload of copy() taking hierarchy, member_paths, and consolidation_factors. Use tt.consolidate() instead.
  • Importing ParquetLoad from atoti. Import it from atoti_parquet instead.
  • The copy_tutorial notebook script. Follow the tutorial from the online documentation instead.
  • The atoti.pyapi module. Import Request (previously exposed as HttpRequest) and atoti.User directly instead.
  • Leaving username_case_conversion unset on atoti.LdapConfig and atoti.KerberosConfig. It is now required: pass "upper" or "lower".
  • The deprecation warning emitted when atoti.AggregateProvider is given multiple levels belonging to the same hierarchy. This misconfiguration now raises a ValueError.

Fixed

  • Creating a DirectQuery cube with mode="no_measures" and a filter no longer sends unfiltered APPROX_COUNT_DISTINCT queries for virtual hierarchy detection. The cube filter is now applied to these statistics queries, preventing timeouts on large tables 12.
  • LoggingCsvHealthEventHandler no longer logs spurious errors when non-CSV health events are dispatched to it 15.
  • JdbcPingDiscoveryProtocol backed by an embedded H2 file database intermittently failing to form the cluster, because the query and data nodes, which run in separate JVMs, could not open the discovery database concurrently. AUTO_SERVER=TRUE is now enabled automatically for embedded H2 databases so that the query and data nodes can access the discovery table concurrently 17.

Security

  • AWS S3: The commitment policy is now REQUIRE_ENCRYPT_REQUIRE_DECRYPT, enforcing authenticated encryption for both reads and writes. Existing S3 data encrypted with older AWS encryption SDK clients (V1/V2) must be re-encrypted before upgrading 2.

Internal issue tracker references

0.9.14

Released on April 17, 2026.

Added

  • OpenTelemetry spans around the main functions and methods of the library. See atoti_observability for example traces.

Changed

  • Upgraded Atoti Server to 6.1.19.

Deprecated

  • atoti_directquery_databricks.ConnectionConfig.array_sum_agg_function_name, atoti_directquery_databricks.ConnectionConfig.array_long_agg_function_name, atoti_directquery_databricks.ConnectionConfig.array_short_agg_function_name, and atoti_directquery_databricks.ConnectionConfig.array_sum_product_agg_function_name. Spark UDAFs API has been deprecated as it is not recommended by Databricks anymore, use array conversion instead 19.

Fixed

Internal issue tracker references

0.9.13

Released on March 10, 2026.

Added

Changed

  • Upgraded Atoti Server to 6.1.18.
  • Upgraded Atoti UI and Atoti Admin UI to 5.2.20.
  • Renamed cloud storage packages:
    • atoti-client-awsatoti-client-storage-aws
    • atoti-server-awsatoti-server-storage-aws
    • atoti-client-azureatoti-client-storage-azure
    • atoti-server-azureatoti-server-storage-azure
    • atoti-client-gcpatoti-client-storage-gcp
    • atoti-server-gcpatoti-server-storage-gcp
    The module names have also changed:
    - uv add "atoti[aws]"
    + uv add "atoti[storage-aws]"
    
    - from atoti_aws import ClientSideEncryptionConfig
    + from atoti_storage_aws import ClientSideEncryptionConfig
    

Deprecated

  • atoti-client-aws, atoti-server-aws, and the atoti_aws module. Use atoti-client-storage-aws, atoti-server-storage-aws, and atoti_storage_aws instead.
  • atoti-client-azure, atoti-server-azure, and the atoti_azure module. Use atoti-client-storage-azure, atoti-server-storage-azure, and atoti_storage_azure instead.
  • atoti-client-gcp, atoti-server-gcp, and the atoti_gcp module. Use atoti-client-storage-gcp, atoti-server-storage-gcp, and atoti_storage_gcp instead.

Fixed

Internal issue tracker references

0.9.12

Released on January 23, 2026.

Added

Changed

  • Upgraded Atoti Server to 6.1.16.
  • Upgraded Atoti UI and Atoti Admin UI to 5.2.18.
  • Loading Apache Parquet files requires the new atoti-client-parquet and atoti-server-parquet packages 26. Installing the atoti package also installs these packages but the next breaking release will make them opt-in.
    - uv add "atoti"
    + uv add "atoti[parquet]"
    
    If you do no use Parquet files and wish to reduce the size of your dependencies, you can skip the installation of the atoti package and manually install the atoti-client and atoti-server packages instead:
    - # Also installs atoti-client-parquet and atoti-server-parquet.
    - uv add "atoti"
    + # Does not install atoti-client-parquet and atoti-server-parquet.
    + uv add "atoti-client" "atoti-server"
    
    Or, if you use some extras:
    - uv add "atoti[jdbc]"
    + uv add "atoti-client[jdbc]" "atoti-server[jdbc]"
    

Deprecated

  • atoti.ParquetLoad 26. Use atoti_parquet.ParquetLoad instead:
    - from atoti import ParquetLoad
    + from atoti_parquet import ParquetLoad
    
  • Session.read_parquet since support for Apache Parquet will become opt-in 26. Use infer_data_types(), create_table(), and load() instead:
    - table = session.read_parquet(path, table_name="Example")
    + parquet_load = ParquetLoad(path)
    + data_types = session.tables.infer_data_types(parquet_load)
    + table = session.create_table("Example", data_types=data_types)
    + table.load(parquet_load)
    
  • atoti.Session.query_mdx() and atoti.Cube.query()’s timeout parameter 27. The default value has also been changed from datetime.timedelta(seconds=30) to None to automatically use shared_context’s queriesTimeLimit . Use the context parameter to specify a custom timeout:
    - session.query_mdx(mdx, timeout=datetime.timedelta(seconds=10))
    + session.query_mdx(mdx, context={"queriesTimeLimit": 10})
    
    - cube.query(m["contributors.COUNT"], timeout=datetime.timedelta(seconds=10))
    + cube.query(m["contributors.COUNT"], context={"queriesTimeLimit": 10})
    

Fixed

  • Atoti Admin UI connecting to wrong server due to wrong order of WebJars in the server’s classpath 29.
  • Hierarchies disappearing from the Data model tree when editing the data model of a secured session 28.

Internal issue tracker references

0.9.11

Released on December 16, 2025.

Added

Changed

  • Upgraded Atoti Server to 6.1.15.
  • Upgraded Atoti UI and Atoti Admin UI to 5.2.17.
  • atoti.Measure.description returns "" instead of None 33.
  • Everything related to Security is kept in memory instead of being persisted in the user_content_storage 30. This makes the API more consistent since all the other methods on atoti.Session and its related classes never persisted their changes either. This improves performance, both when configuring these properties but also when executing queries.
    Basic authentication credentials are unaffected since, for security reasons, they were already only kept in memory.

Deprecated

  • atoti.Measure.description’s deleter 33. Set the description to a blank string instead:
    - del measure.description
    + measure.description = ""
    

Fixed

Internal issue tracker references

0.9.10

Released on October 31, 2025.

Added

Changed

  • Upgraded Atoti Server to 6.1.13.
  • Upgraded Atoti UI and Atoti Admin UI to 5.2.15.
  • Installation instructions with Conda to recommend Miniforge over Anaconda’s installer to not be tied to Anaconda’s license requirements 39.
  • Configuring branding does not remove the By ActiveViam signature at the bottom right corner of the app anymore. It is still possible to remove this signature through a custom app extension 37.

Internal issue tracker references

0.9.9.2

Released on October 20, 2025.
This version is not published on PyPI, it is only available on ActiveViam’s repository.

Added

Changed

  • Improved planning of MDX queries requesting many specific cells of the cube 40.
  • Reduce number of requests made when representing (or evaluating in a Jupyter notebook cell) cubes, levels, measures, etc of a connected session 42.

Fixed

Internal issue tracker references

0.9.9.1

Released on September 30, 2025.
This version is not published on PyPI, it is only available on ActiveViam’s repository.

Changed

  • Improved planning of MDX queries requesting many specific cells of the cube 44.

Fixed

  • ArrayIndexOutOfBoundsException when running some MDX queries 45.
  • AssertionError: Output has already been set when deleting a cube 47.
  • GraphQLClientGraphQLMultiError: Structural transaction must be committed by the thread starting it when setting atoti.Measure.visible inside a data_model_transaction() 46.

Internal issue tracker references

0.9.9

Released on September 22, 2025.

Added

Changed

Fixed

Internal issue tracker references

0.9.8

Released on August 12, 2025.

Added

Changed

  • Upgraded Atoti Server to 6.1.11.
  • When atoti.JwtConfig.key_pair is None, the automatically generated key pair will use 3072 bits instead of the old 2048 bits.

Deprecated

  • Requests to endpoints created with atoti.Session.endpoint should now use /proxy instead of /atoti/pyapi. /atoti/pyapi remains available but logs a warning when used 56.
  • The atoti.pyapi module is deprecated, import its classes directly from atoti.User or atoti.endpoint.Request instead 56.

Fixed

Internal issue tracker references

0.9.7

Released on July 01, 2025.

Added

Changed

Deprecated

Fixed

  • where() rejecting condition if it contained a ~hierarchy.isin(...) or ~level.isin(...) leaf 62.
  • atoti.finance.irr() returning nan when the actual rate was too large 64.

Internal issue tracker references

0.9.6

Released on May 17, 2025.

Added

Changed

  • Upgraded Atoti UI and Atoti Admin UI to 5.2.8.
  • Upgraded Atoti Server to 6.1.8.
  • The functions that were in the atoti.experimental subpackage have been moved outside of it and require passing their feature key to experimental():
    - tt.experimental.agg.distinct(...)
    + with tt.experimental({"agg.distinct"}):
    +   tt.agg.distinct(...)
    

Deprecated

  • Deleting a level through atoti.Cube.levels. Redefine its hierarchy instead:
    - del l["Geography", "City"]
    + h["Geography"] = [l["Continent"], l["Country"]]
    
  • atoti.date_shift()’s method parameter 71. The new parameter name is fallback:
      tt.date_shift(
        m["Price"],
        h["Date"],
    -   method="previous",
    +   fallback="past",
        offset="P1M",
      )
    

Fixed

Internal issue tracker references

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 72. 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 73. 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_parquet.ParquetLoad.path raised an InvalidPathException 77.

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 78. 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 74. 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 75.
  • Passing isnull conditions to atoti.where() raised an UnknownUnderlyingMeasureRuntimeException 76.

Internal issue tracker references

0.9.4

Released on February 28, 2025.

Distribution

The name QuerySession, unused since 0.9.0, makes a come back in this release but, this time, with a different meaning. QuerySession becomes the entry point to create clusters of Atoti applications 84. atoti.Cube.restrictions has been introduced to secure query sessions. For the sake of symmetry, atoti.Session.security.restrictions has moved to atoti.tables.Tables.restrictions (the old location remains available but is deprecated). See Scaling with distribution.

Performance

Client/server communication

Most components of Session are exposed through mappings such as Cubes or Tables. As a project grows, a lot of mapping lookups will be made to iteratively define the data model or to pass arguments to methods such as atoti.Cube.query(). For instance, the following code will make 3 lookups:
new_measure = tt.agg.sum(
    m["Foo.SUM"], # 1
    scope=tt.OriginScope({
        l["Bar"], # 2
        l["Baz"], # 3
    })
)
Each lookup makes a request to the server to check that the key exists. This is pretty quick but, when thousands of lookups are made, this client/server communication can add up. mapping_lookup() allows skipping these server requests.

Cube queries

The aggregate cache of a cube can be restricted to a subset of measures 81. This improvement comes with a new atoti.Cube.aggregate_cache API deprecating the previous atoti.Cube.aggregates_cache (with an “s”) one:
- cube.aggregates_cache.capacity = 200
+ cube.aggregate_cache = tt.AggregateCache(capacity=200)
- cube.aggregates_cache.capacity = -1
+ del cube.aggregate_cache

Hierarchy creation

Creating hierarchies from columns of a table fully joined (i.e. all their keys are mapped) to the cube’s fact table do not require “rebuilding” the cube anymore 82. This means that no time will be lost reindexing hierarchies or performing other expensive computations.

Data modeling

Measures

atoti.where() was not respecting the order of condition_to_value when the same value was assigned to multiple conditions 80. For example, in:
m["8"] = 8
m["Test"] = tt.where(
    {
        m["8"] < 5: -1, # False
        m["8"] >= 5: 1, # True
        m["8"] < 10: -1, # True
    },
    default=0,
)
Test was equal to -1 because the first and last conditions, being both assigned to the same value, were merged together and that merged condition became the first one evaluating to True. This incorrect merging of conditions has been removed: m["8"] >= 5 is correctly detected as the first condition evaluating to True and so Test is equal to 1.

Table columns

Columns with an arrray data type can be made non-nullable. atoti.Column.default_value’s documentation has been updated accordingly.

Data loading

Transactions

data_transaction()’s tables parameter allows some data transactions to execute concurrently 79.

CSV

The atoti.CsvLoad.true_values and atoti.CsvLoad.false_values attributes can be configured to parse more values than "True", "true", "False", and "false" as "boolean" 83.

Dependencies

  • Atoti Server has been upgraded to 6.1.4.
  • Atoti UI and Atoti Admin UI have been upgraded to 5.2.6.

Internal issue tracker references

0.9.3

Released on January 13, 2025.

Added

Changed

  • Upgraded Atoti Server to 6.1.3.
  • Upgraded Atoti UI and Atoti Admin UI to 5.2.4.
  • SessionConfig and its inner classes validate that passed Path attributes exist. It prevents situations such as passing a path containing a typo to extra_jars and being confused that the JAR’s classes still cannot be loaded.

Fixed

  • Session.read_parquet and ParquetLoad not being able to load Parquet files with extension other than .parquet.
  • NullPointerException returned by the server when executing some MDX queries.

0.9.2

Released on December 05, 2024.

Added

Documentation

Changed

  • Upgraded Atoti Server to 6.1.2.
  • Upgraded Atoti UI and Atoti Admin UI to 5.2.3.
  • To support remote clusters, atoti.Session.read_spark() and atoti.Table.load_spark() convert the passed Spark DataFrame to an in-memory Pandas DataFrame instead of exporting it to a Parquet file.

Deprecated

  • atoti.Table.append():
    - table.append(*rows)
    + table.load(pd.DataFrame(rows, columns=list(table)))
    
  • atoti.Table.load_arrow():
    - table.load_arrow(arrow_table)
    + table.load(arrow_table)
    
  • atoti.Table.load_csv():
    - table.load_csv(path)
    + table.load(tt.CsvLoad(path))
    
  • atoti.Table.load_kafka():
    - table.load_kafka(...)
    + from atoti_kafka import KafkaStream
    + table.stream(KafkaStream(...))
    
  • atoti.Table.load_numpy():
    - table.load_numpy(numpy_array)
    + table.load(pd.DataFrame(numpy_array, columns=list(table)))
    
  • atoti.Table.load_pandas():
    - table.load_pandas(pandas_df)
    + table.load(pandas_df)
    
  • atoti.Table.load_parquet():
    - table.load_parquet(path)
    + table.load(atoti_parquet.ParquetLoad(path))
    
  • atoti.Table.load_spark()
    - table.load_spark(spark_df)
    + table.load(spark_df.toPandas())
    
  • atoti.Table.load_sql():
    - table.load_sql(query, url=url)
    + from atoti_jdbc import JdbcLoad
    + table.load(JdbcLoad(query, url=url))
    
  • atoti.Session.read_numpy():
    - table = session.read_numpy(numpy_array, columns=columns)
    + table = session.read_pandas(pd.DataFrame(numpy_array, columns=columns))
    
  • atoti.Session.read_spark():
    - table = session.read_spark(spark_df)
    + table = session.read_pandas(spark_df.toPandas())
    
  • atoti.Session.read_sql():
    - table = session.read_sql(query, table_name=table_name, url=url)
    + from atoti_jdbc import JdbcLoad
    + jdbc_load = JdbcLoad(query, url=url)
    + data_types = session.tables.infer_data_types(jdbc_load)
    + table = session.create_table(table_name, data_types=data_types)
    + table.load(jdbc_load)
    
  • create_table(), read_arrow(), read_csv(), read_pandas(), Session.read_parquet’s types parameter. Use the data_types parameter instead.
  • Inference of read_csv() and Session.read_parquet’s table_name parameter. Pass a table_name argument instead.

Fixed

0.9.1

Released on October 18, 2024.

Added

Changed

  • Upgraded Atoti Server to 6.1.1.
  • Upgraded Atoti UI and Atoti Admin UI to 5.2.1.

Fixed

0.9.0

Released on September 13, 2024.

Added

  • atoti-directquery-jdbc to connect to an external database through JDBC.
  • data_model_transaction(). Batching measure creation with a data model transaction has the same performance as using Measures.update() without being limited to independent measures:
    - m.update({"foo": 13, "bar": 42})
    - m.update({"foo + 1": m["foo"] + 1, "bar + 1": m["bar"] + 1})
    + with session.data_model_transaction():
    +    m["foo"] = 13
    +    m["foo + 1"] = m["foo"] + 1
    +    m["bar"] = 42
    +    m["bar + 1"] = m["bar"] + 1
    
    Data model transactions also replace the private API relying on atoti.MeasureMetadata:
    - m["foo"] = (13, tt.MeasureMetadata(visible=True))
    - m["bar"] = (42, tt.MeasureMetadata(description="The answer"))
    + with session.data_model_transaction():
    +    m["foo"] = 13
    +    m["foo"].visible = True
    +    m["bar"] = 42
    +    m["bar"].description = "The answer"
    
  • atoti_directquery_redshift.ConnectionConfig.connection_pool_size.

User interface

  • Filters tool in the sidebar of the JupyterLab extension to see default filters.

Changed

Packaging

  • The atoti package and most of its plugins (e.g. atoti-aws, atoti-directquery-directquery, atoti-kafka etc.) have been split into atoti-client-* and atoti-server-* packages. The atoti-client-* packages contain the Python code composing the API while the atoti-server-* packages mostly contain the JARs implementing the corresponding features. The advanced installation section explains the goal of this split. The atoti package still exists but has become empty, it is only there to provide a convenient way to install both client and server packages. For instance:
    • pip install atoti will install both atoti-client and atoti-server . It will actually also install jdk4py. Note: jdk4py is not a dependency of atoti-server so that projects willing to use another JDK can avoid installing jdk4py by dependending on atoti-client and atoti-server directly.
    • pip install "atoti[aws]" will install atoti-client, atoti-aws-client, atoti-aws-server, and atoti-server.
    • pip install "atoti[jupyterlab]" will install atoti-client, atoti-server, and atoti-jupyterlab (no client/server split for this package because it only contains frontend assets).
    Because Conda does not support “extras”, the installation of Atoti plugins with this package manager is more complex. For instance, the command to install atoti and its AWS plugin is: conda install atoti atoti-client-aws atoti-server-aws.

Session start and configuration

  • atoti.Session.__init__() has been replaced with atoti.Session.start() for symmetry with atoti.Session.connect() (the removed section gives more details about that latter method):
    - session = tt.Session()
    + session = tt.Session.start()
    
    The top-level config parameters have been grouped into a SessionConfig dataclass providing better error reporting and allowing code reuse:
    - session = tt.Session(port=1337)
    + session = tt.Session.start(tt.SessionConfig(port=1337))
    
  • atoti.Session.__init__()’s authentication parameter has been replaced with atoti.SessionConfig.security:
      config = tt.OidcConfig(...)
    - tt.Session(authentication=config)
    + tt.Session.start(tt.SessionConfig(security=tt.SecurityConfig(sso=config)))
    
  • atoti.UserContentStorageConfig has been moved to atoti_jdbc.UserContentStorageConfig:
    - config = tt.UserContentStorageConfig(url=url)
    - tt.Session(user_content_storage=config)
    + from atoti_jdbc import UserContentStorageConfig
    + config = UserContentStorageConfig(url)
    + tt.Session.start(tt.SessionConfig(user_content_storage=config))
    
    It makes it obvious that storing user content in an external database requires atoti-jdbc to be installed.

DirectQuery

  • DirectQuery *ConnectionInfo and *TableOptions classes have been renamed ConnectionConfig and TableConfig.
    - from atoti_directquery_clickhouse import ClickhouseConnectionInfo
    + from atoti_directquery_clickhouse import ConnectionConfig
    
    - from atoti_directquery_clickhouse import ClickhouseTableOptions
    + from atoti_directquery_clickhouse import TableConfig
    
  • The cache attribute controlling whether DirectQuery connections should use caching has been moved from the connection instance to the connection config:
    - from atoti_directquery_snowflake import SnowflakeConnectionInfo
    + from atoti_directquery_snowflake import ConnectionConfig
    - connection_config = SnowflakeConnectionInfo(url=...)
    + connection_config = ConnectionConfig(url=..., cache=True)
      external_database = session.connect_to_external_database(connection_config)
    - external_database.cache = True
    
  • The DatabricksConnectionInfo.heavy_load_url attribute has been renamed feeding_url.

Other

  • Upgraded jdk4py dependency to 21.0.4 which adds support for Linux Arm64.
  • atoti_aws.AwsKeyPair, atoti_aws.AwsKmsConfig, and atoti_azure.AzureKeyPair have been renamed KeyPair, KmsConfig, and KeyPair.
  • The atoti-sql package has been renamed atoti-jdbc.
  • atoti.Session.explain_mdx_query() and atoti.Cube.explain_query() have been replaced with an explain parameter to atoti.Session.query_mdx() and atoti.Cube.query():
    - session.explain_mdx_query(mdx)
    + session.query_mdx(mdx, explain=True)
    
  • atoti.Table.keys returns a tuple instead of a list. It communicates that keys cannot be changed once the table exists.
  • create_cube()’s base_table parameter has been renamed fact_table.

User interface

  • Upgraded Atoti UI and Admin UI to 5.2.0.

Deprecated

  • atoti.Session.port. Use atoti.Session.url instead:
    - url = f"http://localhost:{session.port}"
    + url = session.url
    
    - port = session.port
    + from urllib.parse import urlparse
    + port = urlparse(session.url).port
    
  • atoti.Session.start_transaction(). Use atoti.tables.Tables.data_transaction() instead:
    - with session.start_transaction(): ...
    + with session.tables.data_model_transaction(): ...
    
  • atoti.Session.security.basic. Use basic_authentication:
    - session.security.basic.credentials
    + session.security.basic_authentication.credentials
    
  • atoti.Table.columns and atoti.ExternalTable.columns. Use list(table) to list column names and for column_name in table: ... to iterate on column names:
    - column_names = table.columns
    + column_names = list(table)
    
  • atoti.Hierarchy.levels. Iterate on the Hierarchy instead:
    - level_names = list(h["Geography"].levels)
    + level_names = list(h["Geography"])
    
    - h["Date parts"] = {**h["Date parts"].levels, "Date": table["Date"]}
    + h["Date parts"] = {**h["Date parts"], "Date": table["Date"]}
    

Removed

  • Support for Java 17, 18, 19, and 20.
  • The atoti-query package and its QuerySession class. Use atoti.Session.connect() instead:
    - pip install atoti-query
    + pip install atoti-client
    
    - from atoti_query import QuerySession
    + from atoti as tt
    - existing_session = QuerySession(url)
    + existing_session = tt.Session.connect(url)
      existing_session.query_mdx(...)
    
  • atoti.Table.__len__(). It was ambiguous because it could be interpreted as counting either rows or columns. Instead, use atoti.Table.row_count to count rows and len(list(table)) to count columns:
    - row_count = len(table)
    + row_count = table.row_count
    

Previously deprecated