immutable-views - Immutable views on other collection objects

Introduction

The immutable-views package provides collection classes that are immutable views on other (mutable) collection objects:

An important behavior of views is that they are “live”: Since the view classes delegate to the original collection, any modification of the original collection object will be visible in the view object.

Creating an immutable view on a collection does not copy the collection and is therefore much faster than creating an immutable copy of the collection.

This is useful if a method or function maintains data in form of a mutable collection and is intended to return that data but users should not be able to modify the data. The original collection can be updated by the method or function as needed, but the caller only gets an immutable view on it.

The view classes in the immutable-views package implement the complete behavior of the corresponding Python collection types except for any operations that would modify the collection object.

The view classes delegate all operations to the original collection object they are a view of. Therefore, the original collection can be any kind of collection implementation (i.e. not just the original Python collection classes).

Note that the immutability of the view objects only applies to the view object itself, but not to the items shown by the view. So if the original collection contains mutable objects, they will still be mutable when accessed through the view objects. However, since string types are immutable in Python, for example the common case of a dictionary with string-typed keys and string-typed values results in complete immutability of the dictionary and its items.

Example with dictionaries:

$ python
>>> from immutable_views import DictView
>>> dict1 = {'a': 1, 'b': 2}
>>> dictview1 = DictView(dict1)

# Read-only access to the view is supported:
>>> dictview1['a']
1

# Modifying the view is rejected:
>>> dictview1['a'] = 2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'DictView' object does not support item assignment

# Modifications of the original collection are visible in the view:
>>> dict1['a'] = 2
>>> dictview1['a']
2

Example with lists:

$ python
>>> from immutable_views import ListView
>>> list1 = ['a', 'b']
>>> listview1 = ListView(list1)

# Read-only access to the view is supported:
>>> listview1[0]
'a'

# Modifying the view is rejected:
>>> listview1[0] = 'c'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'ListView' object does not support item assignment

# Modifications of the original collection are visible in the view:
>>> list1[0] = 'c'
>>> listview1[0]
'c'

Example with sets:

$ python
>>> from immutable_views import SetView
>>> set1 = {'a', 'b'}
>>> setview1 = SetView(set1)

# Read-only access to the view is supported:
>>> 'a' in setview1
True

# Modifying the view is rejected:
>>> setview1.add('c')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'SetView' object has no attribute 'add'

# Modifications of the original collection are visible in the view:
>>> set1.add('c')
>>> 'c' in setview1
True

Note that there are several packages on Pypi that provide immutable collections, but they all are collections on their own, and not views on other collections. Here is a notable subset of such packages:

Installation

Supported environments

The package does not have any dependencies on the type of operating system and is regularly tested in CI systems on the following operating systems:

  • Ubuntu, native Windows, CygWin, OS-X / macOS

The package is supported on the following Python versions:

  • Python: 2.7, 3.4 and all higher 3.x versions

Installing

  • Prerequisites:

    • The Python environment into which you want to install must be the current Python environment, and must have at least the following Python packages installed:

      • setuptools

      • wheel

      • pip

  • Install the immutable-views package and its prerequisite Python packages into the active Python environment:

    $ pip install immutable-views
    

Installing a different version

The examples in the previous sections install the latest version of immutable-views that is released on PyPI. This section describes how different versions of immutable-views can be installed.

  • To install an older released version of immutable-views, Pip supports specifying a version requirement. The following example installs immutable-views version 0.1.0 from PyPI:

    $ pip install immutable-views==0.1.0
    
  • If you need to get a certain new functionality or a new fix that is not yet part of a version released to PyPI, Pip supports installation from a Git repository. The following example installs immutable-views from the current code level in the master branch of the immutable-views repository:

    $ pip install git+https://github.com/andy-maier/immutable-views.git@master#egg=immutable-views
    

Verifying the installation

You can verify that immutable-views is installed correctly by importing the package into Python (using the Python environment you installed it to):

$ python -c "import immutable-views; print('ok')"
ok

Compatibility and deprecation policy

The immutable-views project uses the rules of Semantic Versioning 2.0.0 for compatibility between versions, and for deprecations. The public interface that is subject to the semantic versioning rules and specificically to its compatibility rules are the APIs and commands described in this documentation.

The semantic versioning rules require backwards compatibility for new minor versions (the ‘N’ in version ‘M.N.P’) and for new patch versions (the ‘P’ in version ‘M.N.P’).

Thus, a user of an API or command of the immutable-views project can safely upgrade to a new minor or patch version of the immutable-views package without encountering compatibility issues for their code using the APIs or for their scripts using the commands.

In the rare case that exceptions from this rule are needed, they will be documented in the Change log.

Occasionally functionality needs to be retired, because it is flawed and a better but incompatible replacement has emerged. In the immutable-views project, such changes are done by deprecating existing functionality, without removing it immediately.

The deprecated functionality is still supported at least throughout new minor or patch releases within the same major release. Eventually, a new major release may break compatibility by removing deprecated functionality.

Any changes at the APIs or commands that do introduce incompatibilities as defined above, are described in the Change log.

Deprecation of functionality at the APIs or commands is communicated to the users in multiple ways:

  • It is described in the documentation of the API or command

  • It is mentioned in the change log.

  • It is raised at runtime by issuing Python warnings of type DeprecationWarning (see the Python warnings module).

