summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCoprDistGit <infra@openeuler.org>2023-05-05 04:41:28 +0000
committerCoprDistGit <infra@openeuler.org>2023-05-05 04:41:28 +0000
commit662e531f22d6d1ec5a083395337add3538d84234 (patch)
treefde310116fd3f6d7316cddf236e3dfb25ea6d68b
parent059ecb1824c4fd9b13f8a41dbc3245a3a558874d (diff)
automatic import of python-gelidumopeneuler20.03
-rw-r--r--.gitignore1
-rw-r--r--python-gelidum.spec1263
-rw-r--r--sources1
3 files changed, 1265 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
index e69de29..1176df6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -0,0 +1 @@
+/gelidum-0.6.0.tar.gz
diff --git a/python-gelidum.spec b/python-gelidum.spec
new file mode 100644
index 0000000..15b0175
--- /dev/null
+++ b/python-gelidum.spec
@@ -0,0 +1,1263 @@
+%global _empty_manifest_terminate_build 0
+Name: python-gelidum
+Version: 0.6.0
+Release: 1
+Summary: Freeze your python objects
+License: MIT
+URL: https://github.com/diegojromerolopez/gelidum
+Source0: https://mirrors.nju.edu.cn/pypi/web/packages/e3/40/e8d6daafbf6776697e5ce8a663a59a07d70f3d4c65b60ba2c8d14fc8374b/gelidum-0.6.0.tar.gz
+BuildArch: noarch
+
+
+%description
+# gelidum
+
+![test](https://github.com/diegojromerolopez/gelidum/actions/workflows/test.yml/badge.svg)
+[![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
+[![Maintenance](https://img.shields.io/badge/Maintained%3F-yes-green.svg)](https://github.com/diegojromerolopez/gelidum/graphs/commit-activity)
+[![made-with-python](https://img.shields.io/badge/Made%20with-Python-1f425f.svg)](https://www.python.org/)
+[![PyPI pyversions](https://img.shields.io/pypi/pyversions/gelidum.svg)](https://pypi.python.org/pypi/gelidum/)
+[![PyPI version gelidum](https://badge.fury.io/py/gelidum.svg)](https://pypi.python.org/pypi/gelidum/)
+[![PyPI status](https://img.shields.io/pypi/status/gelidum.svg)](https://pypi.python.org/pypi/gelidum/)
+[![PyPI download month](https://img.shields.io/pypi/dm/gelidum.svg)](https://pypi.python.org/pypi/gelidum/)
+[![Maintainability](https://api.codeclimate.com/v1/badges/331d7d462e578ce5733e/maintainability)](https://codeclimate.com/github/diegojromerolopez/gelidum/maintainability)
+[![Test Coverage](https://api.codeclimate.com/v1/badges/331d7d462e578ce5733e/test_coverage)](https://codeclimate.com/github/diegojromerolopez/gelidum/test_coverage)
+
+Freeze your objects in python.
+
+![Gelidum](https://raw.githubusercontent.com/diegojromerolopez/gelidum/main/resources/gelidum.jpg "Gelidum image")
+
+[Public domain photo](https://www.flickr.com/photos/140296773@N07/45771062385) by [Katsujiro Maekawa](https://www.flickr.com/photos/hjppo/)
+
+| Latin | English |
+| -------------------------------------------------------- | -------------------------------------------------------- |
+| *Caelum est hieme frigidum et gelidum; myrtos oleas quaeque alia assiduo tepore laetantur, aspernatur ac respuit; laurum tamen patitur atque etiam nitidissimam profert, interdum sed non saepius quam sub urbe nostra necat.* | *In winter the air is cold and frosty: myrtles, olives and all other trees which require constant warmth for them to do well, the climate rejects and spurns, though it allows laurel to grow, and even brings it to a luxuriant leaf. Occasionally, however, it kills it, but that does not happen more frequently than in the neighbourhood of Rome.* |
+
+[The Letters of the Younger Pliny, First Series — Volume 1 by the Younger Pliny](https://www.gutenberg.org/ebooks/3234), translated to English by John Benjamin Firth.
+
+## Introduction
+Inspired by the method freeze found in other languages like Javascript,
+this package tries to make immutable objects to make it easier avoiding
+accidental modifications in your code.
+
+See more comments about this project in this [Show HN](https://news.ycombinator.com/item?id=27507524).
+
+## Major highlights
+- **freeze** method creates objects with the same attributes of inputs that cannot be expanded or modified.
+- Frozen object creation is thread-safe.
+- Structural sharing: any frozen object is shared by all of its user objects. There is no copy
+performed, only reference.
+- cpython and pypy support.
+
+## How it works
+In case of the [builtin types](https://docs.python.org/3/library/stdtypes.html)
+(bool, None, int, float, bytes, complex, str) it does nothing, as they are already immutable.
+
+For the list type, a [frozenlist](/gelidum/collections/frozenlist.py) with frozen items is returned.
+
+Tuples are already immutable, so a new tuple with frozen items is returned.
+
+When freezing a set, a [frozenzet](/gelidum/collections/frozenzet.py) of frozen items is returned.
+
+In the case of dicts, freezing one of them creates
+a new [frozendict](/gelidum/collections/frozendict.py)
+with the keys and frozen values of the original dict.
+
+This package, change the methods \_\_setattr\_\_, \_\_delattr\_\_, \_\_set\_\_,
+\_\_setitem\_\_, and \_\_delitem\_\_ of the object argument and all of its attributed recursively,
+making them raise an exception if the developer tries to call them to modify
+the attributes of the instance.
+
+## How to use it
+
+### Freeze in the same object
+```python
+from typing import List
+from gelidum import freeze
+
+class Dummy(object):
+ def __init__(self, attr1: int, attr2: List):
+ self.attr1 = attr1
+ self.attr2 = attr2
+
+dummy = Dummy(1, [2, 3, 4])
+frozen_dummy = freeze(dummy, on_freeze="inplace")
+assert(id(dummy) == id(frozen_dummy))
+
+# Both raise exception
+new_value = 1
+dummy.attr1 = new_value
+frozen_dummy.attr1 = new_value
+
+# Both raise exception
+new_value_list = [1]
+dummy.attr2 = new_value_list
+frozen_dummy.attr2 = new_value_list
+```
+
+### Freeze in a new object
+
+#### Basic use
+```python
+from typing import List
+from gelidum import freeze
+
+class Dummy(object):
+ def __init__(self, attr1: int, attr2: List):
+ self.attr1 = attr1
+ self.attr2 = attr2
+
+dummy = Dummy(1, [2, 3, 4])
+# on_freeze="copy" by default
+frozen_dummy = freeze(dummy)
+assert(id(dummy) != id(frozen_dummy))
+
+# on_freeze="copy" by default
+frozen_object_dummy2 = freeze(dummy, on_freeze="copy")
+
+# It doesn't raise an exception,
+# dummy keeps being a mutable object
+new_attr1_value = 99
+dummy.attr1 = new_attr1_value
+
+# Raises exception,
+# frozen_dummy is an immutable object
+frozen_dummy.attr1 = new_attr1_value
+```
+
+#### Access to original object
+The parameter on_freeze admits a callable, so you can have
+some side effects when freezing objects.
+
+There is a particular callable class that allows
+returning the original object:
+
+```python
+from gelidum import freeze
+from gelidum.on_freeze import OnFreezeOriginalObjTracker
+
+class Dummy(object):
+ def __init__(self, value1: int, value2: int):
+ self.attr1 = value1
+ self.attr2 = value2
+
+dummy = Dummy(value1=1, value2=2)
+
+freezer = OnFreezeOriginalObjTracker()
+
+frozen_dummy = freeze(dummy, on_freeze=freezer)
+original_obj = freezer.original_obj
+
+assert(dummy == original_obj)
+```
+
+Note that in the earlier case the original object is not frozen
+but a copy of it.
+
+#### What to do when trying to update an attribute
+```python
+import logging
+from gelidum import freeze
+
+class SharedState(object):
+ def __init__(self, count: int):
+ self.count = count
+
+shared_state = SharedState(1)
+
+# on_update="exception": raises an exception when an update is tried
+frozen_shared_state = freeze(shared_state, on_update="exception")
+frozen_shared_state.count = 4 # Raises exception
+
+# on_update="warning": shows a warning in console exception when an update is tried
+frozen_shared_state = freeze(shared_state, on_update="warning")
+frozen_shared_state.count = 4 # Shows a warning in console
+
+# on_update="nothing": does nothing when an update is tried
+frozen_shared_state = freeze(shared_state, on_update="nothing")
+frozen_shared_state.count = 4 # Does nothing, as this update did not exist
+
+# on_update=<lambda message, *args, **kwargs>: calls the function
+# Note the parameters of that function must be message, *args, **kwargs
+frozen_shared_state = freeze(
+ shared_state,
+ on_update=lambda message, *args, **kwargs: logging.warning(message)
+)
+frozen_shared_state.count = 4 # Calls on_update function and logs in the warning level:
+ # "Can't assign 'count' on immutable instance"
+```
+
+
+### Freeze input params
+Use the decorator freeze_params to freeze the input parameters
+and avoid non-intended modifications:
+```python
+from typing import List
+from gelidum import freeze_params
+
+@freeze_params()
+def append_to_list(a_list: List, new_item: int):
+ a_list.append(new_item)
+```
+If freeze_params is called without arguments, all input parameters will be frozen.
+Otherwise, passing a set of parameters will inform the decorator of which named
+parameters must be frozen.
+
+```python
+from typing import List
+from gelidum import freeze_params
+
+@freeze_params(params={"list1", "list2"})
+def concat_lists(dest: List, list1: List, list2: List) -> List:
+ dest = list1 + list2
+ return dest
+
+# Freeze dest, list1 and list2
+concat_lists([], list1=[1, 2, 3], list2=[4, 5, 6])
+
+# Freeze list1 and list2
+concat_lists(dest=[], list1=[1, 2, 3], list2=[4, 5, 6])
+```
+
+Always use kwargs unless you want to freeze the args params. A good way to enforce this is by making the
+function have keyword-only arguments:
+
+```python
+from typing import List
+from gelidum import freeze_params
+
+@freeze_params(params={"list1", "list2"})
+def concat_lists_in(*, dest: List, list1: List, list2: List):
+ dest = list1 + list2
+ return dest
+```
+
+You can use the **Final typehint from gelidum** to signal that an argument is immutable:
+
+```python
+from typing import List
+from gelidum import freeze_final, Final
+
+@freeze_final
+def concatenate_lists(list1: Final[List], list2: Final[List]):
+ return list1 + list2
+```
+
+Finally, take in account that all freezing is done in a new object (i.e. freeze with on_freeze="copy").
+It makes no sense to freeze a parameter of a function that could be used later, *outside*
+said function.
+
+### Check original (i.e. "hot") class
+- **get_gelidum_hot_class_name**: returns the name of hot class.
+- **get_gelidum_hot_class_module** returns the module reference where the hot class was.
+
+## Collections
+There are four immutable collections in the gelidum.collections module.
+
+- frozendict
+- frozenlist
+- frozenzet (frozenset is already a builtin type in Python)
+
+All of these classes can be used to make sure a collection of objects
+is not modified. Indeed, when creating a new collection object, you
+can pass a custom freeze function, to customize the freezing process
+of each of its items, e.g.:
+
+```python
+import logging
+from gelidum.freeze import freeze
+from gelidum.collections import frozenzet
+from gelidum.typing import FrozenType
+from typing import Any
+
+
+def my_freeze_func(item: Any) -> FrozenType:
+ logging.debug(f"Freezing item {item}")
+ return freeze(item, on_update="exception", on_freeze="copy")
+
+frozen_zet = frozenzet([1, 2, 3], freeze_func=my_freeze_func)
+```
+
+## Rationale and background information
+Inspired by my old work with Ruby on Rails, I decided to create a mechanism to make
+objects immutable in Python. The first aim was to do a tool to avoid accidental
+modifications on the objects while passing them through an execution flow.
+
+Anyways, as time passed I thought that an implementation of a programming language
+with real threading support (i.e. not cpython) could be benefited from this feature.
+I know that both cpython and pypy implementations of the Python programming
+language have a [GIL](https://en.wikipedia.org/wiki/Global_interpreter_lock) but IronPython
+and [Graalpython](https://github.com/oracle/graalpython) don't.
+IronPython3 has no support for typehintings yet,
+but Graalpython seems to work fine, so more experiments will be coming.
+
+On the other hand, I'm also interested in creating functional data structures
+in this package, easing the life of developers that do not want side effects.
+
+It's true that the complexity of Python does not play well with this kind of library.
+Thus, Python usually serves as easy interface with native libraries (pandas, numpy, etc.)
+However, this project is fun to develop and maybe with the popularity of alternative
+implementations of Python some work can be done to improve performance.
+
+More information can be seen in this [Show HN post](https://news.ycombinator.com/item?id=27507524)
+and some appreciated feedback of the users of that great community.
+
+## Limitations
+- dict, list, tuple and set objects cannot be modified inplace although the flag inplace is set.
+- file handler attributes are not supported. An exception is raised when trying to freeze
+ an object with them.
+- frozen objects cannot be serialized with [marshal](https://docs.python.org/3/library/marshal.html).
+- frozen objects cannot be (deep)-copied. This limitation is intended to make structural sharing easier.
+- Classes with \_\_slots\_\_:
+ - cannot be frozen in-place.
+ - will be frozen with a unique class. The frozen class will not be shared by instances of the same class.
+
+## Advice & comments on use
+### On_update parameter of freeze function
+Use on_update with a callable to store when somebody tried to write in the immutable object:
+```python
+import datetime
+import logging
+import threading
+from gelidum import freeze
+
+
+class Dummy(object):
+ def __init__(self, attr: int):
+ self.attr = attr
+
+
+class FrozenDummyUpdateTryRecorder:
+ LOCK = threading.Lock()
+ written_tries = []
+
+ @classmethod
+ def add_writing_try(cls, message, *args, **kwargs):
+ logging.warning(message)
+ with cls.LOCK:
+ cls.written_tries.append({
+ "message": message,
+ "args": args,
+ "kwargs": kwargs,
+ "datetime": datetime.datetime.utcnow()
+ })
+
+
+dummy = Dummy(1)
+frozen_dummy = freeze(
+ dummy,
+ on_update=FrozenDummyUpdateTryRecorder.add_writing_try
+ )
+# It will call FrozenDummyUpdateTryRecorder.add_writing_try
+# and will continue the execution flow with the next sentence.
+frozen_dummy.attr = 4
+```
+
+### On_freeze parameter of freeze function
+The parameter on_freeze of the function freeze must be a string or a callable.
+This parameter informs of what to do with the object that will be frozen.
+Should it be the same input object frozen or a copy of it?
+
+If it has a string as parameter, values "inplace" and "copy" are allowed.
+A value of "inplace" will make the freeze method to try to freeze the object
+as-is, while a value of "copy" will make a copy of the original object and then,
+freeze that copy. **These are the recommended parameters**.
+
+On the other hand, the interesting part is to define a custom on_freeze method.
+This method must return an object of the same type of the input.
+**This returned will be frozen, and returned to the caller of freeze**.
+
+Note this parameter has no interference with the structural sharing of the frozen objects.
+Any frozen object that have several references to it will be shared, not copied.
+
+```python
+import copy
+
+def on_freeze(self, obj: object) -> object:
+ frozen_object = copy.deepcopy(obj)
+ # log, copy the original method or do any other
+ # custom action in this function
+ return frozen_object
+```
+As seen earlier, there is also the possibility to
+pass a callable object. If you would like you can even
+define your own on_freeze functions by inheriting
+from classes:
+
+- OnFreezeCopier
+- OnFreezeIdentityFunc
+
+See some examples in [on_freeze.py](/gelidum/on_freeze.py) file.
+
+## Dependencies
+This package has no dependencies.
+
+
+## Roadmap
+- [x] Freeze only when attributes are modified?
+ Not exactly but structural sharing is used.
+- [x] Include immutable collections.
+- [ ] [Graalpython](https://github.com/oracle/graalpython) support.
+- [ ] Make some use-cases with threading/async module (i.e. server)
+
+
+## Collaborations
+This project is open to collaborations. Make a PR or an issue,
+and I'll take a look to it.
+
+## License
+[MIT](LICENSE) license, but if you need any other contact me.
+
+
+%package -n python3-gelidum
+Summary: Freeze your python objects
+Provides: python-gelidum
+BuildRequires: python3-devel
+BuildRequires: python3-setuptools
+BuildRequires: python3-pip
+%description -n python3-gelidum
+# gelidum
+
+![test](https://github.com/diegojromerolopez/gelidum/actions/workflows/test.yml/badge.svg)
+[![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
+[![Maintenance](https://img.shields.io/badge/Maintained%3F-yes-green.svg)](https://github.com/diegojromerolopez/gelidum/graphs/commit-activity)
+[![made-with-python](https://img.shields.io/badge/Made%20with-Python-1f425f.svg)](https://www.python.org/)
+[![PyPI pyversions](https://img.shields.io/pypi/pyversions/gelidum.svg)](https://pypi.python.org/pypi/gelidum/)
+[![PyPI version gelidum](https://badge.fury.io/py/gelidum.svg)](https://pypi.python.org/pypi/gelidum/)
+[![PyPI status](https://img.shields.io/pypi/status/gelidum.svg)](https://pypi.python.org/pypi/gelidum/)
+[![PyPI download month](https://img.shields.io/pypi/dm/gelidum.svg)](https://pypi.python.org/pypi/gelidum/)
+[![Maintainability](https://api.codeclimate.com/v1/badges/331d7d462e578ce5733e/maintainability)](https://codeclimate.com/github/diegojromerolopez/gelidum/maintainability)
+[![Test Coverage](https://api.codeclimate.com/v1/badges/331d7d462e578ce5733e/test_coverage)](https://codeclimate.com/github/diegojromerolopez/gelidum/test_coverage)
+
+Freeze your objects in python.
+
+![Gelidum](https://raw.githubusercontent.com/diegojromerolopez/gelidum/main/resources/gelidum.jpg "Gelidum image")
+
+[Public domain photo](https://www.flickr.com/photos/140296773@N07/45771062385) by [Katsujiro Maekawa](https://www.flickr.com/photos/hjppo/)
+
+| Latin | English |
+| -------------------------------------------------------- | -------------------------------------------------------- |
+| *Caelum est hieme frigidum et gelidum; myrtos oleas quaeque alia assiduo tepore laetantur, aspernatur ac respuit; laurum tamen patitur atque etiam nitidissimam profert, interdum sed non saepius quam sub urbe nostra necat.* | *In winter the air is cold and frosty: myrtles, olives and all other trees which require constant warmth for them to do well, the climate rejects and spurns, though it allows laurel to grow, and even brings it to a luxuriant leaf. Occasionally, however, it kills it, but that does not happen more frequently than in the neighbourhood of Rome.* |
+
+[The Letters of the Younger Pliny, First Series — Volume 1 by the Younger Pliny](https://www.gutenberg.org/ebooks/3234), translated to English by John Benjamin Firth.
+
+## Introduction
+Inspired by the method freeze found in other languages like Javascript,
+this package tries to make immutable objects to make it easier avoiding
+accidental modifications in your code.
+
+See more comments about this project in this [Show HN](https://news.ycombinator.com/item?id=27507524).
+
+## Major highlights
+- **freeze** method creates objects with the same attributes of inputs that cannot be expanded or modified.
+- Frozen object creation is thread-safe.
+- Structural sharing: any frozen object is shared by all of its user objects. There is no copy
+performed, only reference.
+- cpython and pypy support.
+
+## How it works
+In case of the [builtin types](https://docs.python.org/3/library/stdtypes.html)
+(bool, None, int, float, bytes, complex, str) it does nothing, as they are already immutable.
+
+For the list type, a [frozenlist](/gelidum/collections/frozenlist.py) with frozen items is returned.
+
+Tuples are already immutable, so a new tuple with frozen items is returned.
+
+When freezing a set, a [frozenzet](/gelidum/collections/frozenzet.py) of frozen items is returned.
+
+In the case of dicts, freezing one of them creates
+a new [frozendict](/gelidum/collections/frozendict.py)
+with the keys and frozen values of the original dict.
+
+This package, change the methods \_\_setattr\_\_, \_\_delattr\_\_, \_\_set\_\_,
+\_\_setitem\_\_, and \_\_delitem\_\_ of the object argument and all of its attributed recursively,
+making them raise an exception if the developer tries to call them to modify
+the attributes of the instance.
+
+## How to use it
+
+### Freeze in the same object
+```python
+from typing import List
+from gelidum import freeze
+
+class Dummy(object):
+ def __init__(self, attr1: int, attr2: List):
+ self.attr1 = attr1
+ self.attr2 = attr2
+
+dummy = Dummy(1, [2, 3, 4])
+frozen_dummy = freeze(dummy, on_freeze="inplace")
+assert(id(dummy) == id(frozen_dummy))
+
+# Both raise exception
+new_value = 1
+dummy.attr1 = new_value
+frozen_dummy.attr1 = new_value
+
+# Both raise exception
+new_value_list = [1]
+dummy.attr2 = new_value_list
+frozen_dummy.attr2 = new_value_list
+```
+
+### Freeze in a new object
+
+#### Basic use
+```python
+from typing import List
+from gelidum import freeze
+
+class Dummy(object):
+ def __init__(self, attr1: int, attr2: List):
+ self.attr1 = attr1
+ self.attr2 = attr2
+
+dummy = Dummy(1, [2, 3, 4])
+# on_freeze="copy" by default
+frozen_dummy = freeze(dummy)
+assert(id(dummy) != id(frozen_dummy))
+
+# on_freeze="copy" by default
+frozen_object_dummy2 = freeze(dummy, on_freeze="copy")
+
+# It doesn't raise an exception,
+# dummy keeps being a mutable object
+new_attr1_value = 99
+dummy.attr1 = new_attr1_value
+
+# Raises exception,
+# frozen_dummy is an immutable object
+frozen_dummy.attr1 = new_attr1_value
+```
+
+#### Access to original object
+The parameter on_freeze admits a callable, so you can have
+some side effects when freezing objects.
+
+There is a particular callable class that allows
+returning the original object:
+
+```python
+from gelidum import freeze
+from gelidum.on_freeze import OnFreezeOriginalObjTracker
+
+class Dummy(object):
+ def __init__(self, value1: int, value2: int):
+ self.attr1 = value1
+ self.attr2 = value2
+
+dummy = Dummy(value1=1, value2=2)
+
+freezer = OnFreezeOriginalObjTracker()
+
+frozen_dummy = freeze(dummy, on_freeze=freezer)
+original_obj = freezer.original_obj
+
+assert(dummy == original_obj)
+```
+
+Note that in the earlier case the original object is not frozen
+but a copy of it.
+
+#### What to do when trying to update an attribute
+```python
+import logging
+from gelidum import freeze
+
+class SharedState(object):
+ def __init__(self, count: int):
+ self.count = count
+
+shared_state = SharedState(1)
+
+# on_update="exception": raises an exception when an update is tried
+frozen_shared_state = freeze(shared_state, on_update="exception")
+frozen_shared_state.count = 4 # Raises exception
+
+# on_update="warning": shows a warning in console exception when an update is tried
+frozen_shared_state = freeze(shared_state, on_update="warning")
+frozen_shared_state.count = 4 # Shows a warning in console
+
+# on_update="nothing": does nothing when an update is tried
+frozen_shared_state = freeze(shared_state, on_update="nothing")
+frozen_shared_state.count = 4 # Does nothing, as this update did not exist
+
+# on_update=<lambda message, *args, **kwargs>: calls the function
+# Note the parameters of that function must be message, *args, **kwargs
+frozen_shared_state = freeze(
+ shared_state,
+ on_update=lambda message, *args, **kwargs: logging.warning(message)
+)
+frozen_shared_state.count = 4 # Calls on_update function and logs in the warning level:
+ # "Can't assign 'count' on immutable instance"
+```
+
+
+### Freeze input params
+Use the decorator freeze_params to freeze the input parameters
+and avoid non-intended modifications:
+```python
+from typing import List
+from gelidum import freeze_params
+
+@freeze_params()
+def append_to_list(a_list: List, new_item: int):
+ a_list.append(new_item)
+```
+If freeze_params is called without arguments, all input parameters will be frozen.
+Otherwise, passing a set of parameters will inform the decorator of which named
+parameters must be frozen.
+
+```python
+from typing import List
+from gelidum import freeze_params
+
+@freeze_params(params={"list1", "list2"})
+def concat_lists(dest: List, list1: List, list2: List) -> List:
+ dest = list1 + list2
+ return dest
+
+# Freeze dest, list1 and list2
+concat_lists([], list1=[1, 2, 3], list2=[4, 5, 6])
+
+# Freeze list1 and list2
+concat_lists(dest=[], list1=[1, 2, 3], list2=[4, 5, 6])
+```
+
+Always use kwargs unless you want to freeze the args params. A good way to enforce this is by making the
+function have keyword-only arguments:
+
+```python
+from typing import List
+from gelidum import freeze_params
+
+@freeze_params(params={"list1", "list2"})
+def concat_lists_in(*, dest: List, list1: List, list2: List):
+ dest = list1 + list2
+ return dest
+```
+
+You can use the **Final typehint from gelidum** to signal that an argument is immutable:
+
+```python
+from typing import List
+from gelidum import freeze_final, Final
+
+@freeze_final
+def concatenate_lists(list1: Final[List], list2: Final[List]):
+ return list1 + list2
+```
+
+Finally, take in account that all freezing is done in a new object (i.e. freeze with on_freeze="copy").
+It makes no sense to freeze a parameter of a function that could be used later, *outside*
+said function.
+
+### Check original (i.e. "hot") class
+- **get_gelidum_hot_class_name**: returns the name of hot class.
+- **get_gelidum_hot_class_module** returns the module reference where the hot class was.
+
+## Collections
+There are four immutable collections in the gelidum.collections module.
+
+- frozendict
+- frozenlist
+- frozenzet (frozenset is already a builtin type in Python)
+
+All of these classes can be used to make sure a collection of objects
+is not modified. Indeed, when creating a new collection object, you
+can pass a custom freeze function, to customize the freezing process
+of each of its items, e.g.:
+
+```python
+import logging
+from gelidum.freeze import freeze
+from gelidum.collections import frozenzet
+from gelidum.typing import FrozenType
+from typing import Any
+
+
+def my_freeze_func(item: Any) -> FrozenType:
+ logging.debug(f"Freezing item {item}")
+ return freeze(item, on_update="exception", on_freeze="copy")
+
+frozen_zet = frozenzet([1, 2, 3], freeze_func=my_freeze_func)
+```
+
+## Rationale and background information
+Inspired by my old work with Ruby on Rails, I decided to create a mechanism to make
+objects immutable in Python. The first aim was to do a tool to avoid accidental
+modifications on the objects while passing them through an execution flow.
+
+Anyways, as time passed I thought that an implementation of a programming language
+with real threading support (i.e. not cpython) could be benefited from this feature.
+I know that both cpython and pypy implementations of the Python programming
+language have a [GIL](https://en.wikipedia.org/wiki/Global_interpreter_lock) but IronPython
+and [Graalpython](https://github.com/oracle/graalpython) don't.
+IronPython3 has no support for typehintings yet,
+but Graalpython seems to work fine, so more experiments will be coming.
+
+On the other hand, I'm also interested in creating functional data structures
+in this package, easing the life of developers that do not want side effects.
+
+It's true that the complexity of Python does not play well with this kind of library.
+Thus, Python usually serves as easy interface with native libraries (pandas, numpy, etc.)
+However, this project is fun to develop and maybe with the popularity of alternative
+implementations of Python some work can be done to improve performance.
+
+More information can be seen in this [Show HN post](https://news.ycombinator.com/item?id=27507524)
+and some appreciated feedback of the users of that great community.
+
+## Limitations
+- dict, list, tuple and set objects cannot be modified inplace although the flag inplace is set.
+- file handler attributes are not supported. An exception is raised when trying to freeze
+ an object with them.
+- frozen objects cannot be serialized with [marshal](https://docs.python.org/3/library/marshal.html).
+- frozen objects cannot be (deep)-copied. This limitation is intended to make structural sharing easier.
+- Classes with \_\_slots\_\_:
+ - cannot be frozen in-place.
+ - will be frozen with a unique class. The frozen class will not be shared by instances of the same class.
+
+## Advice & comments on use
+### On_update parameter of freeze function
+Use on_update with a callable to store when somebody tried to write in the immutable object:
+```python
+import datetime
+import logging
+import threading
+from gelidum import freeze
+
+
+class Dummy(object):
+ def __init__(self, attr: int):
+ self.attr = attr
+
+
+class FrozenDummyUpdateTryRecorder:
+ LOCK = threading.Lock()
+ written_tries = []
+
+ @classmethod
+ def add_writing_try(cls, message, *args, **kwargs):
+ logging.warning(message)
+ with cls.LOCK:
+ cls.written_tries.append({
+ "message": message,
+ "args": args,
+ "kwargs": kwargs,
+ "datetime": datetime.datetime.utcnow()
+ })
+
+
+dummy = Dummy(1)
+frozen_dummy = freeze(
+ dummy,
+ on_update=FrozenDummyUpdateTryRecorder.add_writing_try
+ )
+# It will call FrozenDummyUpdateTryRecorder.add_writing_try
+# and will continue the execution flow with the next sentence.
+frozen_dummy.attr = 4
+```
+
+### On_freeze parameter of freeze function
+The parameter on_freeze of the function freeze must be a string or a callable.
+This parameter informs of what to do with the object that will be frozen.
+Should it be the same input object frozen or a copy of it?
+
+If it has a string as parameter, values "inplace" and "copy" are allowed.
+A value of "inplace" will make the freeze method to try to freeze the object
+as-is, while a value of "copy" will make a copy of the original object and then,
+freeze that copy. **These are the recommended parameters**.
+
+On the other hand, the interesting part is to define a custom on_freeze method.
+This method must return an object of the same type of the input.
+**This returned will be frozen, and returned to the caller of freeze**.
+
+Note this parameter has no interference with the structural sharing of the frozen objects.
+Any frozen object that have several references to it will be shared, not copied.
+
+```python
+import copy
+
+def on_freeze(self, obj: object) -> object:
+ frozen_object = copy.deepcopy(obj)
+ # log, copy the original method or do any other
+ # custom action in this function
+ return frozen_object
+```
+As seen earlier, there is also the possibility to
+pass a callable object. If you would like you can even
+define your own on_freeze functions by inheriting
+from classes:
+
+- OnFreezeCopier
+- OnFreezeIdentityFunc
+
+See some examples in [on_freeze.py](/gelidum/on_freeze.py) file.
+
+## Dependencies
+This package has no dependencies.
+
+
+## Roadmap
+- [x] Freeze only when attributes are modified?
+ Not exactly but structural sharing is used.
+- [x] Include immutable collections.
+- [ ] [Graalpython](https://github.com/oracle/graalpython) support.
+- [ ] Make some use-cases with threading/async module (i.e. server)
+
+
+## Collaborations
+This project is open to collaborations. Make a PR or an issue,
+and I'll take a look to it.
+
+## License
+[MIT](LICENSE) license, but if you need any other contact me.
+
+
+%package help
+Summary: Development documents and examples for gelidum
+Provides: python3-gelidum-doc
+%description help
+# gelidum
+
+![test](https://github.com/diegojromerolopez/gelidum/actions/workflows/test.yml/badge.svg)
+[![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
+[![Maintenance](https://img.shields.io/badge/Maintained%3F-yes-green.svg)](https://github.com/diegojromerolopez/gelidum/graphs/commit-activity)
+[![made-with-python](https://img.shields.io/badge/Made%20with-Python-1f425f.svg)](https://www.python.org/)
+[![PyPI pyversions](https://img.shields.io/pypi/pyversions/gelidum.svg)](https://pypi.python.org/pypi/gelidum/)
+[![PyPI version gelidum](https://badge.fury.io/py/gelidum.svg)](https://pypi.python.org/pypi/gelidum/)
+[![PyPI status](https://img.shields.io/pypi/status/gelidum.svg)](https://pypi.python.org/pypi/gelidum/)
+[![PyPI download month](https://img.shields.io/pypi/dm/gelidum.svg)](https://pypi.python.org/pypi/gelidum/)
+[![Maintainability](https://api.codeclimate.com/v1/badges/331d7d462e578ce5733e/maintainability)](https://codeclimate.com/github/diegojromerolopez/gelidum/maintainability)
+[![Test Coverage](https://api.codeclimate.com/v1/badges/331d7d462e578ce5733e/test_coverage)](https://codeclimate.com/github/diegojromerolopez/gelidum/test_coverage)
+
+Freeze your objects in python.
+
+![Gelidum](https://raw.githubusercontent.com/diegojromerolopez/gelidum/main/resources/gelidum.jpg "Gelidum image")
+
+[Public domain photo](https://www.flickr.com/photos/140296773@N07/45771062385) by [Katsujiro Maekawa](https://www.flickr.com/photos/hjppo/)
+
+| Latin | English |
+| -------------------------------------------------------- | -------------------------------------------------------- |
+| *Caelum est hieme frigidum et gelidum; myrtos oleas quaeque alia assiduo tepore laetantur, aspernatur ac respuit; laurum tamen patitur atque etiam nitidissimam profert, interdum sed non saepius quam sub urbe nostra necat.* | *In winter the air is cold and frosty: myrtles, olives and all other trees which require constant warmth for them to do well, the climate rejects and spurns, though it allows laurel to grow, and even brings it to a luxuriant leaf. Occasionally, however, it kills it, but that does not happen more frequently than in the neighbourhood of Rome.* |
+
+[The Letters of the Younger Pliny, First Series — Volume 1 by the Younger Pliny](https://www.gutenberg.org/ebooks/3234), translated to English by John Benjamin Firth.
+
+## Introduction
+Inspired by the method freeze found in other languages like Javascript,
+this package tries to make immutable objects to make it easier avoiding
+accidental modifications in your code.
+
+See more comments about this project in this [Show HN](https://news.ycombinator.com/item?id=27507524).
+
+## Major highlights
+- **freeze** method creates objects with the same attributes of inputs that cannot be expanded or modified.
+- Frozen object creation is thread-safe.
+- Structural sharing: any frozen object is shared by all of its user objects. There is no copy
+performed, only reference.
+- cpython and pypy support.
+
+## How it works
+In case of the [builtin types](https://docs.python.org/3/library/stdtypes.html)
+(bool, None, int, float, bytes, complex, str) it does nothing, as they are already immutable.
+
+For the list type, a [frozenlist](/gelidum/collections/frozenlist.py) with frozen items is returned.
+
+Tuples are already immutable, so a new tuple with frozen items is returned.
+
+When freezing a set, a [frozenzet](/gelidum/collections/frozenzet.py) of frozen items is returned.
+
+In the case of dicts, freezing one of them creates
+a new [frozendict](/gelidum/collections/frozendict.py)
+with the keys and frozen values of the original dict.
+
+This package, change the methods \_\_setattr\_\_, \_\_delattr\_\_, \_\_set\_\_,
+\_\_setitem\_\_, and \_\_delitem\_\_ of the object argument and all of its attributed recursively,
+making them raise an exception if the developer tries to call them to modify
+the attributes of the instance.
+
+## How to use it
+
+### Freeze in the same object
+```python
+from typing import List
+from gelidum import freeze
+
+class Dummy(object):
+ def __init__(self, attr1: int, attr2: List):
+ self.attr1 = attr1
+ self.attr2 = attr2
+
+dummy = Dummy(1, [2, 3, 4])
+frozen_dummy = freeze(dummy, on_freeze="inplace")
+assert(id(dummy) == id(frozen_dummy))
+
+# Both raise exception
+new_value = 1
+dummy.attr1 = new_value
+frozen_dummy.attr1 = new_value
+
+# Both raise exception
+new_value_list = [1]
+dummy.attr2 = new_value_list
+frozen_dummy.attr2 = new_value_list
+```
+
+### Freeze in a new object
+
+#### Basic use
+```python
+from typing import List
+from gelidum import freeze
+
+class Dummy(object):
+ def __init__(self, attr1: int, attr2: List):
+ self.attr1 = attr1
+ self.attr2 = attr2
+
+dummy = Dummy(1, [2, 3, 4])
+# on_freeze="copy" by default
+frozen_dummy = freeze(dummy)
+assert(id(dummy) != id(frozen_dummy))
+
+# on_freeze="copy" by default
+frozen_object_dummy2 = freeze(dummy, on_freeze="copy")
+
+# It doesn't raise an exception,
+# dummy keeps being a mutable object
+new_attr1_value = 99
+dummy.attr1 = new_attr1_value
+
+# Raises exception,
+# frozen_dummy is an immutable object
+frozen_dummy.attr1 = new_attr1_value
+```
+
+#### Access to original object
+The parameter on_freeze admits a callable, so you can have
+some side effects when freezing objects.
+
+There is a particular callable class that allows
+returning the original object:
+
+```python
+from gelidum import freeze
+from gelidum.on_freeze import OnFreezeOriginalObjTracker
+
+class Dummy(object):
+ def __init__(self, value1: int, value2: int):
+ self.attr1 = value1
+ self.attr2 = value2
+
+dummy = Dummy(value1=1, value2=2)
+
+freezer = OnFreezeOriginalObjTracker()
+
+frozen_dummy = freeze(dummy, on_freeze=freezer)
+original_obj = freezer.original_obj
+
+assert(dummy == original_obj)
+```
+
+Note that in the earlier case the original object is not frozen
+but a copy of it.
+
+#### What to do when trying to update an attribute
+```python
+import logging
+from gelidum import freeze
+
+class SharedState(object):
+ def __init__(self, count: int):
+ self.count = count
+
+shared_state = SharedState(1)
+
+# on_update="exception": raises an exception when an update is tried
+frozen_shared_state = freeze(shared_state, on_update="exception")
+frozen_shared_state.count = 4 # Raises exception
+
+# on_update="warning": shows a warning in console exception when an update is tried
+frozen_shared_state = freeze(shared_state, on_update="warning")
+frozen_shared_state.count = 4 # Shows a warning in console
+
+# on_update="nothing": does nothing when an update is tried
+frozen_shared_state = freeze(shared_state, on_update="nothing")
+frozen_shared_state.count = 4 # Does nothing, as this update did not exist
+
+# on_update=<lambda message, *args, **kwargs>: calls the function
+# Note the parameters of that function must be message, *args, **kwargs
+frozen_shared_state = freeze(
+ shared_state,
+ on_update=lambda message, *args, **kwargs: logging.warning(message)
+)
+frozen_shared_state.count = 4 # Calls on_update function and logs in the warning level:
+ # "Can't assign 'count' on immutable instance"
+```
+
+
+### Freeze input params
+Use the decorator freeze_params to freeze the input parameters
+and avoid non-intended modifications:
+```python
+from typing import List
+from gelidum import freeze_params
+
+@freeze_params()
+def append_to_list(a_list: List, new_item: int):
+ a_list.append(new_item)
+```
+If freeze_params is called without arguments, all input parameters will be frozen.
+Otherwise, passing a set of parameters will inform the decorator of which named
+parameters must be frozen.
+
+```python
+from typing import List
+from gelidum import freeze_params
+
+@freeze_params(params={"list1", "list2"})
+def concat_lists(dest: List, list1: List, list2: List) -> List:
+ dest = list1 + list2
+ return dest
+
+# Freeze dest, list1 and list2
+concat_lists([], list1=[1, 2, 3], list2=[4, 5, 6])
+
+# Freeze list1 and list2
+concat_lists(dest=[], list1=[1, 2, 3], list2=[4, 5, 6])
+```
+
+Always use kwargs unless you want to freeze the args params. A good way to enforce this is by making the
+function have keyword-only arguments:
+
+```python
+from typing import List
+from gelidum import freeze_params
+
+@freeze_params(params={"list1", "list2"})
+def concat_lists_in(*, dest: List, list1: List, list2: List):
+ dest = list1 + list2
+ return dest
+```
+
+You can use the **Final typehint from gelidum** to signal that an argument is immutable:
+
+```python
+from typing import List
+from gelidum import freeze_final, Final
+
+@freeze_final
+def concatenate_lists(list1: Final[List], list2: Final[List]):
+ return list1 + list2
+```
+
+Finally, take in account that all freezing is done in a new object (i.e. freeze with on_freeze="copy").
+It makes no sense to freeze a parameter of a function that could be used later, *outside*
+said function.
+
+### Check original (i.e. "hot") class
+- **get_gelidum_hot_class_name**: returns the name of hot class.
+- **get_gelidum_hot_class_module** returns the module reference where the hot class was.
+
+## Collections
+There are four immutable collections in the gelidum.collections module.
+
+- frozendict
+- frozenlist
+- frozenzet (frozenset is already a builtin type in Python)
+
+All of these classes can be used to make sure a collection of objects
+is not modified. Indeed, when creating a new collection object, you
+can pass a custom freeze function, to customize the freezing process
+of each of its items, e.g.:
+
+```python
+import logging
+from gelidum.freeze import freeze
+from gelidum.collections import frozenzet
+from gelidum.typing import FrozenType
+from typing import Any
+
+
+def my_freeze_func(item: Any) -> FrozenType:
+ logging.debug(f"Freezing item {item}")
+ return freeze(item, on_update="exception", on_freeze="copy")
+
+frozen_zet = frozenzet([1, 2, 3], freeze_func=my_freeze_func)
+```
+
+## Rationale and background information
+Inspired by my old work with Ruby on Rails, I decided to create a mechanism to make
+objects immutable in Python. The first aim was to do a tool to avoid accidental
+modifications on the objects while passing them through an execution flow.
+
+Anyways, as time passed I thought that an implementation of a programming language
+with real threading support (i.e. not cpython) could be benefited from this feature.
+I know that both cpython and pypy implementations of the Python programming
+language have a [GIL](https://en.wikipedia.org/wiki/Global_interpreter_lock) but IronPython
+and [Graalpython](https://github.com/oracle/graalpython) don't.
+IronPython3 has no support for typehintings yet,
+but Graalpython seems to work fine, so more experiments will be coming.
+
+On the other hand, I'm also interested in creating functional data structures
+in this package, easing the life of developers that do not want side effects.
+
+It's true that the complexity of Python does not play well with this kind of library.
+Thus, Python usually serves as easy interface with native libraries (pandas, numpy, etc.)
+However, this project is fun to develop and maybe with the popularity of alternative
+implementations of Python some work can be done to improve performance.
+
+More information can be seen in this [Show HN post](https://news.ycombinator.com/item?id=27507524)
+and some appreciated feedback of the users of that great community.
+
+## Limitations
+- dict, list, tuple and set objects cannot be modified inplace although the flag inplace is set.
+- file handler attributes are not supported. An exception is raised when trying to freeze
+ an object with them.
+- frozen objects cannot be serialized with [marshal](https://docs.python.org/3/library/marshal.html).
+- frozen objects cannot be (deep)-copied. This limitation is intended to make structural sharing easier.
+- Classes with \_\_slots\_\_:
+ - cannot be frozen in-place.
+ - will be frozen with a unique class. The frozen class will not be shared by instances of the same class.
+
+## Advice & comments on use
+### On_update parameter of freeze function
+Use on_update with a callable to store when somebody tried to write in the immutable object:
+```python
+import datetime
+import logging
+import threading
+from gelidum import freeze
+
+
+class Dummy(object):
+ def __init__(self, attr: int):
+ self.attr = attr
+
+
+class FrozenDummyUpdateTryRecorder:
+ LOCK = threading.Lock()
+ written_tries = []
+
+ @classmethod
+ def add_writing_try(cls, message, *args, **kwargs):
+ logging.warning(message)
+ with cls.LOCK:
+ cls.written_tries.append({
+ "message": message,
+ "args": args,
+ "kwargs": kwargs,
+ "datetime": datetime.datetime.utcnow()
+ })
+
+
+dummy = Dummy(1)
+frozen_dummy = freeze(
+ dummy,
+ on_update=FrozenDummyUpdateTryRecorder.add_writing_try
+ )
+# It will call FrozenDummyUpdateTryRecorder.add_writing_try
+# and will continue the execution flow with the next sentence.
+frozen_dummy.attr = 4
+```
+
+### On_freeze parameter of freeze function
+The parameter on_freeze of the function freeze must be a string or a callable.
+This parameter informs of what to do with the object that will be frozen.
+Should it be the same input object frozen or a copy of it?
+
+If it has a string as parameter, values "inplace" and "copy" are allowed.
+A value of "inplace" will make the freeze method to try to freeze the object
+as-is, while a value of "copy" will make a copy of the original object and then,
+freeze that copy. **These are the recommended parameters**.
+
+On the other hand, the interesting part is to define a custom on_freeze method.
+This method must return an object of the same type of the input.
+**This returned will be frozen, and returned to the caller of freeze**.
+
+Note this parameter has no interference with the structural sharing of the frozen objects.
+Any frozen object that have several references to it will be shared, not copied.
+
+```python
+import copy
+
+def on_freeze(self, obj: object) -> object:
+ frozen_object = copy.deepcopy(obj)
+ # log, copy the original method or do any other
+ # custom action in this function
+ return frozen_object
+```
+As seen earlier, there is also the possibility to
+pass a callable object. If you would like you can even
+define your own on_freeze functions by inheriting
+from classes:
+
+- OnFreezeCopier
+- OnFreezeIdentityFunc
+
+See some examples in [on_freeze.py](/gelidum/on_freeze.py) file.
+
+## Dependencies
+This package has no dependencies.
+
+
+## Roadmap
+- [x] Freeze only when attributes are modified?
+ Not exactly but structural sharing is used.
+- [x] Include immutable collections.
+- [ ] [Graalpython](https://github.com/oracle/graalpython) support.
+- [ ] Make some use-cases with threading/async module (i.e. server)
+
+
+## Collaborations
+This project is open to collaborations. Make a PR or an issue,
+and I'll take a look to it.
+
+## License
+[MIT](LICENSE) license, but if you need any other contact me.
+
+
+%prep
+%autosetup -n gelidum-0.6.0
+
+%build
+%py3_build
+
+%install
+%py3_install
+install -d -m755 %{buildroot}/%{_pkgdocdir}
+if [ -d doc ]; then cp -arf doc %{buildroot}/%{_pkgdocdir}; fi
+if [ -d docs ]; then cp -arf docs %{buildroot}/%{_pkgdocdir}; fi
+if [ -d example ]; then cp -arf example %{buildroot}/%{_pkgdocdir}; fi
+if [ -d examples ]; then cp -arf examples %{buildroot}/%{_pkgdocdir}; fi
+pushd %{buildroot}
+if [ -d usr/lib ]; then
+ find usr/lib -type f -printf "/%h/%f\n" >> filelist.lst
+fi
+if [ -d usr/lib64 ]; then
+ find usr/lib64 -type f -printf "/%h/%f\n" >> filelist.lst
+fi
+if [ -d usr/bin ]; then
+ find usr/bin -type f -printf "/%h/%f\n" >> filelist.lst
+fi
+if [ -d usr/sbin ]; then
+ find usr/sbin -type f -printf "/%h/%f\n" >> filelist.lst
+fi
+touch doclist.lst
+if [ -d usr/share/man ]; then
+ find usr/share/man -type f -printf "/%h/%f.gz\n" >> doclist.lst
+fi
+popd
+mv %{buildroot}/filelist.lst .
+mv %{buildroot}/doclist.lst .
+
+%files -n python3-gelidum -f filelist.lst
+%dir %{python3_sitelib}/*
+
+%files help -f doclist.lst
+%{_docdir}/*
+
+%changelog
+* Fri May 05 2023 Python_Bot <Python_Bot@openeuler.org> - 0.6.0-1
+- Package Spec generated
diff --git a/sources b/sources
new file mode 100644
index 0000000..283753b
--- /dev/null
+++ b/sources
@@ -0,0 +1 @@
+2206f282ef898bfae8ffb52b658d847d gelidum-0.6.0.tar.gz