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 hashes)

Uploaded Source

Built Distribution

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

Uploaded Python 3

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