Since Python 2.7, DeprecationWarning messages are suppressed by default. They can be shown for example in any of these ways:

  • By specifying the Python command line option: -W default

  • By invoking Python with the environment variable: PYTHONWARNINGS=default

It is recommended that users of the immutable-views project run their test code with DeprecationWarning messages being shown, so they become aware of any use of deprecated functionality.

Here is a summary of the deprecation and compatibility policy used by the immutable-views project, by version type:

  • New patch version (M.N.P -> M.N.P+1): No new deprecations; no new functionality; backwards compatible.

  • New minor release (M.N.P -> M.N+1.0): New deprecations may be added; functionality may be extended; backwards compatible.

  • New major release (M.N.P -> M+1.0.0): Deprecated functionality may get removed; functionality may be extended or changed; backwards compatibility may be broken.

Python namespaces

This documentation describes only the external APIs of the immutable-views project, and omits any internal symbols and any sub-modules.

API Reference

This section describes the Python API of the immutable-views package. The API is kept stable using the compatibility rules defined for semantic versioning. An exception to this rule are fixes for security issues.

Any functions not described in this section are considered internal and may change incompatibly without warning.

DictView class

class immutable_views.DictView(a_dict)[source]

An immutable dictionary view.

Derived from Mapping.

This class provides an immutable view on a possibly mutable mapping object. The mapping object must be an instance of Mapping.

This can be used for example when a class maintains a dictionary that should be made available to users of the class without allowing them to modify the dictionary.

In the description of this class, the term ‘view’ always refers to the DictView object, and the term ‘dictionary’ or ‘original dictionary’ refers to the mapping object the view is based on.

The DictView class supports the complete behavior of Python class dict, except for any methods that would modify the dictionary. Note that the non-modifying methods of class dict are a superset of the methods defined for the abstract class Mapping (the methods are listed in the table at the top of the linked page).

The view is “live”: Since the view class delegates all operations to the original dictionary, any modification of the original dictionary object will be visible in the view object.

Note that only the view object is immutable, not its items. So if the values in the original dictionary are mutable objects, they can be modified through the view.

