2. API Reference

This section describes the external API of the nocasedict project. Any internal symbols and APIs are omitted.

2.1. Class NocaseDict

class nocasedict.NocaseDict(*args, **kwargs)[source]

A case-insensitive and case-preserving ordered dictionary.

The dictionary is case-insensitive: When items of the dictionary are looked up by key or keys are compared by the dictionary, that is done case-insensitively. The case-insensitivity is defined by performing the lookup or comparison on the result of the __casefold__() method on the key value. None is allowed as a key value and will not be case folded.

The dictionary is case-preserving: When keys are returned, they have the lexical case that was originally specified when adding or updating the item.

The dictionary is ordered: The dictionary maintains the order in which items were added for all Python versions supported by this package. This is consistent with the ordering behavior of the built-in dict class starting with Python 3.7.

The NocaseDict class is derived from the abstract base class collections.abc.MutableMapping and not from the dict class, because of the unique implementation of NocaseDict, which maintains a single dictionary with the casefolded keys, and values that are tuples (original key, value). This supports key based lookup with a single dictionary lookup. Users that need to test whether an object is a dictionary should do that with isinstance(obj, Mapping) or isinstance(obj, MutableMapping).

The provided key and value objects will be referenced from the dictionary without being copied, consistent with the built-in dict class.

Except for the case-insensitivity of its keys, the NocaseDict class behaves like the built-in dict class starting with Python 3.7 (where it is guaranteed to be ordered), so its documentation applies completely.

The NocaseDict class itself provides no added functionality compared to the built-in dict class. This package provides mixin classes for adding functionality:

  • HashableMixin mixin class: Adds case-insensitive hashability.

  • KeyableByMixin() mixin generator function: Adds ability to get the key from an attribute of the value object.

Example of usage:

from nocasedict import NocaseDict

dict1 = NocaseDict({'Alpha': 1, 'Beta': 2})

print(dict1['ALPHA'])  # Lookup by key is case-insensitive
# 1

print(dict1)  # Access of keys is case-preserving
# NocaseDict({'Alpha': 1, 'Beta': 2})
Parameters
  • *args

    An optional single positional argument representing key-value pairs to initialize the dictionary from, in iteration order of the specified object. The argument must be one of:

    • a dictionary object, or more specifically an object that has a method keys() providing iteration through the keys and that supports subscription by key (e.g. ncd[key]) for accessing the values.

    • an iterable. If a key occurs more than once (case-insensitively), the last item for that key becomes the corresponding item in the dictionary. Each item in the iterable must be one of:

      • an iterable with exactly two items. The first item is used as the key, and the second item as the value.

      • an object with a key attribute, if the KeyableByMixin() mixin generator function is used. The value of the key attribute is used as the key, and the object itself as the value.

  • **kwargs

    Optional keyword arguments representing key-value pairs to add to the dictionary after being initialized from the positional argument.

    If a key being added is already present (case-insensitively) from the positional argument, key and value will be updated from the keyword argument.

    Before Python 3.7, the order of keyword arguments as specified in the call to the method was not guaranteed to be preserved for the method implementation, so passing more than one keyword argument may have resulted in arbitrary order of items in the dictionary.

To summarize, only the following types of init arguments are guaranteed to preserve the order of provided items after having been added to the new dictionary, across all Python versions supported by this package:

  • Passing an iterable as a single positional argument, and passing at most one keyword argument.

  • Passing an ordered dictionary/mapping as a single positional argument, and passing at most one keyword argument.

A UserWarning will be issued if the order of provided items in the arguments is not guaranteed to be preserved.

Examples for initializing:

from nocasedict import NocaseDict

dict1 = NocaseDict({'Alpha': 1, 'Beta': 2})
dict2 = NocaseDict(dict1)
dict3 = NocaseDict([('Alpha', 1), ('Beta', 2)])
dict4 = NocaseDict((('Alpha', 1), ('Beta', 2)))
dict5 = NocaseDict(Alpha=1, Beta=2)
dict6 = NocaseDict(dict1, BETA=3)
Raises
  • TypeError – Expected at most 1 positional argument, got {n}.

  • ValueError – Cannot unpack positional argument item #{i}.

Methods

clear

Remove all items from the dictionary.

copy

Return a copy of the dictionary.

fromkeys

Return a new NocaseDict object with keys from the specified iterable of keys, and values all set to the specified value.

