From 9916b996e52f91416bffb854187d243a846709ec Mon Sep 17 00:00:00 2001 From: Tishj Date: Wed, 17 Sep 2025 10:14:10 +0200 Subject: [PATCH 1/3] add hash method --- src/duckdb_py/include/duckdb_python/pytype.hpp | 1 + src/duckdb_py/typing/pytype.cpp | 5 +++++ tests/fast/test_type.py | 14 ++++++++++++++ 3 files changed, 20 insertions(+) diff --git a/src/duckdb_py/include/duckdb_python/pytype.hpp b/src/duckdb_py/include/duckdb_python/pytype.hpp index a6e13dfd..6d1e8074 100644 --- a/src/duckdb_py/include/duckdb_python/pytype.hpp +++ b/src/duckdb_py/include/duckdb_python/pytype.hpp @@ -30,6 +30,7 @@ class DuckDBPyType : public enable_shared_from_this { public: bool Equals(const shared_ptr &other) const; + ssize_t Hash() const; bool EqualsString(const string &type_str) const; shared_ptr GetAttribute(const string &name) const; py::list Children() const; diff --git a/src/duckdb_py/typing/pytype.cpp b/src/duckdb_py/typing/pytype.cpp index 009e3dab..01357ad3 100644 --- a/src/duckdb_py/typing/pytype.cpp +++ b/src/duckdb_py/typing/pytype.cpp @@ -46,6 +46,10 @@ bool DuckDBPyType::Equals(const shared_ptr &other) const { return type == other->type; } +ssize_t DuckDBPyType::Hash() const { + return py::hash(py::str(ToString())); +} + bool DuckDBPyType::EqualsString(const string &type_str) const { return StringUtil::CIEquals(type.ToString(), type_str); } @@ -328,6 +332,7 @@ void DuckDBPyType::Initialize(py::handle &m) { type_module.def("__repr__", &DuckDBPyType::ToString, "Stringified representation of the type object"); type_module.def("__eq__", &DuckDBPyType::Equals, "Compare two types for equality", py::arg("other"), py::is_operator()); type_module.def("__eq__", &DuckDBPyType::EqualsString, "Compare two types for equality", py::arg("other"), py::is_operator()); + type_module.def("__hash__", &DuckDBPyType::Hash, "Hashes the type, equal to stringifying+hashing"); type_module.def_property_readonly("id", &DuckDBPyType::GetId); type_module.def_property_readonly("children", &DuckDBPyType::Children); type_module.def(py::init<>([](const string &type_str, shared_ptr connection = nullptr) { diff --git a/tests/fast/test_type.py b/tests/fast/test_type.py index 6f648179..c5a62694 100644 --- a/tests/fast/test_type.py +++ b/tests/fast/test_type.py @@ -214,6 +214,20 @@ def test_struct_from_dict(self): res = duckdb.list_type({'a': VARCHAR, 'b': VARCHAR}) assert res == 'STRUCT(a VARCHAR, b VARCHAR)[]' + def test_hash_method(self): + type1 = duckdb.list_type({'a': VARCHAR, 'b': VARCHAR}) + type2 = duckdb.list_type({'b': VARCHAR, 'a': VARCHAR}) + type3 = VARCHAR + + type_set = set() + type_set.add(type1) + type_set.add(type2) + type_set.add(type3) + + type_set.add(type1) + expected = ['STRUCT(a VARCHAR, b VARCHAR)[]', 'STRUCT(b VARCHAR, a VARCHAR)[]', 'VARCHAR'] + assert sorted([str(x) for x in list(type_set)]) == expected + # NOTE: we can support this, but I don't think going through hoops for an outdated version of python is worth it @pytest.mark.skipif(sys.version_info < (3, 9), reason="python3.7 does not store Optional[..] in a recognized way") def test_optional(self): From ed6b2c5ac4b9f39905867affaf9299cd8a8448db Mon Sep 17 00:00:00 2001 From: Tishj Date: Wed, 17 Sep 2025 12:23:48 +0200 Subject: [PATCH 2/3] avoid collision with windows define --- src/duckdb_py/include/duckdb_python/pytype.hpp | 2 +- src/duckdb_py/typing/pytype.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/duckdb_py/include/duckdb_python/pytype.hpp b/src/duckdb_py/include/duckdb_python/pytype.hpp index 6d1e8074..fced489e 100644 --- a/src/duckdb_py/include/duckdb_python/pytype.hpp +++ b/src/duckdb_py/include/duckdb_python/pytype.hpp @@ -30,7 +30,7 @@ class DuckDBPyType : public enable_shared_from_this { public: bool Equals(const shared_ptr &other) const; - ssize_t Hash() const; + ssize_t HashType() const; bool EqualsString(const string &type_str) const; shared_ptr GetAttribute(const string &name) const; py::list Children() const; diff --git a/src/duckdb_py/typing/pytype.cpp b/src/duckdb_py/typing/pytype.cpp index 35ff81a9..91a95d91 100644 --- a/src/duckdb_py/typing/pytype.cpp +++ b/src/duckdb_py/typing/pytype.cpp @@ -46,7 +46,7 @@ bool DuckDBPyType::Equals(const shared_ptr &other) const { return type == other->type; } -ssize_t DuckDBPyType::Hash() const { +ssize_t DuckDBPyType::HashType() const { return py::hash(py::str(ToString())); } @@ -334,7 +334,7 @@ void DuckDBPyType::Initialize(py::handle &m) { py::is_operator()); type_module.def("__eq__", &DuckDBPyType::EqualsString, "Compare two types for equality", py::arg("other"), py::is_operator()); - type_module.def("__hash__", &DuckDBPyType::Hash, "Hashes the type, equal to stringifying+hashing"); + type_module.def("__hash__", &DuckDBPyType::HashType, "Hashes the type, equal to stringifying+hashing"); type_module.def_property_readonly("id", &DuckDBPyType::GetId); type_module.def_property_readonly("children", &DuckDBPyType::Children); type_module.def(py::init<>([](const string &type_str, shared_ptr connection = nullptr) { From f3ab971c029624ce4efc20cb5cfa7730cd21afb4 Mon Sep 17 00:00:00 2001 From: Tishj Date: Wed, 17 Sep 2025 16:36:40 +0200 Subject: [PATCH 3/3] third attempt at making windows happy --- src/duckdb_py/include/duckdb_python/pytype.hpp | 1 - src/duckdb_py/typing/pytype.cpp | 8 +++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/duckdb_py/include/duckdb_python/pytype.hpp b/src/duckdb_py/include/duckdb_python/pytype.hpp index fced489e..a6e13dfd 100644 --- a/src/duckdb_py/include/duckdb_python/pytype.hpp +++ b/src/duckdb_py/include/duckdb_python/pytype.hpp @@ -30,7 +30,6 @@ class DuckDBPyType : public enable_shared_from_this { public: bool Equals(const shared_ptr &other) const; - ssize_t HashType() const; bool EqualsString(const string &type_str) const; shared_ptr GetAttribute(const string &name) const; py::list Children() const; diff --git a/src/duckdb_py/typing/pytype.cpp b/src/duckdb_py/typing/pytype.cpp index 91a95d91..f04c14ba 100644 --- a/src/duckdb_py/typing/pytype.cpp +++ b/src/duckdb_py/typing/pytype.cpp @@ -46,10 +46,6 @@ bool DuckDBPyType::Equals(const shared_ptr &other) const { return type == other->type; } -ssize_t DuckDBPyType::HashType() const { - return py::hash(py::str(ToString())); -} - bool DuckDBPyType::EqualsString(const string &type_str) const { return StringUtil::CIEquals(type.ToString(), type_str); } @@ -334,7 +330,9 @@ void DuckDBPyType::Initialize(py::handle &m) { py::is_operator()); type_module.def("__eq__", &DuckDBPyType::EqualsString, "Compare two types for equality", py::arg("other"), py::is_operator()); - type_module.def("__hash__", &DuckDBPyType::HashType, "Hashes the type, equal to stringifying+hashing"); + type_module.def("__hash__", [](const DuckDBPyType &type) { + return py::hash(py::str(type.ToString())); + }); type_module.def_property_readonly("id", &DuckDBPyType::GetId); type_module.def_property_readonly("children", &DuckDBPyType::Children); type_module.def(py::init<>([](const string &type_str, shared_ptr connection = nullptr) {