Note that in Python, augmented assignment (e.g. += is not guaranteed to modify the left hand object in place, but can result in a new object. For details, see object.__iadd__(). The += operator on a left hand object that is a DictView object results in a new DictView object on a new dictionary object.

Parameters

a_dict (Mapping) – The original dictionary. If this object is a DictView, its original dictionary is used.

Methods:

__contains__(key)

value in self: Return a boolean indicating whether the dictionary contains a value.

__eq__(other)

self == other: Return a boolean indicating whether the dictionary is equal to the other dictionary.

__ge__(other)

self >= other: Return a boolean indicating whether the dictionary is an inclusive superset of the other dictionary.

__getitem__(key)

self[key]: Return the value of the dictionary item with an existing key.

__gt__(other)

self > other: Return a boolean indicating whether the dictionary is a proper superset of the other dictionary.

__iter__()

Return an iterator through the dictionary keys.

__le__(other)

self <= other: Return a boolean indicating whether the dictionary is an inclusive subset of the other dictionary.

__len__()

len(self): Return the number of items in the dictionary.

__lt__(other)

self < other: Return a boolean indicating whether the dictionary is a proper subset of the other dictionary.

__ne__(other)

self != other: Return a boolean indicating whether the dictionary is not equal to the other dictionary.

__repr__()

repr(self): Return a string representation of the view suitable for debugging.

__reversed__()

reversed(self) ...: Return an iterator through the dictionary in reversed iteration order.

copy()

Return a new view on a shallow copy of the dictionary.

get(key[, default])

Return the value of the dictionary item with an existing key or a default value.

has_key(key)

Python 2 only: Return a boolean indicating whether the dictionary contains an item with the key.

items()

Return the dictionary items.

iteritems()

Python 2 only: Return an iterator through the dictionary items.

iterkeys()

Python 2 only: Return an iterator through the dictionary keys.

itervalues()

Python 2 only: Return an iterator through the dictionary values.

keys()

Return the dictionary keys.

values()

Return the dictionary values.

viewitems()

Python 2 only: Return a view on the dictionary items.

viewkeys()

Python 2 only: Return a view on the dictionary keys.

viewvalues()

Python 2 only: Return a view on the dictionary values.

__contains__(key)[source]

value in self: Return a boolean indicating whether the dictionary contains a value.

The return value indicates whether the original dictionary contains an item that is equal to the value.

__eq__(other)[source]

self == other: Return a boolean indicating whether the dictionary is equal to the other dictionary.

The return value indicates whether the items in the original dictionary are equal to the items in the other dictionary (or in case of a DictView, its original dictionary).

The other object must be a dict or DictView.

Raises

TypeError – The other object is not a dict or DictView.

__ge__(other)[source]

self >= other: Return a boolean indicating whether the dictionary is an inclusive superset of the other dictionary.

The return value indicates whether every item in the other dictionary (or in case of a DictView, its original dictionary) is in the original dictionary.

The other object must be a dict or DictView.

Raises

TypeError – The other object is not a dict or DictView.

__getitem__(key)[source]

self[key]: Return the value of the dictionary item with an existing key.

Raises

KeyError – Key does not exist.

__gt__(other)[source]

self > other: Return a boolean indicating whether the dictionary is a proper superset of the other dictionary.

The return value indicates whether the original dictionary is a proper superset of the other dictionary (or in case of a DictView, its original dictionary).

The other object must be a dict or DictView.

Raises

TypeError – The other object is not a dict or DictView.

__iter__()[source]

Return an iterator through the dictionary keys.

__le__(other)[source]

self <= other: Return a boolean indicating whether the dictionary is an inclusive subset of the other dictionary.

The return value indicates whether every item in the original dictionary is in the other dictionary (or in case of a DictView, its original dictionary).

The other object must be a dict or DictView.

Raises

TypeError – The other object is not a dict or DictView.

__len__()[source]

len(self): Return the number of items in the dictionary.

The return value is the number of items in the original dictionary.

__lt__(other)[source]

self < other: Return a boolean indicating whether the dictionary is a proper subset of the other dictionary.

The return value indicates whether the original dictionary is a proper subset of the other dictionary (or in case of a DictView, its original dictionary).

The other object must be a dict or DictView.

Raises

TypeError – The other object is dict a set or DictView.

__ne__(other)[source]

self != other: Return a boolean indicating whether the dictionary is not equal to the other dictionary.

The return value indicates whether the items in the original dictionary are not equal to the items in the other dictionary (or in case of a DictView, its original dictionary).

The other object must be a dict or DictView.

Raises

TypeError – The other object is not a dict or DictView.

__repr__()[source]

repr(self): Return a string representation of the view suitable for debugging.

The original dictionary is represented using its repr() representation.

__reversed__()[source]

reversed(self) ...: Return an iterator through the dictionary in reversed iteration order.

The returned iterator yields the items in the original dictionary in the reversed iteration order.

copy()[source]

Return a new view on a shallow copy of the dictionary.

The returned DictView object is a new view object on a dictionary object of the type of the original dictionary.

If the dictionary type is immutable, the returned dictionary object may be the original dictionary object. If the dictionary type is mutable, the returned dictionary is a new dictionary object that is a shallow copy of the original dictionary object.

get(key, default=None)[source]

Return the value of the dictionary item with an existing key or a default value.

has_key(key)[source]

Python 2 only: Return a boolean indicating whether the dictionary contains an item with the key.

Raises

AttributeError – The method does not exist on Python 3.

items()[source]

Return the dictionary items.

The items of the original dictionary are returned in iteration order and as a view in Python 3 and as a list in Python 2. Each returned item is a tuple of key and value.

See Dictionary View Objects on Python 3 for details about view objects.

iteritems()[source]

Python 2 only: Return an iterator through the dictionary items. Each item is a tuple of key and value.

Raises

AttributeError – The method does not exist on Python 3.

iterkeys()[source]

Python 2 only: Return an iterator through the dictionary keys.

Raises

AttributeError – The method does not exist on Python 3.

itervalues()[source]

Python 2 only: Return an iterator through the dictionary values.

Raises

AttributeError – The method does not exist on Python 3.

keys()[source]

Return the dictionary keys.

The keys of the original dictionary are returned in iteration order and as a view in Python 3 and as a list in Python 2.

See Dictionary View Objects on Python 3 for details about view objects.

values()[source]

Return the dictionary values.

The values of the original dictionary are returned in iteration order and as a view in Python 3 and as a list in Python 2.

See Dictionary View Objects on Python 3 for details about view objects.

viewitems()[source]

Python 2 only: Return a view on the dictionary items.

The items of the original dictionary are returned in iteration order and as a view. Each returned item is a tuple of key and value.

See Dictionary View Objects on Python 2 for details about view objects.

Raises

AttributeError – The method does not exist on Python 3.

viewkeys()[source]

Python 2 only: Return a view on the dictionary keys.

The keys of the original dictionary are returned in iteration order and as a view.

See Dictionary View Objects on Python 2 for details about view objects.

Raises

AttributeError – The method does not exist on Python 3.

viewvalues()[source]

Python 2 only: Return a view on the dictionary values.

The values of the original dictionary are returned in iteration order and as a view.

See Dictionary View Objects on Python 2 for details about view objects.

Raises

AttributeError – The method does not exist on Python 3.

ListView class

class immutable_views.ListView(a_list)[source]

An immutable list view.

Derived from Sequence.

This is an immutable view on an original (mutable) Sequence object, e.g. list or tuple.

The view class supports the complete Python list behavior, except for any operations that would modify the list. More precisely, the view class supports all methods of Sequence (the methods are listed in the table at the top of the linked page).

The view is “live”: Since the view class delegates all operations to the original list, any modification of the original list object will be visible in the view object.

Note that only the view object is immutable, not its items. So if the values in the original list are mutable objects, they can be modified through the view.

Note that in Python, augmented assignment (e.g. += is not guaranteed to modify the left hand object in place, but can result in a new object. For details, see object.__iadd__(). The += operator on a left hand object that is a ListView object results in a new ListView object on a new list object.

Parameters

a_list (Sequence) – The original list. If this object is a ListView, its original list is used.

Methods:

__add__(other)

self + other: Return a new view on the concatenation of the list and the other list.

__contains__(value)

value in self: Return a boolean indicating whether the list contains a value.

__eq__(other)

self == other: Return a boolean indicating whether the list is equal to the other list.

__ge__(other)

self < other: Return a boolean indicating whether the list is greater than or equal to the other list.

__getitem__(index)

self[index]:

__gt__(other)

self > other: Return a boolean indicating whether the list is greater than the other list.

__iter__()

Return an iterator through the list items.

__le__(other)

self < other: Return a boolean indicating whether the list is less than or equal to the other list.

__len__()

len(self): Return the number of items in the list.

__lt__(other)

self < other: Return a boolean indicating whether the list is less than the other list.

__mul__(number)

self * number: Return a new view on the multiplication of the list with a number.

__ne__(other)

self != other: Return a boolean indicating whether the list is not equal to the other list.

__repr__()

repr(self): Return a string representation of the view suitable for debugging.

__reversed__()

reversed(self) ...: Return an iterator through the list in reversed iteration order.

__rmul__(number)

number * self: Return a new view on the multiplication of the list with a number.

copy()

Return a new view on a shallow copy of the list.

count(value)

Return the number of times the specified value occurs in the list.

index(value[, start, stop])

Return the index of the first item in the list with the specified value.

__add__(other)[source]

self + other: Return a new view on the concatenation of the list and the other list.

The returned ListView object is a view on a new list object of the type of the left hand operand that contains the items that are in the original list of the left hand operand, concatenated with the items in the other list (or in case of a ListView, its original list).

The other object must be an iterable or ListView.

The list and the other list are not changed.

Raises

TypeError – The other object is not an iterable.

__contains__(value)[source]

value in self: Return a boolean indicating whether the list contains a value.

The return value indicates whether the original list contains an item that is equal to the value.

__eq__(other)[source]

self == other: Return a boolean indicating whether the list is equal to the other list.

The return value indicates whether the items in the original list are equal to the items in the other list (or in case of a ListView, its original list).

The other object must be a list or ListView.

Raises

TypeError – The other object is not a list or ListView.

__ge__(other)[source]

self < other: Return a boolean indicating whether the list is greater than or equal to the other list.

The return value indicates whether the original list is greater than or equal to the other list (or in case of a ListView, its original list), based on the lexicographical ordering Python defines for sequence types (see https://docs.python.org/3/tutorial/datastructures.html#comparing-sequences-and-other-types)

The other object must be a list or ListView.

Raises

TypeError – The other object is not a list or ListView.

__getitem__(index)[source]
self[index]:

Return the list value at the index position.

Raises

IndexError – Index out of range.

__gt__(other)[source]

self > other: Return a boolean indicating whether the list is greater than the other list.

The return value indicates whether the original list is greater than the other list (or in case of a ListView, its original list), based on the lexicographical ordering Python defines for sequence types (see https://docs.python.org/3/tutorial/datastructures.html#comparing-sequences-and-other-types)

The other object must be a list or ListView.

Raises

TypeError – The other object is not a list or ListView.

__iter__()[source]

Return an iterator through the list items.

__le__(other)[source]

self < other: Return a boolean indicating whether the list is less than or equal to the other list.

The return value indicates whether the original list is less than or equal to the other list (or in case of a ListView, its original list), based on the lexicographical ordering Python defines for sequence types (see https://docs.python.org/3/tutorial/datastructures.html#comparing-sequences-and-other-types)

The other object must be a list or ListView.

Raises

TypeError – The other object is not a list or ListView.

__len__()[source]

len(self): Return the number of items in the list.

The return value is the number of items in the original list.

__lt__(other)[source]

self < other: Return a boolean indicating whether the list is less than the other list.

The return value indicates whether the original list is less than the other list (or in case of a ListView, its original list), based on the lexicographical ordering Python defines for sequence types (see https://docs.python.org/3/tutorial/datastructures.html#comparing-sequences-and-other-types)

The other object must be a list or ListView.

Raises

TypeError – The other object is not a list or ListView.

__mul__(number)[source]

self * number: Return a new view on the multiplication of the list with a number.

The returned ListView object is a view on a new list object of the type of the left hand operand that contains the items that are in the original list of the left hand operand as many times as specified by the right hand operand.

A number <= 0 causes the returned list to be empty.

The left hand operand is not changed.

__ne__(other)[source]

self != other: Return a boolean indicating whether the list is not equal to the other list.

The return value indicates whether the items in the original list are not equal to the items in the other list (or in case of a ListView, its original list).

The other object must be a list or ListView.

Raises

TypeError – The other object is not a list or ListView.

__repr__()[source]

repr(self): Return a string representation of the view suitable for debugging.

The original list is represented using its repr() representation.

__reversed__()[source]

reversed(self) ...: Return an iterator through the list in reversed iteration order.

The returned iterator yields the items in the original list in the reversed iteration order.

__rmul__(number)[source]

number * self: Return a new view on the multiplication of the list with a number.

This method is a fallback and is called only if the left operand does not support the operation.

The returned ListView object is a view on a new list object of the type of the right hand operand that contains the items that are in the original list of the right hand operand as many times as specified by the left hand operand.

A number <= 0 causes the returned list to be empty.

The right hand operand is not changed.

copy()[source]

Return a new view on a shallow copy of the list.

The returned ListView object is a new view object on a list object of the type of the original list.

If the list type is immutable, the returned list object may be the original list object. If the list type is mutable, the returned list is a new list object that is a shallow copy of the original list object.

count(value)[source]

Return the number of times the specified value occurs in the list.

index(value, start=0, stop=9223372036854775807)[source]

Return the index of the first item in the list with the specified value.

The search is limited to the index range defined by the specified start and stop parameters, whereby stop is the index of the first item after the search range.

Raises

ValueError – No such item is found.

SetView class

class immutable_views.SetView(a_set)[source]

An immutable set view.

Derived from Set.

This class provides an immutable view on a possibly mutable set object. The set object must be an instance of Set.

This can be used for example when a class maintains a set that should be made available to users of the class without allowing them to modify the set.

In the description of this class, the term ‘view’ always refers to the SetView object, and the term ‘set’ or ‘original set’ refers to the set object the view is based on.

The SetView class supports the complete behavior of Python class set, except for any methods that would modify the set. Note that the non-modifying methods of class set are a superset of the methods defined for the abstract class Set (the methods are listed in the table at the top of the linked page).

The view is “live”: Since the view class delegates all operations to the original set, any modification of the original set object will be visible in the view object.

Note that only the view object is immutable, not necessarily its items. So if the items in the original set are mutable objects, they can be modified through the view.

Note that in Python, augmented assignment (e.g. += is not guaranteed to modify the left hand object in place, but can result in a new object. For details, see object.__iadd__(). The += operator on a left hand object that is a SetView object results in a new SetView object on a new set object.

Parameters

a_set (Set) – The original set. If this object is a SetView, its original set is used.

Methods:

__and__(other)

self & other: Return a new view on the intersection of the set and the other set.

__contains__(value)

value in self: Return a boolean indicating whether the set contains a value.

__eq__(other)

self == other: Return a boolean indicating whether the set is equal to the other set.

__ge__(other)

self >= other: Return a boolean indicating whether the set is an inclusive superset of the other set.

__gt__(other)

self > other: Return a boolean indicating whether the set is a proper superset of the other set.

__iter__()

iter(self) ...: Return an iterator through the set.

__le__(other)

self <= other: Return a boolean indicating whether the set is an inclusive subset of the other set.

__len__()

len(self): Return the number of items in the set.

__lt__(other)

self < other: Return a boolean indicating whether the set is a proper subset of the other set.

__ne__(other)

self != other: Return a boolean indicating whether the set is not equal to the other set.

__or__(other)

self | other: Return a new view on the union of the set and the other set.

__rand__(other)

other & self: Return a new view on the intersection of the set and the other set.

__repr__()

repr(self): Return a string representation of the view suitable for debugging.

__ror__(other)

other | self: Return a new view on the union of the set and the other set.

__rsub__(other)

other - self: Return a new view on the difference of the other set and the set.

__rxor__(other)

other ^ self: Return a new view on the symmetric difference of the set and the other set.

__sub__(other)

self - other: Return a new view on the difference of the set and the other set.

__xor__(other)

self ^ other: Return a new view on the symmetric difference of the set and the other set.

copy()

Return a new view on a shallow copy of the set.

difference(*others)

Return a new view on the difference of the set and the other iterables.

intersection(*others)

Return a new view on the intersection of the set and the other iterables.

isdisjoint(other)

Return a boolean indicating whether the set does not intersect with the other iterable.

issubset(other)

Return a boolean indicating whether the set is an inclusive subset of the other iterable.

issuperset(other)

Return a boolean indicating whether the set is an inclusive superset of the other iterable.

symmetric_difference(other)

Return a new view on the symmetric difference of the set and the other iterable.

union(*others)

Return a new view on the union of the set and the other iterables.

__and__(other)[source]

self & other: Return a new view on the intersection of the set and the other set.

The returned SetView object is a view on a new set object of the type of the left hand operand that contains the items that are in the original set of the left hand operand and in the other set (or in case of a SetView, its original set).

The other object must be a set or SetView.

The set and the other set are not changed.

Raises

TypeError – The other object is not a set or SetView.

__contains__(value)[source]

value in self: Return a boolean indicating whether the set contains a value.

The return value indicates whether the original set contains an item that is equal to the value.

__eq__(other)[source]

self == other: Return a boolean indicating whether the set is equal to the other set.

The return value indicates whether the items in the original set are equal to the items in the other set (or in case of a SetView, its original set).

The other object must be a set or SetView.

Raises

TypeError – The other object is not a set or SetView.

__ge__(other)[source]

self >= other: Return a boolean indicating whether the set is an inclusive superset of the other set.

The return value indicates whether every item in the other set (or in case of a SetView, its original set) is in the original set.

The other object must be a set or SetView.

Raises

TypeError – The other object is not a set or SetView.

__gt__(other)[source]

self > other: Return a boolean indicating whether the set is a proper superset of the other set.

The return value indicates whether the original set is a proper superset of the other set (or in case of a SetView, its original set).

The other object must be a set or SetView.

Raises

TypeError – The other object is not a set or SetView.

__iter__()[source]

iter(self) ...: Return an iterator through the set.

The returned iterator yields the items in the original set in its iteration order.

__le__(other)[source]

self <= other: Return a boolean indicating whether the set is an inclusive subset of the other set.

The return value indicates whether every item in the original set is in the other set (or in case of a SetView, its original set).

The other object must be a set or SetView.

Raises

TypeError – The other object is not a set or SetView.

__len__()[source]

len(self): Return the number of items in the set.

The return value is the number of items in the original set.

__lt__(other)[source]

self < other: Return a boolean indicating whether the set is a proper subset of the other set.

The return value indicates whether the original set is a proper subset of the other set (or in case of a SetView, its original set).

The other object must be a set or SetView.

Raises

TypeError – The other object is not a set or SetView.

__ne__(other)[source]

self != other: Return a boolean indicating whether the set is not equal to the other set.

The return value indicates whether the items in the original set are not equal to the items in the other set (or in case of a SetView, its original set).

The other object must be a set or SetView.

Raises

TypeError – The other object is not a set or SetView.

__or__(other)[source]

self | other: Return a new view on the union of the set and the other set.

The returned SetView object is a view on a new set object of the type of the left hand operand that contains all the (unique) items from the original set of the left hand operand and the other set (or in case of a SetView, its original set).

The other object must be a set or SetView.

The set and the other set are not changed.

Raises

TypeError – The other object is not a set or SetView.

__rand__(other)[source]

other & self: Return a new view on the intersection of the set and the other set.

This method is a fallback and is called only if the left operand does not support the operation.

The returned SetView object is a view on a new set object of the type of the right hand operand that contains the items that are in the original set of the right hand operand and in the other set (or in case of a SetView, its original set).

The other object must be a set or SetView.

The set and the other set are not changed.

Raises

TypeError – The other object is not a set or SetView.

__repr__()[source]

repr(self): Return a string representation of the view suitable for debugging.

The original set is represented using its repr() representation.

__ror__(other)[source]

other | self: Return a new view on the union of the set and the other set.

This method is a fallback and is called only if the left operand does not support the operation.

The returned SetView object is a view on a new set object of the type of the right hand operand that contains all the (unique) items from the original set of the right hand operand and the other set (or in case of a SetView, its original set).

The other object must be a set or SetView.

The set and the other set are not changed.

Raises

TypeError – The other object is not a set or SetView.

__rsub__(other)[source]

other - self: Return a new view on the difference of the other set and the set.

This method is a fallback and is called only if the left operand does not support the operation.

The returned SetView object is a view on a new set object of the type of the left hand operand that contains the items that are in the other set (or in case of a SetView, its original set) but not in the original set of the left hand operand.

The other object must be a set or SetView.

The set and the other set are not changed.

Raises

TypeError – The other object is not a set or SetView.

__rxor__(other)[source]

other ^ self: Return a new view on the symmetric difference of the set and the other set.

This method is a fallback and is called only if the left operand does not support the operation.

The returned SetView object is a view on a new set object of the type of the right hand operand that contains the items that are in either the original set of the right hand operand or in the other set (or in case of a SetView, its original set), but not in both.

The other object must be a set or SetView.

The set and the other set are not changed.

Raises

TypeError – The other object is not a set or SetView.

__sub__(other)[source]

self - other: Return a new view on the difference of the set and the other set.

The returned SetView object is a view on a new set object of the type of the left hand operand that contains the items that are in the original set of the left hand operand but not in the other set (or in case of a SetView, its original set).

The other object must be a set or SetView.

The set and the other set are not changed.

Raises

TypeError – The other object is not a set or SetView.

__xor__(other)[source]

self ^ other: Return a new view on the symmetric difference of the set and the other set.

The returned SetView object is a view on a new set object of the type of the left hand operand that contains the items that are in either the original set of the left hand operand or in the other set (or in case of a SetView, its original set), but not in both.

The other object must be a set or SetView.

The set and the other set are not changed.

Raises

TypeError – The other object is not a set or SetView.

copy()[source]

Return a new view on a shallow copy of the set.

The returned SetView object is a new view object on a set object of the type of the original set.

If the set type is immutable, the returned set object may be the original set object. If the set type is mutable, the returned set is a new set object that is a shallow copy of the original set object.

difference(*others)[source]

Return a new view on the difference of the set and the other iterables.

The returned SetView object is a view on a new set object of the type of the original set that contains the items that are in the original set but not in any of the other iterables (or in case of SetView objects, their original sets).

The other objects must be iterables.

The set and the other iterables are not changed.

Raises

TypeError – The other objects are not all iterables.

intersection(*others)[source]

Return a new view on the intersection of the set and the other iterables.

The returned SetView object is a view on a new set object of the type of the original set that contains the items that are in the original set and in the other iterables (or in case of SetView objects, their original sets).

The other objects must be iterables.

The set and the other iterables are not changed.

Raises

TypeError – The other objects are not all iterables.

isdisjoint(other)[source]

Return a boolean indicating whether the set does not intersect with the other iterable.

The return value indicates whether the original set has no items in common with the other iterable (or in case of a SetView, its original set).

The other object must be an iterable.

Raises

TypeError – The other object is not an iterable.

issubset(other)[source]

Return a boolean indicating whether the set is an inclusive subset of the other iterable.

The return value indicates whether every item in the original set is in the other iterable (or in case of a SetView, its original set).

The other object must be an iterable.

Raises

TypeError – The other object is not an iterable.

issuperset(other)[source]

Return a boolean indicating whether the set is an inclusive superset of the other iterable.

The return value indicates whether every item in the other iterable (or in case of a SetView, its original set) is in the original set.

The other object must be an iterable.

Raises

TypeError – The other object is not an iterable.

symmetric_difference(other)[source]

Return a new view on the symmetric difference of the set and the other iterable.

The returned SetView object is a view on a new set object of the type of the original set that contains the items that are in either the original set or in the other iterable (or in case of a SetView, its original set), but not in both.

The other object must be an iterable.

The set and the other iterable are not changed.

Raises

TypeError – The other object is not an iterable.

union(*others)[source]

Return a new view on the union of the set and the other iterables.

The returned SetView object is a view on a new set object of the type of the original set that contains all the (unique) items from the original set and the other iterables (or in case of SetView objects, their original sets).

The other objects must be iterables.

The set and the other iterables are not changed.

Raises

TypeError – The other objects are not all iterables.

Package version

immutable_views.__version__ = '0.5.0'

The full version of this package including any development levels, as a string.

Possible formats for this version string are:

  • “M.N.P.dev1”: Development level 1 of a not yet released version M.N.P

  • “M.N.P”: A released version M.N.P

Development

This section only needs to be read by developers of the immutable-views project, including people who want to make a fix or want to test the project.

Repository

The repository for the immutable-views project is on GitHub:

https://github.com/andy-maier/immutable-views

Setting up the development environment

  1. If you have write access to the Git repo of this project, clone it using its SSH link, and switch to its working directory:

    $ git clone git@github.com:andy-maier/immutable-views.git
    $ cd immutable-views
    

    If you do not have write access, create a fork on GitHub and clone the fork in the way shown above.

  2. It is recommended that you set up a virtual Python environment. Have the virtual Python environment active for all remaining steps.

  3. Install the project for development. This will install Python packages into the active Python environment, and OS-level packages:

    $ make develop
    
  4. This project uses Make to do things in the currently active Python environment. The command:

    $ make
    

    displays a list of valid Make targets and a short description of what each target does.

Building the documentation

The ReadTheDocs (RTD) site is used to publish the documentation for the project package at https://immutable-views.readthedocs.io/

This page is automatically updated whenever the Git repo for this package changes the branch from which this documentation is built.

In order to build the documentation locally from the Git work directory, execute:

$ make builddoc

The top-level document to open with a web browser will be build_doc/html/docs/index.html.

Testing

All of the following make commands run the tests in the currently active Python environment. Depending on how the immutable-views package is installed in that Python environment, either the directories in the main repository directory are used, or the installed package. The test case files and any utility functions they use are always used from the tests directory in the main repository directory.

The tests directory has the following subdirectory structure:

tests
 +-- unittest            Unit tests
  • Unit tests

    These tests can be run standalone, and the tests validate their results automatically.

    They are run by executing:

    $ make test
    

    Test execution can be modified by a number of environment variables, as documented in the make help (execute make help).

Contributing

Third party contributions to this project are welcome!

In order to contribute, create a Git pull request, considering this:

  • Test is required.

  • Each commit should only contain one “logical” change.

  • A “logical” change should be put into one commit, and not split over multiple commits.

  • Large new features should be split into stages.

  • The commit message should not only summarize what you have done, but explain why the change is useful.

What comprises a “logical” change is subject to sound judgement. Sometimes, it makes sense to produce a set of commits for a feature (even if not large). For example, a first commit may introduce a (presumably) compatible API change without exploitation of that feature. With only this commit applied, it should be demonstrable that everything is still working as before. The next commit may be the exploitation of the feature in other components.

For further discussion of good and bad practices regarding commits, see:

Further rules:

  • The following long-lived branches exist and should be used as targets for pull requests:

    • master - for next functional version

    • stable_$MN - for fix stream of released version M.N.

  • We use topic branches for everything!

    • Based upon the intended long-lived branch, if no dependencies

    • Based upon an earlier topic branch, in case of dependencies

    • It is valid to rebase topic branches and force-push them.

  • We use pull requests to review the branches.

    • Use the correct long-lived branch (e.g. master or stable_0.2) as a merge target.

    • Review happens as comments on the pull requests.

    • At least one approval is required for merging.

  • GitHub meanwhile offers different ways to merge pull requests. We merge pull requests by rebasing the commit from the pull request.

Releasing a version to PyPI

This section describes how to release a version of immutable-views to PyPI.

It covers all variants of versions that can be released:

  • Releasing a new major version (Mnew.0.0) based on the master branch

  • Releasing a new minor version (M.Nnew.0) based on the master branch

  • Releasing a new update version (M.N.Unew) based on the stable branch of its minor version

The description assumes that the andy-maier/immutable-views Github repo is cloned locally and its upstream repo is assumed to have the Git remote name origin.

Any commands in the following steps are executed in the main directory of your local clone of the andy-maier/immutable-views Git repo.

  1. Set shell variables for the version that is being released and the branch it is based on:

    • MNU - Full version M.N.U that is being released

    • MN - Major and minor version M.N of that full version

    • BRANCH - Name of the branch the version that is being released is based on

    When releasing a new major version (e.g. 1.0.0) based on the master branch:

    MNU=1.0.0
    MN=1.0
    BRANCH=master
    

    When releasing a new minor version (e.g. 0.9.0) based on the master branch:

    MNU=0.9.0
    MN=0.9
    BRANCH=master
    

    When releasing a new update version (e.g. 0.8.1) based on the stable branch of its minor version:

    MNU=0.8.1
    MN=0.8
    BRANCH=stable_${MN}
    
  2. Create a topic branch for the version that is being released:

    git checkout ${BRANCH}
    git pull
    git checkout -b release_${MNU}
    
  3. Edit the version file:

    vi immutable_views/_version.py
    

    and set the __version__ variable to the version that is being released:

    __version__ = 'M.N.U'
    
  4. Edit the change log:

    vi docs/changes.rst
    

    and make the following changes in the section of the version that is being released:

    • Finalize the version.

    • Change the release date to today’s date.

    • Make sure that all changes are described.

    • Make sure the items shown in the change log are relevant for and understandable by users.

    • In the “Known issues” list item, remove the link to the issue tracker and add text for any known issues you want users to know about.

    • Remove all empty list items.

  5. When releasing based on the master branch, edit the GitHub workflow file test.yml:

    vi .github/workflows/test.yml
    

    and in the on section, increase the version of the stable_* branch to the new stable branch stable_M.N created earlier:

    on:
      schedule:
        . . .
      push:
        branches: [ master, stable_M.N ]
      pull_request:
        branches: [ master, stable_M.N ]
    
  6. Commit your changes and push the topic branch to the remote repo:

    git status  # Double check the changed files
    git commit -asm "Release ${MNU}"
    git push --set-upstream origin release_${MNU}
    
  7. On GitHub, create a Pull Request for branch release_M.N.U. This will trigger the CI runs.

    Important: When creating Pull Requests, GitHub by default targets the master branch. When releasing based on a stable branch, you need to change the target branch of the Pull Request to stable_M.N.

  8. On GitHub, close milestone M.N.U.

  9. On GitHub, once the checks for the Pull Request for branch start_M.N.U have succeeded, merge the Pull Request (no review is needed). This automatically deletes the branch on GitHub.

  10. Add a new tag for the version that is being released and push it to the remote repo. Clean up the local repo:

    git checkout ${BRANCH}
    git pull
    git tag -f ${MNU}
    git push -f --tags
    git branch -d release_${MNU}
    
  11. When releasing based on the master branch, create and push a new stable branch for the same minor version:

    git checkout -b stable_${MN}
    git push --set-upstream origin stable_${MN}
    git checkout ${BRANCH}
    

    Note that no GitHub Pull Request is created for any stable_* branch.

  12. On GitHub, edit the new tag M.N.U, and create a release description on it. This will cause it to appear in the Release tab.

    You can see the tags in GitHub via Code -> Releases -> Tags.

  13. On ReadTheDocs, activate the new version M.N.U:

  14. Upload the package to PyPI:

    make upload
    

    This will show the package version and will ask for confirmation.

    Attention! This only works once for each version. You cannot release the same version twice to PyPI.

    Verify that the released version arrived on PyPI at https://pypi.python.org/pypi/immutable-views/

Starting a new version

This section shows the steps for starting development of a new version of the immutable-views project in its Git repo.

This section covers all variants of new versions:

  • Starting a new major version (Mnew.0.0) based on the master branch

  • Starting a new minor version (M.Nnew.0) based on the master branch

  • Starting a new update version (M.N.Unew) based on the stable branch of its minor version

The description assumes that the andy-maier/immutable-views Github repo is cloned locally and its upstream repo is assumed to have the Git remote name origin.

Any commands in the following steps are executed in the main directory of your local clone of the andy-maier/immutable-views Git repo.

  1. Set shell variables for the version that is being started and the branch it is based on:

    • MNU - Full version M.N.U that is being started

    • MN - Major and minor version M.N of that full version

    • BRANCH - Name of the branch the version that is being started is based on

    When starting a new major version (e.g. 1.0.0) based on the master branch:

    MNU=1.0.0
    MN=1.0
    BRANCH=master
    

    When starting a new minor version (e.g. 0.9.0) based on the master branch:

    MNU=0.9.0
    MN=0.9
    BRANCH=master
    

    When starting a new minor version (e.g. 0.8.1) based on the stable branch of its minor version:

    MNU=0.8.1
    MN=0.8
    BRANCH=stable_${MN}
    
  2. Create a topic branch for the version that is being started:

    git checkout ${BRANCH}
    git pull
    git checkout -b start_${MNU}
    
  3. Edit the version file:

    vi immutable_views/_version.py
    

    and update the version to a draft version of the version that is being started:

    __version__ = 'M.N.U.dev1'
    
  4. Edit the change log:

    vi docs/changes.rst
    

    and insert the following section before the top-most section:

    immutable-views M.N.U.dev1
    --------------------------
    
    Released: not yet
    
    **Incompatible changes:**
    
    **Deprecations:**
    
    **Bug fixes:**
    
    **Enhancements:**
    
    **Cleanup:**
    
    **Known issues:**
    
    * See `list of open issues`_.
    
    .. _`list of open issues`: https://github.com/andy-maier/immutable-views/issues
    
  5. Commit your changes and push them to the remote repo:

    git status  # Double check the changed files
    git commit -asm "Start ${MNU}"
    git push --set-upstream origin start_${MNU}
    
  6. On GitHub, create a Pull Request for branch start_M.N.U.

    Important: When creating Pull Requests, GitHub by default targets the master branch. When starting a version based on a stable branch, you need to change the target branch of the Pull Request to stable_M.N.

  7. On GitHub, create a milestone for the new version M.N.U.

    You can create a milestone in GitHub via Issues -> Milestones -> New Milestone.

  8. On GitHub, go through all open issues and pull requests that still have milestones for previous releases set, and either set them to the new milestone, or to have no milestone.

  9. On GitHub, once the checks for the Pull Request for branch start_M.N.U have succeeded, merge the Pull Request (no review is needed). This automatically deletes the branch on GitHub.

  10. Update and clean up the local repo:

    git checkout ${BRANCH}
    git pull
    git branch -d start_${MNU}
    

Appendix

Glossary

string

a unicode string or a byte string

unicode string

a Unicode string type (unicode in Python 2, and str in Python 3)

byte string

a byte string type (str in Python 2, and bytes in Python 3). Unless otherwise indicated, byte strings in this project are always UTF-8 encoded.

References

Python Glossary

Change log

Version 0.5.0

Released: 2021-04-12

Initial release