get

Return the value of the item with an existing key (looked up case-insensitively), or if the key does not exist, a default value.

items

Return a view on the dictionary items in dictionary iteration order, where each item is a tuple of its key (in the original lexical case) and its value.

keys

Return a view on the dictionary keys (in the original lexical case) in dictionary iteration order.

keys_nocase

Return a view on the casefolded dictionary keys in dictionary iteration order.

pop

Remove the item with the specified key if it exists (looked up case-insensitively), and return its value.

popitem

Remove the last dictionary item (in iteration order) and return it as a tuple (key, value).

setdefault

If an item with the key (looked up case-insensitively) does not exist, add an item with that key and the specified default value, and return the value of the item with the key.

update

Update the dictionary from key/value pairs.

values

Return a view on the dictionary values in dictionary iteration order.

Attributes

Details

static __casefold__(key: AnyStr) AnyStr[source]

This method implements the case-insensitive behavior of the class.

It returns a case-insensitive form of the input key by calling a “casefold method” on the key. The input key will not be None.

The casefold method called by this method is str.casefold(). If that method does not exist on the key value (e.g. because it is a byte string), bytes.lower() is called, for compatibility with earlier versions of the package.

This method can be overridden by users in order to change the case-insensitive behavior of the class. See Overriding the default casefold method for details.

Parameters

key (AnyStr) – Input key. Will not be None.

Returns

Case-insensitive form of the input key.

Return type

AnyStr

Raises

AttributeError – The key does not have the casefold method.

__contains__(key: Any) bool[source]

Return a boolean indicating whether the dictionary contains an item with the key (looked up case-insensitively).

Invoked when using: key in ncd

Raises

AttributeError – The key does not have the casefold method.

__delitem__(key: Optional[AnyStr]) None[source]

Delete the item with an existing key (looked up case-insensitively).

Invoked when using: del ncd[key]

Raises
  • AttributeError – The key does not have the casefold method.

  • KeyError – Key does not exist (case-insensitively).

__eq__(other: Any) bool[source]

Return a boolean indicating whether the dictionary and the other dictionary are equal, by matching items (case-insensitively) based on their keys, and then comparing the values of matching items for equality.

The other dictionary may be a NocaseDict object or any other mapping. In all cases, the matching of keys takes place case-insensitively.

Invoked when using e.g.: ncd == other

Raises

AttributeError – The key does not have the casefold method.

__getitem__(key: Optional[AnyStr]) Any[source]

Return the value of the item with an existing key (looked up case-insensitively).

Invoked when using e.g.: value = ncd[key]

Raises
  • AttributeError – The key does not have the casefold method.

  • KeyError – Key does not exist (case-insensitively).

__iter__() Iterator[Optional[AnyStr]][source]

Return an iterator through the dictionary keys (in the original lexical case) in dictionary iteration order.

Invoked when using: for key in ncd

__len__() int[source]

Return the number of items in the dictionary.

Invoked when using: len(ncd)

__ne__(other: Any) bool[source]

Return a boolean indicating whether the dictionary and the other dictionary are not equal, by negating the equality test.

The other dictionary may be a NocaseDict object or any other mapping. In all cases, the matching of keys takes place case-insensitively.

Invoked when using e.g.: ncd != other

Raises

AttributeError – The key does not have the casefold method.

__repr__() str[source]

Return a string representation of the dictionary that is suitable for debugging.

The order of items is in dictionary iteration order, and the keys are in the original lexical case.

Invoked when using e.g.: repr(ncd)

__reversed__() Iterator[Any][source]

Return an iterator for the reversed iteration order of the dictionary.

Invoked when using: reversed[ncd]

__setitem__(key: Optional[AnyStr], value: Any) None[source]

Update the value of the item with an existing key (looked up case-insensitively), or if an item with the key does not exist, add an item with the specified key and value.

Invoked when using e.g.: ncd[key] = value

Raises

AttributeError – The key does not have the casefold method.

clear() None[source]

Remove all items from the dictionary.

copy() NocaseDict[source]

Return a copy of the dictionary.

This is a middle-deep copy; the copy is independent of the original in all attributes that have mutable types except for:

  • The values in the dictionary

Note that the Python functions copy.copy() and copy.deepcopy() can be used to create completely shallow or completely deep copies of objects of this class.

classmethod fromkeys(iterable, value=None) NocaseDict[source]

Return a new NocaseDict object with keys from the specified iterable of keys, and values all set to the specified value.

Raises

AttributeError – The key does not have the casefold method.

get(key: Optional[AnyStr], default=None) Any[source]

Return the value of the item with an existing key (looked up case-insensitively), or if the key does not exist, a default value.

Raises

AttributeError – The key does not have the casefold method.

items() dict_items[source]

Return a view on the dictionary items in dictionary iteration order, where each item is a tuple of its key (in the original lexical case) and its value.

See Dictionary View Objects on for details about view objects.

keys() dict_keys[source]

Return a view on the dictionary keys (in the original lexical case) in dictionary iteration order.

See Dictionary View Objects for details about view objects.

keys_nocase() KeysView[source]

Return a view on the casefolded dictionary keys in dictionary iteration order.

See Dictionary View Objects for details about view objects.

pop(key: ~typing.Optional[AnyStr], default=<object object>) Any[source]

Remove the item with the specified key if it exists (looked up case-insensitively), and return its value.

If an item with the key does not exist, the default value is returned if specified, otherwise KeyError is raised.

Raises

KeyError – Key does not exist (case-insensitively) and no default was specified.

popitem() Tuple[Optional[AnyStr], Any][source]

Remove the last dictionary item (in iteration order) and return it as a tuple (key, value).

The last item in iteration order is the last item that was added to the dictionary.

Raises

KeyError – Dictionary is empty.

setdefault(key: Optional[AnyStr], default=None) Any[source]

If an item with the key (looked up case-insensitively) does not exist, add an item with that key and the specified default value, and return the value of the item with the key.

Raises

AttributeError – The key does not have the casefold method.

update(*args, **kwargs) None[source]

Update the dictionary from key/value pairs.

If a key is already present in the dictionary (looked up case-insensitively), its key and value is updated (without affecting its position in the dictionary iteration order). Otherwise, an item with the key and value is added to the dictionary.

The provided key and value objects will be referenced from the dictionary without being copied, consistent with the built-in dict class.

Parameters
  • *args

    An optional single positional argument representing key-value pairs to update the dictionary from, in iteration order of the specified object. The argument must be one of:

    • a dictionary object, or more specifically an object that has a method keys() providing iteration through the keys and that supports subscription by key for accessing the values.

    • an iterable. If a key occurs more than once (case-insensitively), the last item for that key becomes the corresponding item in the dictionary. Each item in the iterable must be one of:

      • an iterable with exactly two items. The first item is used as the key, and the second item as the value.

      • an object with a key attribute, if the KeyableByMixin() mixin generator function is used. The value of the key attribute is used as the key, and the object itself as the value.

  • **kwargs

    Optional keyword arguments representing key-value pairs to update the dictionary from, after having processed the positional argument.

    Before Python 3.7, the order of keyword arguments as specified in the call to the method was not guaranteed to be preserved for the method implementation, so passing more than one keyword argument may have resulted in arbitrary order of items in the dictionary.

Raises
  • AttributeError – The key does not have the casefold method.

  • TypeError – Expected at most 1 positional argument, got {n}.

  • ValueError – Cannot unpack positional argument item #{i}.

values() dict_values[source]

Return a view on the dictionary values in dictionary iteration order.

See Dictionary View Objects for details about view objects.

2.2. Mixin class HashableMixin

class nocasedict.HashableMixin(*args, **kwargs)[source]

A mixin class that adds case-insensitive hashability to nocasedict.NocaseDict.

The derived class inheriting from HashableMixin must (directly or indirectly) inherit from NocaseDict.

Hashability allows objects of the derived class to be used as keys of dict and members of set, because these data structures use the hash values internally.

The hash value calculated by this mixin class uses the hash values of the keys and values of the dictionary in such a way that the hash value of the key is case-insensitive (i.e. it does not change for different lexical cases of a key), and the hash value of the dictionary is order-insensitive (i.e. it does not change for a different order of items).

Since NocaseDict objects are mutable, reliable use of the hash value requires that no items in the dictionary are added, removed or updated, while the dictionary object is used as a key (in another dictionary) or as a set member.

See hashable for more details.

Example:

from nocasedict import NocaseDict, HashableMixin

class MyDict(HashableMixin, NocaseDict):
    pass

mykey1 = MyDict(a=1, b=2)
mykey2 = MyDict(B=2, A=1)  # case- and order-insensitively equal

dict1 = {mykey1: 'foo'}  # Add item using first key

print(dict1[mykey2])  # Access item using second key
# 'foo'
Parameters
  • *args

    An optional single positional argument representing key-value pairs to initialize the dictionary from, in iteration order of the specified object. The argument must be one of:

    • a dictionary object, or more specifically an object that has a method keys() providing iteration through the keys and that supports subscription by key (e.g. ncd[key]) for accessing the values.

    • an iterable. If a key occurs more than once (case-insensitively), the last item for that key becomes the corresponding item in the dictionary. Each item in the iterable must be one of:

      • an iterable with exactly two items. The first item is used as the key, and the second item as the value.

      • an object with a key attribute, if the KeyableByMixin() mixin generator function is used. The value of the key attribute is used as the key, and the object itself as the value.

  • **kwargs

    Optional keyword arguments representing key-value pairs to add to the dictionary after being initialized from the positional argument.

    If a key being added is already present (case-insensitively) from the positional argument, key and value will be updated from the keyword argument.

    Before Python 3.7, the order of keyword arguments as specified in the call to the method was not guaranteed to be preserved for the method implementation, so passing more than one keyword argument may have resulted in arbitrary order of items in the dictionary.

To summarize, only the following types of init arguments are guaranteed to preserve the order of provided items after having been added to the new dictionary, across all Python versions supported by this package:

  • Passing an iterable as a single positional argument, and passing at most one keyword argument.

  • Passing an ordered dictionary/mapping as a single positional argument, and passing at most one keyword argument.

A UserWarning will be issued if the order of provided items in the arguments is not guaranteed to be preserved.

Examples for initializing:

from nocasedict import NocaseDict

dict1 = NocaseDict({'Alpha': 1, 'Beta': 2})
dict2 = NocaseDict(dict1)
dict3 = NocaseDict([('Alpha', 1), ('Beta', 2)])
dict4 = NocaseDict((('Alpha', 1), ('Beta', 2)))
dict5 = NocaseDict(Alpha=1, Beta=2)
dict6 = NocaseDict(dict1, BETA=3)
Raises
  • TypeError – Expected at most 1 positional argument, got {n}.

  • ValueError – Cannot unpack positional argument item #{i}.

Methods

clear

Remove all items from the dictionary.

copy

Return a copy of the dictionary.

fromkeys

Return a new NocaseDict object with keys from the specified iterable of keys, and values all set to the specified value.

get

Return the value of the item with an existing key (looked up case-insensitively), or if the key does not exist, a default value.

items

Return a view on the dictionary items in dictionary iteration order, where each item is a tuple of its key (in the original lexical case) and its value.

keys

Return a view on the dictionary keys (in the original lexical case) in dictionary iteration order.

keys_nocase

Return a view on the casefolded dictionary keys in dictionary iteration order.

pop

Remove the item with the specified key if it exists (looked up case-insensitively), and return its value.

popitem

Remove the last dictionary item (in iteration order) and return it as a tuple (key, value).

setdefault

If an item with the key (looked up case-insensitively) does not exist, add an item with that key and the specified default value, and return the value of the item with the key.

update

Update the dictionary from key/value pairs.

values

Return a view on the dictionary values in dictionary iteration order.

Attributes

Details

__hash__() Any[source]

Return a case-insensitive and order-insensitive hash value for the dictionary.

2.3. Mixin generator function KeyableByMixin()

nocasedict.KeyableByMixin(key_attr: str) Type[source]

A generator function returning a mixin class that adds the ability to the nocasedict.NocaseDict class to initialize or update the dictionary from an iterable of objects, whereby a particular attribute of each object is used as the key.

This simplifies the initialization of dictionaries because simple lists or tuples of such objects can be provided.

The derived class inheriting from the returned mixin class must (directly or indirectly) inherit from NocaseDict.

Example:

from nocasedict import NocaseDict, KeyableByMixin

class MyDict(KeyableByMixin('name'), NocaseDict):
    pass

class Obj(object):
    def __init__(self, name, thing):
        self.name = name  # Will be used as the key
        self.thing = thing

md = MyDict([Obj('A', 1), Obj('B', 2)])

print(md)
# MyDict({'A': <__main__.Obj object at 0x10bc3d820>,
#         'B': <__main__.Obj object at 0x10bc89af0>})