immutable-proxy


Nameimmutable-proxy JSON
Version 0.1.1 PyPI version JSON
download
home_page
SummaryProvides a proxy to an object that prevents mutation at any depth
upload_time2023-04-01 15:17:39
maintainer
docs_urlNone
author
requires_python>=3.9
licenseThe MIT License (MIT) Copyright © 2023 José Carlos Díaz-Ramos Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
keywords immutable object proxy
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # immutable-proxy

The main aim of this package is to define the class `DeepImmutableProxy` that prevents a user-defined class to be mutated to any level of depth.

`DeepImmutableProxy` takes an object as an argument and acts as a proxy to that object.
Attributes and methods can be accessed normally unless they try to mutate any object that is accessible from the wrapped object.

## Installation

This package is written in pure python. Simply install it using pip

```
pip install immutable-proxy
```


## Basic usage

```python
>>> from immutable import DeepImmutableProxy, ConstantAttributeError

```

We define a simple class:

```python
>>> class Example():
...     def __init__(self, x):
...         self.x = x
...     def __repr__(self):
...         return f'{type(self).__qualname__}({self.x!r})'

>>> example = Example(42)
>>> example.x = 0
>>> print(example)
Example(0)

```

If we wrap an object with `DeepImmutableProxy`, then this object cannot be mutated.

```python
>>> proxy = DeepImmutableProxy(Example(42))

>>> try:
...     proxy.x = 0
... except ConstantAttributeError as error:
...     print(error)
'DeepImmutableProxy[Example]' object is immutable. Cannot change attribute 'x' after initialization.

>>> try:
...     del proxy.x
... except ConstantAttributeError as error:
...     print(error)
'DeepImmutableProxy[Example]' object is immutable. Cannot delete attribute 'x' after initialization.

>>> print(proxy)
Example(42)

```


## Nested attributes, indirect mutation, and inheritance

The following example demonstrates a more complicated class with properties and methods that set attributes.

```python
>>> class Inner:
...     def __init__(self, x, y):
...         self.x = x
...         self.y = y
...     def __repr__(self):
...         return f'{type(self).__qualname__}({self.x!r}, {self.y!r})'
...     def set_x(self, x):
...         "Demonstrates a way to set attribute 'x'."
...         self.x = x
...     def get_x(self) -> int:
...         return self.x
...     @property
...     def y(self):
...         "Demonstrates a property to handle attribute 'y'."
...         return self.__dict__['y']
...     @y.setter
...     def y(self, value):
...         self.__dict__['y'] = value

```

The objects of this class work as expected.

```python
>>> inner = Inner(1, 2)
>>> inner.x, inner.y = -1, -2
>>> print(inner)
Inner(-1, -2)

>>> inner.set_x(3)
>>> print(inner)
Inner(3, -2)

```

Now we wrap an object of this class with `DeepImmutableProxy`.
This object cannot be mutated.

```python
>>> inner_proxy = DeepImmutableProxy(Inner(1, 2))

>>> try:
...     inner_proxy.x = -1
... except ConstantAttributeError as error:
...     print(error)
'DeepImmutableProxy[Inner]' object is immutable. Cannot change attribute 'x' after initialization.

>>> try:
...     inner_proxy.y = -2
... except ConstantAttributeError as error:
...     print(error)
'DeepImmutableProxy[Inner]' object is immutable. Cannot change attribute 'y' after initialization.

>>> try:
...     inner_proxy.set_x(3)
... except ConstantAttributeError as error:
...     print(error)
'DeepImmutableProxy[Inner]' object is immutable. Cannot change attribute 'x' after initialization.

>>> try:
...     del inner_proxy.x
... except ConstantAttributeError as error:
...     print(error)
'DeepImmutableProxy[Inner]' object is immutable. Cannot delete attribute 'x' after initialization.

```

We define another class whose attributes are of type `Inner`.
It also contains an attribute referring to another object that refers back to an object of this class.  The initial object cannot be modified indirectly trough this back reference.

```python 
>>> class Outer:
...     def __init__(self, a: Inner, b: Inner) -> None:
...         self.a = a
...         self.b = b
...         self.delegate = Modifier(self)
...     def __repr__(self):
...         return f'{type(self).__qualname__}({self.a!r}, {self.b!r})'
...     def set_a(self, a):
...         'Sets a.'
...         self.a = a
...     def get_a(self):
...         return self.a
...     @property
...     def b(self):
...         return self.__dict__['b']
...     @b.setter
...     def b(self, value) -> None:
...         self.__dict__['b'] = value

>>> class Modifier():
...     def __init__(self, obj):
...         self.back = obj
...     def reset(self, x):
...         self.back.x = x

>>> outer_proxy = DeepImmutableProxy(Outer(Inner(1, 2), Inner(3, 4)))

```

The object just created is wrapped with `DeepImmutableProxy`. 
This prevents mutation of attributes at all levels of depth.

```python
>>> try:
...     outer_proxy.a = Inner(-1, -2)
... except ConstantAttributeError as error:
...     print(error)
'DeepImmutableProxy[Outer]' object is immutable. Cannot change attribute 'a' after initialization.

>>> try:
...     outer_proxy.b = Inner(-3, -4)
... except ConstantAttributeError as error:
...     print(error)
'DeepImmutableProxy[Outer]' object is immutable. Cannot change attribute 'b' after initialization.

>>> try:
...     outer_proxy.a.x = -1
... except ConstantAttributeError as error:
...     print(error)
'DeepImmutableProxy[Inner]' object is immutable. Cannot change attribute 'x' after initialization.

>>> try:
...     outer_proxy.a.y = -2
... except ConstantAttributeError as error:
...     print(error)
'DeepImmutableProxy[Inner]' object is immutable. Cannot change attribute 'y' after initialization.

>>> try:
...     outer_proxy.b.x = -3
... except ConstantAttributeError as error:
...     print(error)
'DeepImmutableProxy[Inner]' object is immutable. Cannot change attribute 'x' after initialization.

>>> try:
...     outer_proxy.b.y = -4
... except ConstantAttributeError as error:
...     print(error)
'DeepImmutableProxy[Inner]' object is immutable. Cannot change attribute 'y' after initialization.

>>> try:
...     outer_proxy.set_a(Inner(-1, -2))
... except ConstantAttributeError as error:
...     print(error)
'DeepImmutableProxy[Outer]' object is immutable. Cannot change attribute 'a' after initialization.

>>> try:
...     outer_proxy.a.set_x(-1)
... except ConstantAttributeError as error:
...     print(error)
'DeepImmutableProxy[Inner]' object is immutable. Cannot change attribute 'x' after initialization.

>>> try:
...     del outer_proxy.a
... except ConstantAttributeError as error:
...     print(error)
'DeepImmutableProxy[Outer]' object is immutable. Cannot delete attribute 'a' after initialization.

```

We recall that instances of `Outer` have an attribute that references an object of another class that has a reference to the first.  We show that `DeepImmutableProxy` still prevents mutation.

```python
>>> try:
...     outer_proxy.delegate.back.x = 5
... except ConstantAttributeError as error:
...     print(error)
'DeepImmutableProxy[Outer]' object is immutable. Cannot change attribute 'x' after initialization.

```

Next we present an example where inheritance is involved and `super()` is used.

```python
>>> class Derived(Outer):
...     def set_a(self, a):
...         modified = Inner(2*a.x, 2*a.y)
...         super().set_a(modified)
    
>>> derived = Derived(Inner(1, 2), Inner(3, 4))
>>> derived.set_a(Inner(10, 20))
>>> print(derived)
Derived(Inner(20, 40), Inner(3, 4))

>>> derived_proxy = DeepImmutableProxy(Derived(Inner(1, 2), Inner(3, 4)))

>>> try:
...     derived_proxy.set_a(Inner(10, 20))
... except ConstantAttributeError as error:
...     print(error)
'DeepImmutableProxy[Derived]' object is immutable. Cannot change attribute 'a' after initialization.

```


## Built-in types

The class `DeepImmutableProxy` works with instances of user defined classes.
For classes that are not written in *Python* there might be problems, as the introspection methods used by `DeepImmutableProxy` may not work.
This problem is solved with the class methods `DeepImmutableProxy.register_immutable`, which can be used to tell `DeepImmutableProxy` that certain class is already immutable (hence, wrapping it with `DeepImmutableProxy` has no effect), and `DeepImmutableProxy.register_proxy`, that allows a user to register a subclass of `DeepImmutableProxy` that defines an immutable proxy specific for the given type.

For example, types `int`, `float`, `str`, `bytes`, and some others, have already been registered to be immutable:

```python
>>> no_proxy = DeepImmutableProxy(3)
>>> type(no_proxy) == int
True

```

The class method `DeepImmutableProxy.get_registered_immutable` returns a list of the currently registered immutable classes:

```python
>>> [cls.__name__ for cls in DeepImmutableProxy.get_registered_immutable()] # doctest: +ELLIPSIS
['bytes', 'int', 'float', 'complex', 'str', 'NoneType', ...]

``` 

Some containers like tuples are immutable only if their content is immutable.  
Thus, when applying `DeepImmutableProxy` to a container like a tuple, we need a specific wrapper.
This wrapper is already defined in this package.

For example, the second element of the next tuple is mutable, so the resulting object is mutable.
However, when wrapped with `DeepImmutableProxy` it really becomes immutable.
This is achieved by returning a special wrapper for tuples.
The usual `DeepImmutableProxy` class does not work because attribute access for built-in types does not follow the standard Python rules.

```python
>>> mutable_tuple_proxy = DeepImmutableProxy(('immutable', ['mutable']))
>>> mutable_tuple_proxy
TupleImmutableProxy(('immutable', ['mutable']))

```

The first item of the tuple is a `str`, which is immutable, so it is returned as is.

```python
>>> mutable_tuple_proxy[0]
'immutable'

```

The second item is mutable, so it is wrapped and cannot be modified either.

```python
>>> mutable_tuple_proxy[1]
ListImmutableProxy(['mutable'])

```

This second item is of type list, so it also needs a special wrapper.
Methods that do not mutate the content can be used normally.

```python
>>> len(mutable_tuple_proxy[1]), mutable_tuple_proxy[1].index('mutable')
(1, 0)

```

However, methods that do mutate content are forbidden.

```python
>>> try:
...     mutable_tuple_proxy[1].append('another')
... except AttributeError as error:
...     print(error)
Object of type "ListImmutableProxy" does not have attribute "append".
    
```


## Copying

`DeepImmutableProxy` objects include a hook for `__copy__` and `__deepcopy__` that copies the underlying object.  The copy is mutable.

```python
>>> from copy import deepcopy

>>> copied = deepcopy(outer_proxy)
>>> print(copied)
Outer(Inner(1, 2), Inner(3, 4))

>>> copied.a.x = -1

>>> try:
...     outer_proxy.a.x = -1
... except ConstantAttributeError as error:
...     print(error)
'DeepImmutableProxy[Inner]' object is immutable. Cannot change attribute 'x' after initialization.

```


            

