Skip to main content

Little python package implementing optional chaining

Project description

safebag

Stable Version tests

Safebag is a little python package implementing optional chaining.

Installation

pip install safebag

Usage

Code we want to avoid

if (
    obj is not None
    and obj.attr is not None
    and obj.attr.attr is not None
    and obj.attr.attr.attr is not None
    and obj.attr.attr.attr.attr is not None
):
    # Do something useful with obj.attr.attr.attr.attr
    ...

Pythonic solution

try:
    print(obj.attr.attr.attr.attr)
    # Do something useful with obj.attr.attr.attr.attr
except(NameError, AttributeError) as e:
    # Do something useful with an error

Still it's not very clean way in case of multiple attribute handling in one place

try:
    print(obj.attr.attr.attr.attr)
    # Do something useful with obj.attr.attr.attr.attr
except(NameError, AttributeError) as e:
    ...

try:
    print(obj.attr.attr)
    # Do something useful with obj.attr.attr
except(NameError, AttributeError) as e:
    ...

try:
    print(obj.attr)
    # Do something useful with obj.attr
except(NameError, AttributeError) as e:
    ...

Usage example:

from safebag import chain, get_value

if attr := chain(obj).attr.attr.attr.attr:
    # Do something useful with obj.attr.attr.attr.attr
    print(get_value(attr))

if attr := chain(obj).attr.attr:
    # Do something useful with obj.attr.attr
    print(get_value(attr))

if attr := chain(obj).attr:
    # Do something useful with obj.attr
    print(get_value(attr))

Examples

chain [source]

Optional chain constructor, may be constructed from any object

Chain is used for building sequence of null-safe attribute calls

from __future__ import annotations

import dataclasses as dt
import typing


@dt.dataclass
class Node:
    data: int
    node: typing.Optional[Node]


nodes = Node(data=1, node=Node(data=2, node=None))

from safebag import chain

third_node_proxy = chain(nodes).node.node.node
print(third_node_proxy)  # ChainProxy(data_object=None, bool_hook=False)

get_value [source]

Final value getter for optional chain.

Optional chain constructed from any object. Chain is used for building sequence of null-safe attribute calls.

from __future__ import annotations

import dataclasses as dt
import typing


@dt.dataclass
class Node:
    data: int
    node: typing.Optional[Node]


nodes = Node(data=1, node=Node(data=2, node=None))

from safebag import chain, get_value

third_node_proxy = chain(nodes).node.node.node
value = get_value(third_node_proxy)
assert value is None

next_node = chain(nodes).node
value = get_value(next_node)  # Node(data=2, node=None)

Possible way of getting value

if next_node := chain(nodes).node:
    print(next_node.get_value())  # Node(data=2, node=None)

Default can be passed as argument

if next_node := chain(nodes).node.node:
    print(next_node.get_value(default='Default')) # 'Default'

Useful in combination with walrus operator:

if next_node := chain(nodes).node.node:
    print(get_value(next_node))

if next_node := chain(nodes).node:
    print(get_value(next_node))  # Node(data=2, node=None)

ChainProxy [source]

ChainProxy container:

  • stores data_object
  • proxying data_object attribute value into new ChainProxy instance when attribute is invoked. If attribute does not exist or attribute value is None. ChainProxy instance data_object will be None and bool_hook will be False.
  • ChainProxy instance always returning when attribute is invoked.

Release: 0.2.0

Performance update

Increase performance by adding empty_proxy instead of real ChainProxy

For case below Numbers: # 0.100 -> 0.046

import timeit
from dataclasses import dataclass

@dataclass
class Node:
    data: int
    node: typing.Optional[Node]

node = Node(1, None)
executable = lambda: get_value(chain(node).node.node.node.node.node.node.node.node.node.node)
perf = timeit.timeit(executable, number=10000)

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

safebag-0.2.0.tar.gz (4.5 kB view details)

Uploaded Source

Built Distribution

safebag-0.2.0-py3-none-any.whl (5.8 kB view details)

Uploaded Python 3

File details

Details for the file safebag-0.2.0.tar.gz.

File metadata

  • Download URL: safebag-0.2.0.tar.gz
  • Upload date:
  • Size: 4.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.1.13 CPython/3.10.2 Linux/5.13.0-1031-azure

File hashes

Hashes for safebag-0.2.0.tar.gz
Algorithm Hash digest
SHA256 88ad201513276fe32dd3ab975e9ae2eee8edcd819d5bf4a6f2894a52c76be756
MD5 aafdcb74a2676fdfe770d4748f35625b
BLAKE2b-256 18b2fc127bc602aa2ca95f4e6170a13bdaaafaa4f6a6b86dcd4c6b62574f923a

See more details on using hashes here.

File details

Details for the file safebag-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: safebag-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 5.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.1.13 CPython/3.10.2 Linux/5.13.0-1031-azure

File hashes

Hashes for safebag-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 e43e6d7bfa441fb00b3baaf0dec058f4ed2094c7b38c191b90785b49ab63926f
MD5 82544940b5405553c311164cff50ebb4
BLAKE2b-256 b52e23274c9ebb6e5b0c91a29da05a743889447c8737a0cb7c4b467f311f8d15

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