Skip to content

Instantly share code, notes, and snippets.

@Keyacom
Last active September 17, 2022 14:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Keyacom/17c72746fe47d21114612a93ac6f0420 to your computer and use it in GitHub Desktop.
Save Keyacom/17c72746fe47d21114612a93ac6f0420 to your computer and use it in GitHub Desktop.
None-aware functions (for PEP 505 being deferred)
"""
A way the proposed ``??``, ``?.`` and ``?[`` operators could be implemented.
For now, this lacks an implementation for ``??=``.
"""
from __future__ import annotations
from types import NoneType
from typing import Hashable, NamedTuple
def none_coalesce(*args):
"""
An alias for the ``??`` operator, for PEP 505 being currently deferred.
:param args: The arguments to coalesce. Returns the first one that isn't ``None``.
"""
for a in args:
if a is not None:
return a
def safe_navigate(entity, *chain):
"""
An alias for the ``?.`` and ``?[`` operators, for PEP 505 being currently deferred.
Usage:
```py
class Demo:
person = {
'name': 'Kira',
'mother': {
'name': 'Irina'
}
}
# Proposed syntax:
print(Demo?.person?['father']?['name'])
# The actual function:
print(safe_navigate(Demo, "person", ["father"], ["name"]))
```
:param entity: The object to safely navigate through.
:param chain: The combination of ``str``s, ``int``s, and ``list``s. They're pseudo-evaled like this:
- ``str``s are pseudo-evaled as attributes
- ``int``s are pseudo-evaled as list indices
- ``list``s are pseudo-evaled as subscriptive indexing (e.g. of dicts).
"""
ret = entity
for c in chain:
try:
match type(c).__name__:
case "str":
if hasattr(ret, "_asdict"):
ret = ret._asdict()[c]
elif hasattr(ret, "__dict__"):
ret = ret.__dict__[c]
else:
ret = ret[c]
case "int":
ret = ret[c]
case "list":
assert issubclass(type(c[0]), Hashable)
if isinstance(c[0], str):
ret = ret[c[0]]
elif [isinstance(i, (int, NoneType)) for i in c] == [True] * 3:
d = [
c[0] if c[0] is not None else 0,
c[1] if c[1] is not None else -1,
c[2] if c[2] is not None else 1,
]
ret = ret[d[0] : d[1] : d[2]]
else:
ret = ret[c[0]]
if ret is None:
return None
except (AttributeError, IndexError, KeyError):
return None
return ret
class Person(NamedTuple): # Test class
name: str
mother: Person = None
father: Person = None
person = Person(name="Kira", mother=Person(name="Irina"))
print(safe_navigate(person, "father", "name")) # None
print(safe_navigate(person, "mother", "name")) # Irina
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment