Skip to content

Implement Pure Python AVL Tree Backend, Update Benchmarks & Myths FAQ#21

Merged
AnshMNSoni merged 2 commits into
mainfrom
v1.1.7
Jul 1, 2026
Merged

Implement Pure Python AVL Tree Backend, Update Benchmarks & Myths FAQ#21
AnshMNSoni merged 2 commits into
mainfrom
v1.1.7

Conversation

@AnshMNSoni

Copy link
Copy Markdown
Owner

Description

This PR resolves the behavioral inconsistency and benchmarking discrepancy between the C++ (Rust-compiled) backend and the pure Python fallback backend for sorted containers (stl_set and stl_map).

Problem

Previously, the Python implementations wrapped built-in set and dict objects. Because Python's built-ins are hash tables, they offered O(1) complexity and unordered iteration, whereas the Rust/C++ implementations used B-Trees with O(log n) complexity and sorted iteration. This resulted in behavioral inconsistencies (elements were sorted when Rust was compiled, but unsorted when it fell back to Python) and an unequal "apples-to-oranges" benchmark comparison.

Solution

  1. AVL Tree Implementation: Created a core self-balancing AVL Tree in pure Python to serve as the fallback backend for both set and map containers.
  2. Backend Unification: Modified the private set and map implementation wrappers to use this AVL tree, ensuring O(log n) time complexity and sorted iteration on both Rust and Python backends.
  3. 3-Way Benchmark: Expanded the benchmark script to support a three-way comparison (Rust Backend vs. Pure Python Backend vs. Native Python Built-ins) to clearly document FFI boundary crossing costs and algorithmic tradeoffs.
  4. README FAQ: Updated the README to address common myths regarding project utility (competitive programming compatibility) and the systems design benefits of the hybrid Rust-Python architecture.
  5. Version Bump: Bumped versioning to 1.1.7 across configuration files.

Changes

  • [NEW] pythonstl/core/avl_tree.py: Self-balancing BST containing node insertion, balancing, deletions, and sorted in-order iterators.
  • [MODIFY] pythonstl/implementations/associative/_set_impl.py: Replaced native set fallback with AVLTree.
  • [MODIFY] pythonstl/implementations/associative/_map_impl.py: Replaced native dict fallback with AVLTree.
  • [MODIFY] benchmarks/benchmark_all_structures.py: Added native built-in baseline and expanded the summary reporting table.
  • [MODIFY] README.md: Added the 3-way container benchmark table and the Myths & Common Misconceptions FAQ.
  • [MODIFY] pythonstl/init.py & pyproject.toml: Bumped version to 1.1.7.

Verification

  • Added test cases in tests/test_set.py and tests/test_map.py to verify that both backends maintain elements in sorted order.
  • Ran pytest suite: All 83 tests passed successfully.

@AnshMNSoni AnshMNSoni self-assigned this Jul 1, 2026
@AnshMNSoni AnshMNSoni added bug Something isn't working enhancement New feature or request labels Jul 1, 2026
@deepsource-io

deepsource-io Bot commented Jul 1, 2026

Copy link
Copy Markdown

DeepSource Code Review

We reviewed changes in dfd1cb2...623d428 on this pull request. Below is the summary for the review, and you can see the individual issues we found as inline review comments.

See full review on DeepSource ↗

PR Report Card

Overall Grade   Security  

Reliability  

Complexity  

Hygiene  

Code Review Summary

Analyzer Status Updated (UTC) Details
C & C++ Jul 1, 2026 9:29a.m. Review ↗
Rust Jul 1, 2026 9:29a.m. Review ↗
Python Jul 1, 2026 9:29a.m. Review ↗

Important

AI Review is run only on demand for your team. We're only showing results of static analysis review right now. To trigger AI Review, comment @deepsourcebot review on this thread.

self.height: int = 1


class AVLTree(Generic[K, V]):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implementing __eq__ without also implementing __hash__


In order to conform to the object model, classes that define their own equality method should also define their own hash method, or be unhashable. If the hash method is not defined then the hash of the super class is used. This is unlikely to result in the expected behavior. A class can be made unhashable by setting its __hash__ attribute to None.

"""Check if a key exists in the tree."""
return self._find(self.root, key) is not None

def _find(self, node: Optional[AVLNode[K, V]], key: K) -> Optional[AVLNode[K, V]]:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Method doesn't use the class instance and could be converted into a static method


The method doesn't use its bound instance. Decorate this method with @staticmethod decorator, so that Python does not have to instantiate a bound method for every instance of this class thereby saving memory and computation. Read more about staticmethods here.

raise KeyError(key)
return node.value

def _get_height(self, node: Optional[AVLNode[K, V]]) -> int:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Method doesn't use the class instance and could be converted into a static method


The method doesn't use its bound instance. Decorate this method with @staticmethod decorator, so that Python does not have to instantiate a bound method for every instance of this class thereby saving memory and computation. Read more about staticmethods here.

"""Insert or update a key-value pair in the tree."""
self.root = self._insert(self.root, key, value)

def _insert(self, node: Optional[AVLNode[K, V]], key: K, value: V) -> AVLNode[K, V]:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

`AVLTree._insert` has a cyclomatic complexity of 16 with "high" risk


A function with high cyclomatic complexity can be hard to understand and
maintain. Cyclomatic complexity is a software metric that measures the number of
independent paths through a function. A higher cyclomatic complexity indicates
that the function has more decision points and is more complex.

if key in self:
self.root = self._delete(self.root, key)

def _min_value_node(self, node: AVLNode[K, V]) -> AVLNode[K, V]:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Method doesn't use the class instance and could be converted into a static method


The method doesn't use its bound instance. Decorate this method with @staticmethod decorator, so that Python does not have to instantiate a bound method for every instance of this class thereby saving memory and computation. Read more about staticmethods here.

Comment thread tests/test_map.py
assert m.at(2) == "two"
assert m.find(3) is True

def test_sorted_order(self):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Method doesn't use the class instance and could be converted into a static method


The method doesn't use its bound instance. Decorate this method with @staticmethod decorator, so that Python does not have to instantiate a bound method for every instance of this class thereby saving memory and computation. Read more about staticmethods here.

Comment thread tests/test_set.py
s.erase("banana")
assert s.find("banana") is False

def test_sorted_order(self):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Method doesn't use the class instance and could be converted into a static method


The method doesn't use its bound instance. Decorate this method with @staticmethod decorator, so that Python does not have to instantiate a bound method for every instance of this class thereby saving memory and computation. Read more about staticmethods here.

@AnshMNSoni AnshMNSoni merged commit 257943b into main Jul 1, 2026
5 of 6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant