diff --git a/CHANGELOG.md b/CHANGELOG.md
index c584b9c..0e66ae1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,7 +6,8 @@
 * Add ``async with`` support to ``BaseCache``.
 * Remove deprecated ``loop`` parameters.
 * Remove deprecated ``cache`` parameter from ``create()``.
-* Use ``str()`` in ``_build_key()`` to ensure consistency of enum keys between different Python versions (if using enum keys, upgrading to 0.12 may invalidate existing caches due to key values changing).
+* Use ``base._ensure_key()`` in ``_build_key()`` to ensure consistency of enum
+keys between different Python versions. [#633](https://github.com/aio-libs/aiocache/issues/633) -- Padraic Shafer
 * Improved support for ``build_key(key, namespace)`` [#569](https://github.com/aio-libs/aiocache/issues/569) - Padraic Shafer
     * `BaseCache.build_key` uses `namespace` argument if provided,
     otherwise it uses `self.namespace`.
diff --git a/aiocache/backends/redis.py b/aiocache/backends/redis.py
index d569ec2..407efec 100644
--- a/aiocache/backends/redis.py
+++ b/aiocache/backends/redis.py
@@ -4,7 +4,7 @@ import warnings
 import redis.asyncio as redis
 from redis.exceptions import ResponseError as IncrbyException
 
-from aiocache.base import BaseCache
+from aiocache.base import BaseCache, _ensure_key
 from aiocache.serializers import JsonSerializer
 
 
@@ -219,11 +219,12 @@ class RedisCache(RedisBackend):
         return options
 
     def _build_key(self, key, namespace=None):
-        # TODO(PY311): Remove str()
         if namespace is not None:
-            return "{}{}{}".format(namespace, ":" if namespace else "", str(key))
+            return "{}{}{}".format(
+                namespace, ":" if namespace else "", _ensure_key(key))
         if self.namespace is not None:
-            return "{}{}{}".format(self.namespace, ":" if self.namespace else "", str(key))
+            return "{}{}{}".format(
+                self.namespace, ":" if self.namespace else "", _ensure_key(key))
         return key
 
     def __repr__(self):  # pragma: no cover
diff --git a/aiocache/base.py b/aiocache/base.py
index bc4df81..54e9d18 100644
--- a/aiocache/base.py
+++ b/aiocache/base.py
@@ -3,6 +3,7 @@ import functools
 import logging
 import os
 import time
+from enum import Enum
 from types import TracebackType
 from typing import Callable, Optional, Set, Type
 
@@ -498,14 +499,10 @@ class BaseCache:
         pass
 
     def _build_key(self, key, namespace=None):
-        # TODO(PY311): Remove str() calls.
-        # str() is needed to ensure consistent results when using enums between
-        # Python 3.11+ and older releases due to changed __format__() method:
-        # https://docs.python.org/3/whatsnew/3.11.html#enum
         if namespace is not None:
-            return "{}{}".format(namespace, str(key))
+            return "{}{}".format(namespace, _ensure_key(key))
         if self.namespace is not None:
-            return "{}{}".format(self.namespace, str(key))
+            return "{}{}".format(self.namespace, _ensure_key(key))
         return key
 
     def _get_ttl(self, ttl):
@@ -553,5 +550,12 @@ class _Conn:
         return _do_inject_conn
 
 
+def _ensure_key(key):
+    if isinstance(key, Enum):
+        return key.value
+    else:
+        return key
+
+
 for cmd in API.CMDS:
     setattr(_Conn, cmd.__name__, _Conn._inject_conn(cmd.__name__))

diff --git a/tests/acceptance/test_decorators.py b/tests/acceptance/test_decorators.py
index 13783db..2f755a1 100644
--- a/tests/acceptance/test_decorators.py
+++ b/tests/acceptance/test_decorators.py
@@ -5,6 +5,7 @@ from unittest import mock
 import pytest
 
 from aiocache import cached, cached_stampede, multi_cached
+from aiocache.base import _ensure_key
 from ..utils import Keys
 
 
@@ -137,17 +138,16 @@ class TestMultiCachedDecorator:
         assert await cache.exists(Keys.KEY) is True
 
     async def test_multi_cached_key_builder(self, cache):
-        # TODO(PY311): Remove str() calls
         def build_key(key, f, self, keys, market="ES"):
-            return "{}_{}_{}".format(f.__name__, str(key), market)
+            return "{}_{}_{}".format(f.__name__, _ensure_key(key), market)
 
         @multi_cached(keys_from_attr="keys", key_builder=build_key)
         async def fn(self, keys, market="ES"):
             return {Keys.KEY: 1, Keys.KEY_1: 2}
 
         await fn("self", keys=[Keys.KEY, Keys.KEY_1])
-        assert await cache.exists("fn_" + str(Keys.KEY) + "_ES") is True
-        assert await cache.exists("fn_" + str(Keys.KEY_1) + "_ES") is True
+        assert await cache.exists("fn_" + _ensure_key(Keys.KEY) + "_ES") is True
+        assert await cache.exists("fn_" + _ensure_key(Keys.KEY_1) + "_ES") is True
 
     async def test_fn_with_args(self, cache):
         @multi_cached("keys")
diff --git a/tests/ut/backends/test_memcached.py b/tests/ut/backends/test_memcached.py
index 5f856b4..7e01161 100644
--- a/tests/ut/backends/test_memcached.py
+++ b/tests/ut/backends/test_memcached.py
@@ -4,7 +4,7 @@ import aiomcache
 import pytest
 
 from aiocache.backends.memcached import MemcachedBackend, MemcachedCache
-from aiocache.base import BaseCache
+from aiocache.base import BaseCache, _ensure_key
 from aiocache.serializers import JsonSerializer
 from ...utils import Keys
 
@@ -249,8 +249,7 @@ class TestMemcachedCache:
 
     @pytest.mark.parametrize(
         "namespace, expected",
-        # TODO(PY311): Remove str()
-        ([None, "test" + str(Keys.KEY)], ["", str(Keys.KEY)], ["my_ns", "my_ns" + str(Keys.KEY)]),  # type: ignore[attr-defined]  # noqa: B950
+        ([None, "test" + _ensure_key(Keys.KEY)], ["", _ensure_key(Keys.KEY)], ["my_ns", "my_ns" + _ensure_key(Keys.KEY)]),  # type: ignore[attr-defined]  # noqa: B950
     )
     def test_build_key_bytes(self, set_test_namespace, memcached_cache, namespace, expected):
         assert memcached_cache.build_key(Keys.KEY, namespace=namespace) == expected.encode()
diff --git a/tests/ut/backends/test_redis.py b/tests/ut/backends/test_redis.py
index dd41522..2470584 100644
--- a/tests/ut/backends/test_redis.py
+++ b/tests/ut/backends/test_redis.py
@@ -5,7 +5,7 @@ from redis.asyncio.client import Pipeline
 from redis.exceptions import ResponseError
 
 from aiocache.backends.redis import RedisBackend, RedisCache
-from aiocache.base import BaseCache
+from aiocache.base import BaseCache, _ensure_key
 from aiocache.serializers import JsonSerializer
 from ...utils import Keys
 
@@ -253,8 +253,7 @@ class TestRedisCache:
 
     @pytest.mark.parametrize(
         "namespace, expected",
-        # TODO(PY311): Remove str()
-        ([None, "test:" + str(Keys.KEY)], ["", str(Keys.KEY)], ["my_ns", "my_ns:" + str(Keys.KEY)]),  # noqa: B950
+        ([None, "test:" + _ensure_key(Keys.KEY)], ["", _ensure_key(Keys.KEY)], ["my_ns", "my_ns:" + _ensure_key(Keys.KEY)]),  # noqa: B950
     )
     def test_build_key_double_dot(self, set_test_namespace, redis_cache, namespace, expected):
         assert redis_cache.build_key(Keys.KEY, namespace=namespace) == expected
diff --git a/tests/ut/test_base.py b/tests/ut/test_base.py
index 09b36dd..569b524 100644
--- a/tests/ut/test_base.py
+++ b/tests/ut/test_base.py
@@ -4,7 +4,7 @@ from unittest.mock import ANY, AsyncMock, MagicMock, patch
 
 import pytest
 
-from aiocache.base import API, BaseCache, _Conn
+from aiocache.base import API, BaseCache, _Conn, _ensure_key
 from ..utils import Keys
 
 
@@ -205,8 +205,7 @@ class TestBaseCache:
 
     @pytest.mark.parametrize(
         "namespace, expected",
-        # TODO(PY311): Remove str()
-        ([None, "test" + str(Keys.KEY)], ["", str(Keys.KEY)], ["my_ns", "my_ns" + str(Keys.KEY)]),  # type: ignore[attr-defined]  # noqa: B950
+        ([None, "test" + _ensure_key(Keys.KEY)], ["", _ensure_key(Keys.KEY)], ["my_ns", "my_ns" + _ensure_key(Keys.KEY)]),  # type: ignore[attr-defined]  # noqa: B950
     )
     def test_build_key(self, set_test_namespace, base_cache, namespace, expected):
         assert base_cache.build_key(Keys.KEY, namespace=namespace) == expected
@@ -219,18 +218,16 @@ class TestBaseCache:
     def alt_base_cache(self, init_namespace="test"):
         """Custom key_builder for cache"""
         def build_key(key, namespace=None):
-            # TODO(PY311): Remove str()
             ns = namespace if namespace is not None else ""
             sep = ":" if namespace else ""
-            return f"{ns}{sep}{str(key)}"
+            return f"{ns}{sep}{_ensure_key(key)}"
 
         cache = BaseCache(key_builder=build_key, namespace=init_namespace)
         return cache
 
     @pytest.mark.parametrize(
         "namespace, expected",
-        # TODO(PY311): Remove str()
-        ([None, str(Keys.KEY)], ["", str(Keys.KEY)], ["my_ns", "my_ns:" + str(Keys.KEY)]),  # type: ignore[attr-defined]  # noqa: B950
+        ([None, _ensure_key(Keys.KEY)], ["", _ensure_key(Keys.KEY)], ["my_ns", "my_ns:" + _ensure_key(Keys.KEY)]),  # type: ignore[attr-defined]  # noqa: B950
     )
     def test_alt_build_key_override_namespace(self, alt_base_cache, namespace, expected):
         """Custom key_builder overrides namespace of cache"""
@@ -239,8 +236,7 @@ class TestBaseCache:
 
     @pytest.mark.parametrize(
         "init_namespace, expected",
-        # TODO(PY311): Remove str()
-        ([None, str(Keys.KEY)], ["", str(Keys.KEY)], ["test", "test:" + str(Keys.KEY)]),  # type: ignore[attr-defined]  # noqa: B950
+        ([None, _ensure_key(Keys.KEY)], ["", _ensure_key(Keys.KEY)], ["test", "test:" + _ensure_key(Keys.KEY)]),  # type: ignore[attr-defined]  # noqa: B950
     )
     async def test_alt_build_key_default_namespace(
             self, init_namespace, alt_base_cache, expected):

Ensure that underlying value of enum key is used by BaseCache._build_key() across Python versions
Key builders and tests currently cast key values (notably `Enum` values) to `str` to maintain compatibility with Python 3.11 and older versions.  ~~This requirement can be eliminated by using f-strings rather than `str.__format__`.~~

EDIT: Whoops, that erroneous statement was based on testing in the wrong python environment.  Instead of using `str()`, perhaps we should use a simple guard that check for an enum key.  Something like:

```
def ensure_key(key):
    if isinstance(key, Enum):
        return key.value
    else:
        return key
```
tests/acceptance/test_decorators.py::TestCached::test_cached_ttl[memory_cache] tests/acceptance/test_decorators.py::TestCached::test_cached_key_builder[memory_cache] tests/acceptance/test_decorators.py::TestCached::test_cached_without_namespace[memory_cache] tests/acceptance/test_decorators.py::TestCached::test_cached_with_namespace[memory_cache] tests/acceptance/test_decorators.py::TestCachedStampede::test_cached_stampede[memory_cache] tests/acceptance/test_decorators.py::TestCachedStampede::test_locking_dogpile_lease_expiration[memory_cache] tests/acceptance/test_decorators.py::TestCachedStampede::test_locking_dogpile_task_cancellation[memory_cache] tests/acceptance/test_decorators.py::TestMultiCachedDecorator::test_multi_cached[memory_cache] tests/acceptance/test_decorators.py::TestMultiCachedDecorator::test_keys_without_kwarg[memory_cache] tests/acceptance/test_decorators.py::TestMultiCachedDecorator::test_multi_cached_key_builder[memory_cache] tests/acceptance/test_decorators.py::TestMultiCachedDecorator::test_fn_with_args[memory_cache] tests/acceptance/test_decorators.py::TestMultiCachedDecorator::test_double_decorator[memory_cache] tests/ut/backends/test_memcached.py::TestMemcachedBackend::test_setup tests/ut/backends/test_memcached.py::TestMemcachedBackend::test_setup_override tests/ut/backends/test_memcached.py::TestMemcachedBackend::test_setup_casts tests/ut/backends/test_memcached.py::TestMemcachedBackend::test_get tests/ut/backends/test_memcached.py::TestMemcachedBackend::test_gets tests/ut/backends/test_memcached.py::TestMemcachedBackend::test_get_none tests/ut/backends/test_memcached.py::TestMemcachedBackend::test_get_no_encoding tests/ut/backends/test_memcached.py::TestMemcachedBackend::test_set tests/ut/backends/test_memcached.py::TestMemcachedBackend::test_set_float_ttl tests/ut/backends/test_memcached.py::TestMemcachedBackend::test_set_cas_token tests/ut/backends/test_memcached.py::TestMemcachedBackend::test_cas tests/ut/backends/test_memcached.py::TestMemcachedBackend::test_cas_fail tests/ut/backends/test_memcached.py::TestMemcachedBackend::test_multi_get tests/ut/backends/test_memcached.py::TestMemcachedBackend::test_multi_get_none tests/ut/backends/test_memcached.py::TestMemcachedBackend::test_multi_get_no_encoding tests/ut/backends/test_memcached.py::TestMemcachedBackend::test_multi_set tests/ut/backends/test_memcached.py::TestMemcachedBackend::test_multi_set_float_ttl tests/ut/backends/test_memcached.py::TestMemcachedBackend::test_add tests/ut/backends/test_memcached.py::TestMemcachedBackend::test_add_existing tests/ut/backends/test_memcached.py::TestMemcachedBackend::test_add_float_ttl tests/ut/backends/test_memcached.py::TestMemcachedBackend::test_exists tests/ut/backends/test_memcached.py::TestMemcachedBackend::test_increment tests/ut/backends/test_memcached.py::TestMemcachedBackend::test_increment_negative tests/ut/backends/test_memcached.py::TestMemcachedBackend::test_increment_missing tests/ut/backends/test_memcached.py::TestMemcachedBackend::test_increment_missing_negative tests/ut/backends/test_memcached.py::TestMemcachedBackend::test_increment_typerror tests/ut/backends/test_memcached.py::TestMemcachedBackend::test_expire tests/ut/backends/test_memcached.py::TestMemcachedBackend::test_delete tests/ut/backends/test_memcached.py::TestMemcachedBackend::test_delete_missing tests/ut/backends/test_memcached.py::TestMemcachedBackend::test_clear tests/ut/backends/test_memcached.py::TestMemcachedBackend::test_clear_with_namespace tests/ut/backends/test_memcached.py::TestMemcachedBackend::test_raw tests/ut/backends/test_memcached.py::TestMemcachedBackend::test_raw_bytes tests/ut/backends/test_memcached.py::TestMemcachedBackend::test_redlock_release tests/ut/backends/test_memcached.py::TestMemcachedBackend::test_close tests/ut/backends/test_memcached.py::TestMemcachedCache::test_name tests/ut/backends/test_memcached.py::TestMemcachedCache::test_inheritance tests/ut/backends/test_memcached.py::TestMemcachedCache::test_default_serializer tests/ut/backends/test_memcached.py::TestMemcachedCache::test_parse_uri_path tests/ut/backends/test_memcached.py::TestMemcachedCache::test_build_key_bytes[None-testkey] tests/ut/backends/test_memcached.py::TestMemcachedCache::test_build_key_bytes[-key] tests/ut/backends/test_memcached.py::TestMemcachedCache::test_build_key_bytes[my_ns-my_nskey] tests/ut/backends/test_memcached.py::TestMemcachedCache::test_build_key_no_namespace tests/ut/backends/test_memcached.py::TestMemcachedCache::test_build_key_no_spaces tests/ut/backends/test_redis.py::TestRedisBackend::test_setup tests/ut/backends/test_redis.py::TestRedisBackend::test_setup_override tests/ut/backends/test_redis.py::TestRedisBackend::test_setup_casts tests/ut/backends/test_redis.py::TestRedisBackend::test_get tests/ut/backends/test_redis.py::TestRedisBackend::test_gets tests/ut/backends/test_redis.py::TestRedisBackend::test_set tests/ut/backends/test_redis.py::TestRedisBackend::test_set_cas_token tests/ut/backends/test_redis.py::TestRedisBackend::test_cas tests/ut/backends/test_redis.py::TestRedisBackend::test_cas_float_ttl tests/ut/backends/test_redis.py::TestRedisBackend::test_multi_get tests/ut/backends/test_redis.py::TestRedisBackend::test_multi_set tests/ut/backends/test_redis.py::TestRedisBackend::test_multi_set_with_ttl tests/ut/backends/test_redis.py::TestRedisBackend::test_add tests/ut/backends/test_redis.py::TestRedisBackend::test_add_existing tests/ut/backends/test_redis.py::TestRedisBackend::test_add_float_ttl tests/ut/backends/test_redis.py::TestRedisBackend::test_exists tests/ut/backends/test_redis.py::TestRedisBackend::test_increment tests/ut/backends/test_redis.py::TestRedisBackend::test_increment_typerror tests/ut/backends/test_redis.py::TestRedisBackend::test_expire tests/ut/backends/test_redis.py::TestRedisBackend::test_expire_0_ttl tests/ut/backends/test_redis.py::TestRedisBackend::test_delete tests/ut/backends/test_redis.py::TestRedisBackend::test_clear tests/ut/backends/test_redis.py::TestRedisBackend::test_clear_no_keys tests/ut/backends/test_redis.py::TestRedisBackend::test_clear_no_namespace tests/ut/backends/test_redis.py::TestRedisBackend::test_raw tests/ut/backends/test_redis.py::TestRedisBackend::test_redlock_release tests/ut/backends/test_redis.py::TestRedisBackend::test_close tests/ut/backends/test_redis.py::TestRedisCache::test_name tests/ut/backends/test_redis.py::TestRedisCache::test_inheritance tests/ut/backends/test_redis.py::TestRedisCache::test_default_serializer tests/ut/backends/test_redis.py::TestRedisCache::test_parse_uri_path[-expected0] tests/ut/backends/test_redis.py::TestRedisCache::test_parse_uri_path[/-expected1] tests/ut/backends/test_redis.py::TestRedisCache::test_parse_uri_path[/1-expected2] tests/ut/backends/test_redis.py::TestRedisCache::test_parse_uri_path[/1/2/3-expected3] tests/ut/backends/test_redis.py::TestRedisCache::test_build_key_double_dot[None-test:key] tests/ut/backends/test_redis.py::TestRedisCache::test_build_key_double_dot[-key] tests/ut/backends/test_redis.py::TestRedisCache::test_build_key_double_dot[my_ns-my_ns:key] tests/ut/backends/test_redis.py::TestRedisCache::test_build_key_no_namespace tests/ut/test_base.py::TestAPI::test_register tests/ut/test_base.py::TestAPI::test_unregister tests/ut/test_base.py::TestAPI::test_unregister_unexisting tests/ut/test_base.py::TestAPI::test_aiocache_enabled tests/ut/test_base.py::TestAPI::test_aiocache_enabled_disabled tests/ut/test_base.py::TestAPI::test_timeout_no_timeout tests/ut/test_base.py::TestAPI::test_timeout_self tests/ut/test_base.py::TestAPI::test_timeout_kwarg_0 tests/ut/test_base.py::TestAPI::test_timeout_kwarg_None tests/ut/test_base.py::TestAPI::test_timeout_kwarg tests/ut/test_base.py::TestAPI::test_timeout_self_kwarg tests/ut/test_base.py::TestAPI::test_plugins tests/ut/test_base.py::TestBaseCache::test_str_ttl tests/ut/test_base.py::TestBaseCache::test_str_timeout tests/ut/test_base.py::TestBaseCache::test_add tests/ut/test_base.py::TestBaseCache::test_get tests/ut/test_base.py::TestBaseCache::test_set tests/ut/test_base.py::TestBaseCache::test_multi_get tests/ut/test_base.py::TestBaseCache::test_multi_set tests/ut/test_base.py::TestBaseCache::test_delete tests/ut/test_base.py::TestBaseCache::test_exists tests/ut/test_base.py::TestBaseCache::test_increment tests/ut/test_base.py::TestBaseCache::test_expire tests/ut/test_base.py::TestBaseCache::test_clear tests/ut/test_base.py::TestBaseCache::test_raw tests/ut/test_base.py::TestBaseCache::test_close tests/ut/test_base.py::TestBaseCache::test_acquire_conn tests/ut/test_base.py::TestBaseCache::test_release_conn tests/ut/test_base.py::TestBaseCache::test_build_key[None-testkey] tests/ut/test_base.py::TestBaseCache::test_build_key[-key] tests/ut/test_base.py::TestBaseCache::test_build_key[my_ns-my_nskey] tests/ut/test_base.py::TestBaseCache::test_alt_build_key tests/ut/test_base.py::TestBaseCache::test_alt_build_key_override_namespace[None-key] tests/ut/test_base.py::TestBaseCache::test_alt_build_key_override_namespace[-key] tests/ut/test_base.py::TestBaseCache::test_alt_build_key_override_namespace[my_ns-my_ns:key] tests/ut/test_base.py::TestBaseCache::test_alt_build_key_default_namespace[None-key] tests/ut/test_base.py::TestBaseCache::test_alt_build_key_default_namespace[-key] tests/ut/test_base.py::TestBaseCache::test_alt_build_key_default_namespace[test-test:key] tests/ut/test_base.py::TestBaseCache::test_add_ttl_cache_default tests/ut/test_base.py::TestBaseCache::test_add_ttl_default tests/ut/test_base.py::TestBaseCache::test_add_ttl_overriden tests/ut/test_base.py::TestBaseCache::test_add_ttl_none tests/ut/test_base.py::TestBaseCache::test_set_ttl_cache_default tests/ut/test_base.py::TestBaseCache::test_set_ttl_default tests/ut/test_base.py::TestBaseCache::test_set_ttl_overriden tests/ut/test_base.py::TestBaseCache::test_set_ttl_none tests/ut/test_base.py::TestBaseCache::test_multi_set_ttl_cache_default tests/ut/test_base.py::TestBaseCache::test_multi_set_ttl_default tests/ut/test_base.py::TestBaseCache::test_multi_set_ttl_overriden tests/ut/test_base.py::TestBaseCache::test_multi_set_ttl_none tests/ut/test_base.py::TestCache::test_get tests/ut/test_base.py::TestCache::test_get_timeouts tests/ut/test_base.py::TestCache::test_get_default tests/ut/test_base.py::TestCache::test_get_negative_default tests/ut/test_base.py::TestCache::test_set tests/ut/test_base.py::TestCache::test_set_timeouts tests/ut/test_base.py::TestCache::test_add tests/ut/test_base.py::TestCache::test_add_timeouts tests/ut/test_base.py::TestCache::test_mget tests/ut/test_base.py::TestCache::test_mget_timeouts tests/ut/test_base.py::TestCache::test_mset tests/ut/test_base.py::TestCache::test_mset_timeouts tests/ut/test_base.py::TestCache::test_exists tests/ut/test_base.py::TestCache::test_exists_timeouts tests/ut/test_base.py::TestCache::test_increment tests/ut/test_base.py::TestCache::test_increment_timeouts tests/ut/test_base.py::TestCache::test_delete tests/ut/test_base.py::TestCache::test_delete_timeouts tests/ut/test_base.py::TestCache::test_expire tests/ut/test_base.py::TestCache::test_expire_timeouts tests/ut/test_base.py::TestCache::test_clear tests/ut/test_base.py::TestCache::test_clear_timeouts tests/ut/test_base.py::TestCache::test_raw tests/ut/test_base.py::TestCache::test_raw_timeouts tests/ut/test_base.py::TestCache::test_close tests/ut/test_base.py::TestCache::test_get_connection tests/ut/test_base.py::TestConn::test_conn tests/ut/test_base.py::TestConn::test_conn_getattr tests/ut/test_base.py::TestConn::test_conn_context_manager tests/ut/test_base.py::TestConn::test_inject_conn