Raw data

            {
    "_id": null,
    "home_page": "",
    "name": "immutable-proxy",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.9",
    "maintainer_email": "",
    "keywords": "immutable object,proxy",
    "author": "",
    "author_email": "Jos\u00e9 Carlos D\u00edaz-Ramos <carlitos.diaz.ramos@gmail.com>",
    "download_url": "https://files.pythonhosted.org/packages/45/bd/9dda13eba8db5fb8b6e6c40481455efcc30102cf65f322b900da42014a1b/immutable-proxy-0.1.1.tar.gz",
    "platform": null,
    "description": "# immutable-proxy\r\n\r\nThe main aim of this package is to define the class `DeepImmutableProxy` that prevents a user-defined class to be mutated to any level of depth.\r\n\r\n`DeepImmutableProxy` takes an object as an argument and acts as a proxy to that object.\r\nAttributes and methods can be accessed normally unless they try to mutate any object that is accessible from the wrapped object.\r\n\r\n## Installation\r\n\r\nThis package is written in pure python. Simply install it using pip\r\n\r\n```\r\npip install immutable-proxy\r\n```\r\n\r\n\r\n## Basic usage\r\n\r\n```python\r\n>>> from immutable import DeepImmutableProxy, ConstantAttributeError\r\n\r\n```\r\n\r\nWe define a simple class:\r\n\r\n```python\r\n>>> class Example():\r\n...     def __init__(self, x):\r\n...         self.x = x\r\n...     def __repr__(self):\r\n...         return f'{type(self).__qualname__}({self.x!r})'\r\n\r\n>>> example = Example(42)\r\n>>> example.x = 0\r\n>>> print(example)\r\nExample(0)\r\n\r\n```\r\n\r\nIf we wrap an object with `DeepImmutableProxy`, then this object cannot be mutated.\r\n\r\n```python\r\n>>> proxy = DeepImmutableProxy(Example(42))\r\n\r\n>>> try:\r\n...     proxy.x = 0\r\n... except ConstantAttributeError as error:\r\n...     print(error)\r\n'DeepImmutableProxy[Example]' object is immutable. Cannot change attribute 'x' after initialization.\r\n\r\n>>> try:\r\n...     del proxy.x\r\n... except ConstantAttributeError as error:\r\n...     print(error)\r\n'DeepImmutableProxy[Example]' object is immutable. Cannot delete attribute 'x' after initialization.\r\n\r\n>>> print(proxy)\r\nExample(42)\r\n\r\n```\r\n\r\n\r\n## Nested attributes, indirect mutation, and inheritance\r\n\r\nThe following example demonstrates a more complicated class with properties and methods that set attributes.\r\n\r\n```python\r\n>>> class Inner:\r\n...     def __init__(self, x, y):\r\n...         self.x = x\r\n...         self.y = y\r\n...     def __repr__(self):\r\n...         return f'{type(self).__qualname__}({self.x!r}, {self.y!r})'\r\n...     def set_x(self, x):\r\n...         \"Demonstrates a way to set attribute 'x'.\"\r\n...         self.x = x\r\n...     def get_x(self) -> int:\r\n...         return self.x\r\n...     @property\r\n...     def y(self):\r\n...         \"Demonstrates a property to handle attribute 'y'.\"\r\n...         return self.__dict__['y']\r\n...     @y.setter\r\n...     def y(self, value):\r\n...         self.__dict__['y'] = value\r\n\r\n```\r\n\r\nThe objects of this class work as expected.\r\n\r\n```python\r\n>>> inner = Inner(1, 2)\r\n>>> inner.x, inner.y = -1, -2\r\n>>> print(inner)\r\nInner(-1, -2)\r\n\r\n>>> inner.set_x(3)\r\n>>> print(inner)\r\nInner(3, -2)\r\n\r\n```\r\n\r\nNow we wrap an object of this class with `DeepImmutableProxy`.\r\nThis object cannot be mutated.\r\n\r\n```python\r\n>>> inner_proxy = DeepImmutableProxy(Inner(1, 2))\r\n\r\n>>> try:\r\n...     inner_proxy.x = -1\r\n... except ConstantAttributeError as error:\r\n...     print(error)\r\n'DeepImmutableProxy[Inner]' object is immutable. Cannot change attribute 'x' after initialization.\r\n\r\n>>> try:\r\n...     inner_proxy.y = -2\r\n... except ConstantAttributeError as error:\r\n...     print(error)\r\n'DeepImmutableProxy[Inner]' object is immutable. Cannot change attribute 'y' after initialization.\r\n\r\n>>> try:\r\n...     inner_proxy.set_x(3)\r\n... except ConstantAttributeError as error:\r\n...     print(error)\r\n'DeepImmutableProxy[Inner]' object is immutable. Cannot change attribute 'x' after initialization.\r\n\r\n>>> try:\r\n...     del inner_proxy.x\r\n... except ConstantAttributeError as error:\r\n...     print(error)\r\n'DeepImmutableProxy[Inner]' object is immutable. Cannot delete attribute 'x' after initialization.\r\n\r\n```\r\n\r\nWe define another class whose attributes are of type `Inner`.\r\nIt also contains an attribute referring to another object that refers back to an object of this class.  The initial object cannot be modified indirectly trough this back reference.\r\n\r\n```python \r\n>>> class Outer:\r\n...     def __init__(self, a: Inner, b: Inner) -> None:\r\n...         self.a = a\r\n...         self.b = b\r\n...         self.delegate = Modifier(self)\r\n...     def __repr__(self):\r\n...         return f'{type(self).__qualname__}({self.a!r}, {self.b!r})'\r\n...     def set_a(self, a):\r\n...         'Sets a.'\r\n...         self.a = a\r\n...     def get_a(self):\r\n...         return self.a\r\n...     @property\r\n...     def b(self):\r\n...         return self.__dict__['b']\r\n...     @b.setter\r\n...     def b(self, value) -> None:\r\n...         self.__dict__['b'] = value\r\n\r\n>>> class Modifier():\r\n...     def __init__(self, obj):\r\n...         self.back = obj\r\n...     def reset(self, x):\r\n...         self.back.x = x\r\n\r\n>>> outer_proxy = DeepImmutableProxy(Outer(Inner(1, 2), Inner(3, 4)))\r\n\r\n```\r\n\r\nThe object just created is wrapped with `DeepImmutableProxy`. \r\nThis prevents mutation of attributes at all levels of depth.\r\n\r\n```python\r\n>>> try:\r\n...     outer_proxy.a = Inner(-1, -2)\r\n... except ConstantAttributeError as error:\r\n...     print(error)\r\n'DeepImmutableProxy[Outer]' object is immutable. Cannot change attribute 'a' after initialization.\r\n\r\n>>> try:\r\n...     outer_proxy.b = Inner(-3, -4)\r\n... except ConstantAttributeError as error:\r\n...     print(error)\r\n'DeepImmutableProxy[Outer]' object is immutable. Cannot change attribute 'b' after initialization.\r\n\r\n>>> try:\r\n...     outer_proxy.a.x = -1\r\n... except ConstantAttributeError as error:\r\n...     print(error)\r\n'DeepImmutableProxy[Inner]' object is immutable. Cannot change attribute 'x' after initialization.\r\n\r\n>>> try:\r\n...     outer_proxy.a.y = -2\r\n... except ConstantAttributeError as error:\r\n...     print(error)\r\n'DeepImmutableProxy[Inner]' object is immutable. Cannot change attribute 'y' after initialization.\r\n\r\n>>> try:\r\n...     outer_proxy.b.x = -3\r\n... except ConstantAttributeError as error:\r\n...     print(error)\r\n'DeepImmutableProxy[Inner]' object is immutable. Cannot change attribute 'x' after initialization.\r\n\r\n>>> try:\r\n...     outer_proxy.b.y = -4\r\n... except ConstantAttributeError as error:\r\n...     print(error)\r\n'DeepImmutableProxy[Inner]' object is immutable. Cannot change attribute 'y' after initialization.\r\n\r\n>>> try:\r\n...     outer_proxy.set_a(Inner(-1, -2))\r\n... except ConstantAttributeError as error:\r\n...     print(error)\r\n'DeepImmutableProxy[Outer]' object is immutable. Cannot change attribute 'a' after initialization.\r\n\r\n>>> try:\r\n...     outer_proxy.a.set_x(-1)\r\n... except ConstantAttributeError as error:\r\n...     print(error)\r\n'DeepImmutableProxy[Inner]' object is immutable. Cannot change attribute 'x' after initialization.\r\n\r\n>>> try:\r\n...     del outer_proxy.a\r\n... except ConstantAttributeError as error:\r\n...     print(error)\r\n'DeepImmutableProxy[Outer]' object is immutable. Cannot delete attribute 'a' after initialization.\r\n\r\n```\r\n\r\nWe recall that instances of `Outer` have an attribute that references an object of another class that has a reference to the first.  We show that `DeepImmutableProxy` still prevents mutation.\r\n\r\n```python\r\n>>> try:\r\n...     outer_proxy.delegate.back.x = 5\r\n... except ConstantAttributeError as error:\r\n...     print(error)\r\n'DeepImmutableProxy[Outer]' object is immutable. Cannot change attribute 'x' after initialization.\r\n\r\n```\r\n\r\nNext we present an example where inheritance is involved and `super()` is used.\r\n\r\n```python\r\n>>> class Derived(Outer):\r\n...     def set_a(self, a):\r\n...         modified = Inner(2*a.x, 2*a.y)\r\n...         super().set_a(modified)\r\n    \r\n>>> derived = Derived(Inner(1, 2), Inner(3, 4))\r\n>>> derived.set_a(Inner(10, 20))\r\n>>> print(derived)\r\nDerived(Inner(20, 40), Inner(3, 4))\r\n\r\n>>> derived_proxy = DeepImmutableProxy(Derived(Inner(1, 2), Inner(3, 4)))\r\n\r\n>>> try:\r\n...     derived_proxy.set_a(Inner(10, 20))\r\n... except ConstantAttributeError as error:\r\n...     print(error)\r\n'DeepImmutableProxy[Derived]' object is immutable. Cannot change attribute 'a' after initialization.\r\n\r\n```\r\n\r\n\r\n## Built-in types\r\n\r\nThe class `DeepImmutableProxy` works with instances of user defined classes.\r\nFor classes that are not written in *Python* there might be problems, as the introspection methods used by `DeepImmutableProxy` may not work.\r\nThis problem is solved with the class methods `DeepImmutableProxy.register_immutable`, which can be used to tell `DeepImmutableProxy` that certain class is already immutable (hence, wrapping it with `DeepImmutableProxy` has no effect), and `DeepImmutableProxy.register_proxy`, that allows a user to register a subclass of `DeepImmutableProxy` that defines an immutable proxy specific for the given type.\r\n\r\nFor example, types `int`, `float`, `str`, `bytes`, and some others, have already been registered to be immutable:\r\n\r\n```python\r\n>>> no_proxy = DeepImmutableProxy(3)\r\n>>> type(no_proxy) == int\r\nTrue\r\n\r\n```\r\n\r\nThe class method `DeepImmutableProxy.get_registered_immutable` returns a list of the currently registered immutable classes:\r\n\r\n```python\r\n>>> [cls.__name__ for cls in DeepImmutableProxy.get_registered_immutable()] # doctest: +ELLIPSIS\r\n['bytes', 'int', 'float', 'complex', 'str', 'NoneType', ...]\r\n\r\n``` \r\n\r\nSome containers like tuples are immutable only if their content is immutable.  \r\nThus, when applying `DeepImmutableProxy` to a container like a tuple, we need a specific wrapper.\r\nThis wrapper is already defined in this package.\r\n\r\nFor example, the second element of the next tuple is mutable, so the resulting object is mutable.\r\nHowever, when wrapped with `DeepImmutableProxy` it really becomes immutable.\r\nThis is achieved by returning a special wrapper for tuples.\r\nThe usual `DeepImmutableProxy` class does not work because attribute access for built-in types does not follow the standard Python rules.\r\n\r\n```python\r\n>>> mutable_tuple_proxy = DeepImmutableProxy(('immutable', ['mutable']))\r\n>>> mutable_tuple_proxy\r\nTupleImmutableProxy(('immutable', ['mutable']))\r\n\r\n```\r\n\r\nThe first item of the tuple is a `str`, which is immutable, so it is returned as is.\r\n\r\n```python\r\n>>> mutable_tuple_proxy[0]\r\n'immutable'\r\n\r\n```\r\n\r\nThe second item is mutable, so it is wrapped and cannot be modified either.\r\n\r\n```python\r\n>>> mutable_tuple_proxy[1]\r\nListImmutableProxy(['mutable'])\r\n\r\n```\r\n\r\nThis second item is of type list, so it also needs a special wrapper.\r\nMethods that do not mutate the content can be used normally.\r\n\r\n```python\r\n>>> len(mutable_tuple_proxy[1]), mutable_tuple_proxy[1].index('mutable')\r\n(1, 0)\r\n\r\n```\r\n\r\nHowever, methods that do mutate content are forbidden.\r\n\r\n```python\r\n>>> try:\r\n...     mutable_tuple_proxy[1].append('another')\r\n... except AttributeError as error:\r\n...     print(error)\r\nObject of type \"ListImmutableProxy\" does not have attribute \"append\".\r\n    \r\n```\r\n\r\n\r\n## Copying\r\n\r\n`DeepImmutableProxy` objects include a hook for `__copy__` and `__deepcopy__` that copies the underlying object.  The copy is mutable.\r\n\r\n```python\r\n>>> from copy import deepcopy\r\n\r\n>>> copied = deepcopy(outer_proxy)\r\n>>> print(copied)\r\nOuter(Inner(1, 2), Inner(3, 4))\r\n\r\n>>> copied.a.x = -1\r\n\r\n>>> try:\r\n...     outer_proxy.a.x = -1\r\n... except ConstantAttributeError as error:\r\n...     print(error)\r\n'DeepImmutableProxy[Inner]' object is immutable. Cannot change attribute 'x' after initialization.\r\n\r\n```\r\n\r\n",
    "bugtrack_url": null,
    "license": "The MIT License (MIT)  Copyright \u00a9 2023 Jos\u00e9 Carlos D\u00edaz-Ramos  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \u201cSoftware\u201d), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.  THE SOFTWARE IS PROVIDED \u201cAS IS\u201d, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  ",
    "summary": "Provides a proxy to an object that prevents mutation at any depth",
    "version": "0.1.1",
    "split_keywords": [
        "immutable object",
        "proxy"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "d36ec73d4d701087b3f46fcfc4689a2f84b77d7c604f7abcfe7c3d75800633b1",
                "md5": "bd5f3d6d8e22f362cb41972778a18a46",
                "sha256": "0d84fd36c6a807ec4ae7ad7df3006a82b6c117709e0c80bbc599b78769134585"
            },
            "downloads": -1,
            "filename": "immutable_proxy-0.1.1-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "bd5f3d6d8e22f362cb41972778a18a46",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.9",
            "size": 13145,
            "upload_time": "2023-04-01T15:17:36",
            "upload_time_iso_8601": "2023-04-01T15:17:36.217416Z",
            "url": "https://files.pythonhosted.org/packages/d3/6e/c73d4d701087b3f46fcfc4689a2f84b77d7c604f7abcfe7c3d75800633b1/immutable_proxy-0.1.1-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "45bd9dda13eba8db5fb8b6e6c40481455efcc30102cf65f322b900da42014a1b",
                "md5": "80917d441bcda9a047a8ad5715235ad4",
                "sha256": "499947c43131b3ee48f1a79ba2195b0efcf425f536818cf9359d12d96d9704d0"
            },
            "downloads": -1,
            "filename": "immutable-proxy-0.1.1.tar.gz",
            "has_sig": false,
            "md5_digest": "80917d441bcda9a047a8ad5715235ad4",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.9",
            "size": 20465,
            "upload_time": "2023-04-01T15:17:39",
            "upload_time_iso_8601": "2023-04-01T15:17:39.435321Z",
            "url": "https://files.pythonhosted.org/packages/45/bd/9dda13eba8db5fb8b6e6c40481455efcc30102cf65f322b900da42014a1b/immutable-proxy-0.1.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2023-04-01 15:17:39",
    "github": false,
    "gitlab": false,
    "bitbucket": false,
    "lcname": "immutable-proxy"
}
        
Elapsed time: 0.05024s