Skip to main content

Immutable defaults for Python

Project description

immutable_defaults

tests

Github repo: https://github.com/clvnkhr/immutable-defaults

Simple decorator to force immutability to function arguments by deepcopying. Never again pass None when your heart wants to pass an empty list. Also works for arbitrary objects that can be deepcopied. Has simple config options for granularity or performance (copy vs deepcopy).

No dependencies.

In order to use various type hints we require Python >=3.12.

How to install

pip install immutable-defaults or your equivalent (e.g. pdm add immutable-defaults)

Example usage

from immutable_defaults import immutable_defaults 

@immutable_defaults
def my_function(a: list = []):
    a.append("world")
    return a

print(my_function())  # ['world']
print(my_function(a=["hello"]))  # ['hello', 'world']
print(my_function(["HELLO"]))  # ['HELLO', 'world']
print(my_function())  #  ['world']

@immutable_defaults(ignore=["b"])
def my_function2(a = ["hello"], b = []):
    """basic function with ignore parameter"""
    a.append("world")
    b.append("!")
    return a + b

print(my_function2())  # ['hello', 'world', '!']
print(my_function2())  # ['hello', 'world', '!', '!']
print(my_function2())  # ['hello', 'world', '!', '!', '!']

# more exhaustive tests in tests/tests.py

Methods, Classmethods, and Staticmethods

The decorator works with methods, classmethods and staticmethods. Since @immutable_defaults requires that the wrapped function is callable, make sure that the outer decorator is @classmethod/@staticmethod.

Optional keyword arguments

  • @immutable_defaults can be called with keyword arguments deepcopy and ignore.

  • deepcopy: boolean | Iterable[str] = True

    • if True then defaults are copied with copy.deepcopy. If False, then with copy.copy.
    • If passed an iterable of argument names then those arguments will be deep copied and other mutable defaults will be shallow copied, e.g. in the below a and arg will be deep copied while b will be shallow copied.
      @immutable_defaults(deepcopy=["a","arg"]) 
      def f(a=[[1]], b=[], arg={1: {2}}): ...
    
  • ignore: Iterable[str] | None = None

    • all argument names passed will have the default Python behavior.

Input validation

  • We check that you cannot have the same mutable object (as per a is b comparison) marked for both shallow and deep copying. For example, the below will raise an ImmutableDefaultsError:
xss = [[1]]
@immutable_defaults(deepcopy=["xss2"]) # raises ImmutableDefaultsError
def f(x, xss1 = xss, xss2 = xss): ...
  • Similarly, we check that you cannot ignore and not ignore the same mutable object. For example, the below will raise an ImmutableDefaultsError:
xss = [[1]]
@immutable_defaults(ignore=["xss2"]) # raises ImmutableDefaultsError
def f(x, xss1 = xss, xss2 = xss): ...
  • A KeyError is raised if either deepcopy or ignore have arguments that cannot be found in the signature of the decorated function.
    • It would have been easy to silently do nothing when variables in ignore are not present, but this would make typos very hard to debug.
  • ignore takes precedence over deepcopy, i.e. @immutable_defaults(ignore=["x"], deepcopy=["x"]) will do the same thing as @immutable_defaults(ignore=["x"])

Prior art

(Comments valid May 13 2024)

Todo

  • Performance benchmarking - what is the overhead?
  • Make publishing to pypi part of github workflow

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

immutable_defaults-0.1.3.tar.gz (9.2 kB view details)

Uploaded Source

Built Distribution

immutable_defaults-0.1.3-py3-none-any.whl (6.2 kB view details)

Uploaded Python 3

File details

Details for the file immutable_defaults-0.1.3.tar.gz.

File metadata

  • Download URL: immutable_defaults-0.1.3.tar.gz
  • Upload date:
  • Size: 9.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: pdm/2.16.1 CPython/3.10.12 Linux/6.5.0-1022-azure

File hashes

Hashes for immutable_defaults-0.1.3.tar.gz
Algorithm Hash digest
SHA256 0b44fe5343fa33f20520bf9a68008ed87d3b8b50294f06c408f01421571dc752
MD5 f4aeec8f50b4ff7703457c8825b8fd7e
BLAKE2b-256 17d945bcbf0b44368715ee40a015c091961937c893a0ef24813204c44662e643

See more details on using hashes here.

File details

Details for the file immutable_defaults-0.1.3-py3-none-any.whl.

File metadata

File hashes

Hashes for immutable_defaults-0.1.3-py3-none-any.whl
Algorithm Hash digest
SHA256 354556e5c64d533b2ebf4c4782dedf7d6d9bf93e3737ffefaa3b0a77ac2a92b2
MD5 672dc081ce91b6524ab7ef0642c751da
BLAKE2b-256 24a5b48d48ab418897f7a2abc4caa68b78c5ee0aa03d22751e0a9516d489c674

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page