Skip to main content

Null safe support for Python

Project description

Null Safe Python

Null safe support for Python.

Installation

pip install nullsafe

Quick Start

Dummy Class

class Dummy:
    pass

Normal Python code:

o = Dummy()

try:
    value = o.inexistent
    print("accessed")
except AttributeError:
    value = None

With nullsafe:

from nullsafe import undefined, _

o = Dummy()

value = _(o).inexistent

if value is not undefined:
    print("accessed")

Documentation

Basics

There are 5 values importable in nullsafe root:

class NullSafeProxy: (o: T)

Receives an object o on instantiation.

Proxy class for granting nullsafe abilities to an object.

class NullSafe: ()

No argument needed.

Nullish class with with nullsafe abilities. Instances will have a falsy boolean evaluation, equity comparison (==) to None and instance of NullSafe returns True, otherwise False. Identity comparison (is) to None will return False.

variable undefined: NullSafe

Instance of Nullsafe, this instance will be returned for all nullish access in a proxied object, enabling identity comparison value is undefined for code clarity.

function nullsafe: (o: T) -> T

Receives an object o as argument.

Helper function that checks if object is nullish and return the proxied object.

return undefined if o is None or undefined, otherwise return the proxied object NullSafeProxy[T].

This function is generic typed ((o: T) -> T), code autocompletions and linters functionalities will remain. Disclaimer: If the object was not typed before proxy, it obviously won't come out typed out of the blue.

function _: (o: T) -> T (alias to nullsafe)

Alias to nullsafe, used for better code clarity.

The examples shown will be using _ instead of nullsafe for code clarity. For better understanding, the Javascript equivalents will be shown as comments.

Implementation

Nullsafe abilities are granted after proxying an object through NullSafeProxy. To proxy an object pass it through _() or nullsafe(). Due to language limitation, the implementation does not follow the "return the first nullish value in chain", instead it "extend the a custom nullish value until the end of chain". Inexistent values of a proxied object and its subsequent values in chain will return undefined.

Import

from nullsafe import undefined, _

Usage

There are various way to get a nullsafe proxied object.

Null safe attribute access

Proxied object doing a possibly AttributeError access.

o = Dummy()

# o.inexistent
assert _(o).inexistent is undefined
assert _(o).inexistent == None   # undefined == None
assert not _(o).inexistent       # bool(undefined) == False

# o.inexistent?.nested
assert _(o).inexistent.nested is undefined

# o.existent.inexistent?.nested
assert _(o.existent).inexistent.nested is undefined

# o.maybe?.inexistent?.nested
assert _(_(o).maybe).inexistent.nested is undefined

Null safe item access

Proxied object doing a possibly KeyError access.

o = Dummy() # dict works too !

# o.inexistent
assert _(o)["inexistent"] is undefined
assert _(o)["inexistent"] == None    # undefined == None
assert not _(o)["inexistent"]        # bool(undefined) == False

# o.inexistent?.nested
assert _(o)["inexistent"]["nested"] is undefined

# o.existent.inexistent?.nested
assert _(o["existent"])["inexistent"]["nested"] is undefined

# o.maybe?.inexistent?.nested
assert _(_(o)["maybe"])["inexistent"]["nested"] is undefined

Null safe post evaluation

Possibly None or undefined object doing possibly AttributeError or KeyError access.

Note: This only works if the seeking value is accessible, see limitations

o = Dummy() # dict works too !
o.nay = None

# o.nay?.inexistent
assert _(o.nay).inexistent is undefined
assert _(o.nay).inexistent == None   # undefined == None
assert not _(o.nay).inexistent       # bool(undefined) == False

# o.nay?.inexistent.nested
assert _(o.nay).inexistent.nested is undefined
o = Dummy() # dict works too !
o["nay"] = None

# o.nay?.inexistent
assert _(o["nay"])["inexistent"] is undefined
assert not _(o["nay"])["inexistent"]

# o.nay?.inexistent.nested
assert _(o["nay"])["inexistent"]["nested"] is undefined
assert not _(o["nay"])["inexistent"]["nested"]

Combined usage

Of course you can combine different styles.

assert _(o).inexistent["inexistent"].inexistent.inexistent["inexistent"]["inexistent"] is undefined

Limitations

List of limitations that you may encounter.

undefined behavior

undefined is actually an instance of NullSafe, the actual mechanism used for nullsafe chaining, it cannot self rip the nullsafe functionality when the chain ends (because it doesn't know), so this following actually possible and probably not the wanted behavior.

val = _(o).inexistent

assert val.another_inexistent is undefined

Post evaluation

In other languages like Javascript, it checks for each item in the chain and return undefined on the first nullish value, which in fact is post-evaluated. This is not possible in python because it raises an AttributeError or KeyError on access attempt, unless it returns None (see one of the available usage), so it must proxy the instance that may contain the attr or key before accessing.

try:
    val = _(o.inexistent).nested # AttributeError: '<type>' object has no attribute 'inexistent'
except AttributeError:
    assert True
assert _(o).inexistent.nested is undefined

Contributing

Contributions welcomed ! Make sure it passes current tests tho.

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

nullsafe-0.1.2.tar.gz (5.7 kB view details)

Uploaded Source

Built Distribution

nullsafe-0.1.2-py3-none-any.whl (6.2 kB view details)

Uploaded Python 3

File details

Details for the file nullsafe-0.1.2.tar.gz.

File metadata

  • Download URL: nullsafe-0.1.2.tar.gz
  • Upload date:
  • Size: 5.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.4.1 importlib_metadata/4.5.0 pkginfo/1.7.0 requests/2.25.1 requests-toolbelt/0.9.1 tqdm/4.61.1 CPython/3.9.5

File hashes

Hashes for nullsafe-0.1.2.tar.gz
Algorithm Hash digest
SHA256 9b31929fa6c19aa3baa37fe11bdbe1a5ba1b5038f36483863b415fb4eefdba7e
MD5 336074e9e49afca1a332edefaaa859e8
BLAKE2b-256 f252306a18ca13ed70c7ddac4c416f7bb5c7585d96c5cb17172c547f24b08d4d

See more details on using hashes here.

File details

Details for the file nullsafe-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: nullsafe-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 6.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.4.1 importlib_metadata/4.5.0 pkginfo/1.7.0 requests/2.25.1 requests-toolbelt/0.9.1 tqdm/4.61.1 CPython/3.9.5

File hashes

Hashes for nullsafe-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 c18e800feac79dd15b73b738c484433a0005452394c2b6073a22ea9c20fa9e09
MD5 ab14613ad8af731a9da580ef3796c8aa
BLAKE2b-256 e241f10076fa837797857c3b1ea7b181b82221da6e28e447f40133e407bcd6bd

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