Michio JP

Michio JP

1628437362

How to Freeze Your Objects in Python

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.

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 (bool, None, int, float, bytes, complex, str) it does nothing, as they are already immutable.

For the list type, a tuple with frozen items is returned.

Tuples are already immutable, so a new tuple with frozen items is returned.

For sets, frozensets of frozen items are returned.

For dicts, it creates a new frozendict 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

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

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])
# inplace=False by default
frozen_dummy = freeze(dummy)
assert(id(dummy) != id(frozen_dummy))

# inplace=False 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

What to do when trying to update an attribute

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:

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.

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:

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:

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.:

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 but IronPython and 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 and some appreciated feedback of the users of that great community.

Limitations

  • dict, list, tuple and set 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.
  • frozen objects cannot be (deep)-copied. This limitation is intended to make structural sharing easier.
  • Classes with slots cannot be frozen.

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:

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 function. 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.

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

Dependencies

This package has no dependencies.

Roadmap

  • Freeze only when attributes are modified? Not exactly but structural sharing is used.
  • Include immutable collections.
  • Graalpython support.
  • Make some use-cases with threading/async module (i.e. server)

Download Details: 
 

Author: diegojromerolopez

Official Website: https://github.com/diegojromerolopez/gelidum

What is GEEK

Buddha Community

Arvel  Parker

Arvel Parker

1591611780

How to Find Ulimit For user on Linux

How can I find the correct ulimit values for a user account or process on Linux systems?

For proper operation, we must ensure that the correct ulimit values set after installing various software. The Linux system provides means of restricting the number of resources that can be used. Limits set for each Linux user account. However, system limits are applied separately to each process that is running for that user too. For example, if certain thresholds are too low, the system might not be able to server web pages using Nginx/Apache or PHP/Python app. System resource limits viewed or set with the NA command. Let us see how to use the ulimit that provides control over the resources available to the shell and processes.

#[object object] #[object object] #[object object] #[object object] #[object object] #[object object] #[object object] #[object object] #[object object] #[object object]

MEAN Stack Tutorial MongoDB ExpressJS AngularJS NodeJS

We are going to build a full stack Todo App using the MEAN (MongoDB, ExpressJS, AngularJS and NodeJS). This is the last part of three-post series tutorial.

MEAN Stack tutorial series:

AngularJS tutorial for beginners (Part I)
Creating RESTful APIs with NodeJS and MongoDB Tutorial (Part II)
MEAN Stack Tutorial: MongoDB, ExpressJS, AngularJS and NodeJS (Part III) 👈 you are here
Before completing the app, let’s cover some background about the this stack. If you rather jump to the hands-on part click here to get started.

#[object object] #[object object] #[object object] #[object object] #[object object] #[object object] #[object object] #[object object]

systemctl List All Failed Units/Services on Linux

Is there is a command to print list all failed units or services when using systemd on Linux? Can you tell me the systemctl command to list all failed services on Linux?

This quick tutorial explains how to find/list all failed systemd services/units on Linux operating systems using the systemctl command.

#[object object] #[object object] #[object object] #[object object] #[object object] #[object object] #[object object]

CentOS Linux 8.2 Released and Here is How to Upgrade it

CentOS Linux 8.2 (2004) released. It is a Linux distribution derived from RHEL (Red Hat Enterprise Linux) 8.2 source code. CentOS was created when Red Hat stopped providing RHEL free. CentOS 8.2 gives complete control of its open-source software packages and is fully customized for research needs or for running a high-performance website without the need for license fees. Let us see what’s new in CentOS 8.2 (2004) and how to upgrade existing CentOS 8.1.1199 server to 8.2.2004 using the command line.

#[object object] #[object object] #[object object] #[object object] #[object object] #[object object] #[object object]

Tyrique  Littel

Tyrique Littel

1597723200

FreeBSD s3cmd failed [SSL CERTIFICATE_VERIFY_FAILED]

When I install s3cmd package on my FreeBSD system and try to use the s3cmd command I get the following error:

_ERROR: Test failed: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (ssl.c:1091)

How do I fix this problem on FreeBSD Unix system?

Amazon Simple Storage Service (s3 ) is object storage through a web service interface or API. You can store all sorts of files. FreeBSD is free and open-source operating systems. s3cmd is a command-line utility for the Unix-like system to upload, download files to AWS S3 service from the command line.

ERROR: Test failed: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed error and solution

This error indicates that you don’t have packages correctly installed, especially SSL certificates. Let us see how to fix this problem and install s3cmd correctly on FreeBSD to get rid of the problem.

How to install s3cmd on FreeBSD

Search for s3cmd package:

$ pkg search s3cmd

Execute the following command and make sure you install Python 3.x package as Python 2 will be removed after 2020:

$ sudo pkg install py37-s3cmd-2.1.0

Updating FreeBSD repository catalogue...
FreeBSD repository is up to date.
All repositories are up to date.
Checking integrity... done (0 conflicting)
The following 8 package(s) will be affected (of 0 checked):

New packages to be INSTALLED:
	libffi: 3.2.1_3
	py37-dateutil: 2.8.1
	py37-magic: 5.38
	py37-s3cmd: 2.1.0
	py37-setuptools: 44.0.0
	py37-six: 1.14.0
	python37: 3.7.8
	readline: 8.0.4

Number of packages to be installed: 8

The process will require 118 MiB more space.

Proceed with this action? [y/N]: y
[rsnapshot] [1/8] Installing readline-8.0.4...
[rsnapshot] [1/8] Extracting readline-8.0.4: 100%
[rsnapshot] [2/8] Installing libffi-3.2.1_3...
....
..
[rsnapshot] [8/8] Extracting py37-s3cmd-2.1.0: 100%
=====
Message from python37-3.7.8:

--
Note that some standard Python modules are provided as separate ports
as they require additional dependencies. They are available as:

py37-gdbm       databases/py-gdbm@py37
py37-sqlite3    databases/py-sqlite3@py37
py37-tkinter    x11-toolkits/py-tkinter@py37

#[object object] #[object object] #[object object] #[object object] #[object object] #[object object] #[object object]