Skip to content

google-cloud-firestore: Accept values of generic Mapping type (e.g. Python 3.15 frozendict) in addition to dict as field values when creating / replacing / merging a document in the Firestore database #18297

Description

@edgarrmondragon

Determine this is the right repository

  • I determined this is the correct repository in which to report this feature request.

Summary of the feature request

I'd like to be able to use other dictionary types, custom ones and frozendict (Python 3.15+), when adding or updating documents in Firestore without having to create new dict objects by wrapping my instance (dict(my_mapping)).

Looking at the code, the input value doesn't need to be mutable at all (as dict is), and it only needs to support .items(), so it really can be any valid collections.abc.Mapping:

def encode_dict(values_dict) -> dict:
"""Encode a dictionary into protobuf ``Value``-s.
Args:
values_dict (dict): The dictionary to encode as protobuf fields.
Returns:
Dict[str, ~google.cloud.firestore_v1.types.Value]: A
dictionary of string keys and ``Value`` protobufs as dictionary
values.
"""
return {key: encode_value(value) for key, value in values_dict.items()}

That means the code here

if isinstance(value, dict):
value_dict = encode_dict(value)
value_pb = document.MapValue(fields=value_dict)
return document.Value(map_value=value_pb)

could be simply checking

if isinstance(value, collections.abc.Mapping):
    ...

Desired code experience

from collections import UserDict

class UpperCaseDict(UserDict):
    def __setitem__(self, key, item):
        key = key.upper()
        super().__setitem__(key, item)

record = {"my_dict": UpperCaseDict({"one": 1, "two": 2})}

db = firestore.client(...)
db.collection("cities").add(record)  # no errors

Expected results

No errors for valid mapping types:

raise TypeError(
"Cannot convert to a Firestore Value", value, "Invalid type", type(value)
)

API client name and version

google-cloud-firestore v2.30.0

Use case

Uploading my custom mapping instances directly without conversion.

Additional context

A few valid mapping types currently cause an exception:

from collections import UserDict, defaultdict
from collections.abc import Mapping
from types import MappingProxyType


class UpperCaseDict(UserDict):
    def __setitem__(self, key, item):
        key = key.upper()
        super().__setitem__(key, item)


d = {"one": 1, "two": 2}
dd = defaultdict(int, d)
proxy = MappingProxyType(d)
fd = frozendict(one=1, two=2)  # Python 3.15+
uppercase = UpperCaseDict({"one": 1, "two": 2})

print(isinstance(d, Mapping))  # True
print(isinstance(d, dict))  # True

print(isinstance(dd, Mapping))  # True
print(isinstance(dd, dict))  # True

print(isinstance(proxy, Mapping))  # True
print(isinstance(proxy, dict))  # False

print(isinstance(fd, Mapping))  # True
print(isinstance(fd, dict))  # False

print(isinstance(uppercase, Mapping))  # True
print(isinstance(uppercase, dict))  # False

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    triage meI really want to be triaged.type: feature request‘Nice-to-have’ improvement, new feature or different behavior or design.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions