Package: sentinel
Creates simple sentinel objects.InstallBasic features:pip install sentinelwith extra magic features powered bypython-varname:pip install 'sentinel[varname]'What is a sentinel?Sentinelsaresingletonobjects that typically represent some
terminating (end) condition or have a special, symbolic meaning. Python’s built-inNoneis a sentinel. Python also has other sentinels likeNotImplementedandEllipsis.If you want to create your own sentinels, use this library! Make your calls todict.get()more meaningful! You can replace theobject()idiom with a sentinel:d={"a":1,"b":None}# Before sentinel:missing=object()ifd.get("c",missing)ismissing:...# do some stuff# After sentinel:Missing=sentinel.create()ifd.get("c",Missing)isMissing:...# do some stuffFeaturessentinels are uniquesentinels are singletons — theonlyinstance of their own anonymous classsentinels can be used withiscomparisonssentinels can be used withpicklesentinels can be used withcopy.deepcopyyou canaddarbitrary attributes and methods to sentinelssentinels have a nice, self-documenting__repr__!UsageCreate a sentinel:>>> import sentinel
>>> MySentinel = sentinel.create("MySentinel")
>>> MySentinel
MySentinelIf you havepython-varnameinstalled, or installed this module usingpip install 'sentinel[varname]',sentinel.create()can infer the name
from the assignment expression:importsentinelMySentinel=sentinel.create()print(MySentinel)# prints `MySentinel`NOTE: this will not work in the interactive console!>>> import sentinel
>>> # Fails because varname can't find the source code for the interactive console!
>>> MySentinel = sentinel.create("MySentinel")ExampleSentinels are useful when other objects such asNone,False,0,-1, are valid values within some data structure. For example, setting
default values when all other values are valid with:dict.setdefault():d={"stdout":None,"stdin":0,"EOF":-1}MissingEntry=sentinel.create()[d.setdefault(key,MissingEntry)forkeyin("stdin","stdout","stderr")][0,None,MissingEntry]Alternatively, usingdict.get()when fetching values:>>> d = {"stdout": None, "stdin": 0, "EOF": -1}
>>> d.get("stdout", MissingEntry)
None
>>> d.get("stdin", MissingEntry)
0
>>> d.get("stderr", MissingEntry)
MissingEntrySince a new sentinel can never occur in the original dictionary, you can tell which
entries are missing or unset in a dictionary in a self-documenting way:Unset=sentinel.create()ifd.get("stdin",Unset)isUnset:stdin=0# some reasonable defaultAdding extra methods and class attributesSentinels may also inherit from base classes, or implement extra methods.Consider a binary search tree with two kinds of nodes: interior nodes
(Node) which contain some payload and leaves (Leaf), which simply
terminate traversal.To create singleton leaf which implements asearchmethod and anis_leafproperty, you may provide any extra class attributes in thecls_dictkeyword argument. The following is a full example of both
the singletonLeafand itsNodecounterpart:def_search_leaf(self,key):raiseKeyError(key)Leaf=sentinel.create('Leaf',cls_dict={'search':_search_leaf,'is_leaf':property(lambdaself:True)})classNode(object):def__init__(self,key,payload,left=Leaf,right=Leaf):self.left=leftself.right=rightself.key=keyself.payload=payloaddefsearch(self,key):ifkey<self.key:returnself.left.search(key)elifkey>self.key:returnself.right.search(key)else:returnself.payloadis_leaf=property(lambda:false)Example usage:>>> tree = Node(2, 'bar', Node(1, 'foo'), Node(3, 'baz'))
>>> tree.search(1)
'foo'
>>> tree.search(4)
Traceback (most recent call last):
    ...
KeyError: 2ContributingThis project usesPoetry. To contribute to the codebase, make sure toinstall poetry,
With Poetry installed, clone then repo, then within the repo directory, install the developer dependencies:$ poetry install --extras varnameNext, I recommend you do all development tasks within thepoetry shell:$ poetry shell
(sentinel-nUnrocCf-py3.9) $ black .
(sentinel-nUnrocCf-py3.9) $ pytest