Skip to content

SuiteFactory.delete(...) raises a TypeException when using a DatabaseStoreBackend #11318

@smcl

Description

@smcl

Describe the bug
When you configure an ExpectationStore with a DatabaseStoreBackend and try to delete a suite, a TypeException is raised.

To Reproduce

I've got a very small project which demonstrates this in action at https://github.com/smcl/gx-dbstore-delete-issue - you just need to ensure you have great-expectations 1.5.x and sqlalchemy installed.

But basically the code looks like the below

import great_expectations as gx
context = gx.get_context(mode="file")
context.suites.add(gx.core.expectation_suite.ExpectationSuite(name="test_suite"))
context.suites.delete("test_suite")

With a very "vanilla" great_expectations.yml with an expectations_store configured as follows:

  expectations_store:
    # class_name: ExpectationsStore
    # store_backend:
    #   class_name: TupleFilesystemStoreBackend
    #   base_directory: expectations/
    class_name: ExpectationsStore
    store_backend:
      class_name: DatabaseStoreBackend
      credentials:
        drivername: sqlite
        database: ./gx.db

When you run the script you'll encounter the following Exception:

    for key_col, val in zip(self.key_columns, key)
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: 'ExpectationSuiteIdentifier' object is not iterable

Expected behavior
The provided app.py script should terminate without any errors.

Environment (please complete the following information):

  • Operating System: Windows
  • Great Expectations Version: 1.5.6
  • Data Source: n/a
  • Cloud environment: n/a

Additional context

I looked into the GX sources to see if I was doing something stupid. The TypeError is raised inside DatabaseStoreBackend.remove_key which reports that "'ExpectationSuiteIdentifier' object is not iterable" (code is here) - object in question is the key parameter below which we pass into zip(...):

    def remove_key(self, key):  # type: ignore[explicit-override] # FIXME
        delete_statement = self._table.delete().where(
            sa.and_(
                *(
                    getattr(self._table.columns, key_col) == val
                    for key_col, val in zip(self.key_columns, key)
                )
            )
        )
        # ... removed

There is no type annotation for key here, but this code appears to believe that key is a collection that can be iterated over ... but in this case it is called indirectly from SuiteFactory.delete here:

    key = self._store.get_key(name=suite.name, id=suite.id)
    self._store.remove_key(key=key)

That get_key method has a type annotation indicating that it returns either GXCloudIdentifier | ExpectationSuiteIdentifier - neither of these types are iterable so it will obviously cause an exception when it is then passed as an argument into zip().

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions