Little python package implementing optional chaining
Project description
safebag
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_objectattribute value into newChainProxyinstance when attribute is invoked. If attribute does not exist or attribute value isNone.ChainProxyinstancedata_objectwill beNoneandbool_hookwill beFalse. ChainProxyinstance 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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
88ad201513276fe32dd3ab975e9ae2eee8edcd819d5bf4a6f2894a52c76be756
|
|
| MD5 |
aafdcb74a2676fdfe770d4748f35625b
|
|
| BLAKE2b-256 |
18b2fc127bc602aa2ca95f4e6170a13bdaaafaa4f6a6b86dcd4c6b62574f923a
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e43e6d7bfa441fb00b3baaf0dec058f4ed2094c7b38c191b90785b49ab63926f
|
|
| MD5 |
82544940b5405553c311164cff50ebb4
|
|
| BLAKE2b-256 |
b52e23274c9ebb6e5b0c91a29da05a743889447c8737a0cb7c4b467f311f8d15
|