Package: optional.py
Optional.pyAn Implementation of the Optional Object for PythonWhyThere is a difference betweenNoneas Empty andNoneas the result for an Error.  A common bad practice is to
returnNoneto indicate the absence of something. Doing this introduces ambiguity into you code.For example:thing=stuff.getSomeThing().getAnotherThing()What will happen if the result from getSomeThing returnsNone?  We will get anAttributeError: 'NoneType' object has no attribute 'getAnotherThing'.What can you do to prevent these kinds of exceptions?  You can write defensively:something=stuff.getSomeThing()ifsomethingisnotNone:thing=something.getAnotherThing()However, if we add to our chain, you can imagine how the nesting of defensive checks adds up quickly. These defensive checks obfuscate our actual business logic, decreasing readability.
Furthermore, defensive checking is an error prone process, because it is easy to forget to check a required condition.So we present you with anOptionalobject as an alternative.InstallCompatible with both python 2 and 3!$pipinstalloptional.pyUsageYou can import it using:fromoptionalimportOptionalYou can set it to empty:instead of: :scream_cat:returnNoneyou can do: :smile_cat:returnOptional.empty()orreturnOptional.of()You can set it to have content:instead of: :scream_cat:return"thing"you can do: :smile_cat:returnOptional.of("thing")You can check if its present:instead of: :scream_cat:ifthingisnotNone:you can do: :smile_cat:thing=some_func_returning_an_optional()ifthing.is_present():or, alternatively: :smirk_cat:thing=some_func_returning_an_optional()ifthing:You can check if its empty:instead of: :scream_cat:ifthingisNone:you can do: :smile_cat:thing=some_func_returning_an_optional()ifthing.is_empty():or, alternatively: :smirk_cat:thing=some_func_returning_an_optional()ifnotthing:You can get the value:instead of: :scream_cat:print(thing)you can do: :smirk_cat:thing=some_func_returning_an_optional()...print(thing.get())but this is not the recommended way to use this library.Youcan'tget the value if its empty:instead of: :crying_cat_face:ifthingisNone:print(None)# very oddyou can do: :smirk_cat:thing=some_func_returning_an_optional()ifthing.is_empty():print(thing.get())# **will raise an exception**but this will raise an exception!You can get_or_default which takes a default value:instead of: :crying_cat_face:thing=some_func_may_return_none()ifthingisNone:thing='23'or: :scream_cat:thing=Optional.of(some_func_may_return_none())ifthing.is_empty():thing='23'else:thing=thing.get()you can do: :smirk_cat:thing=Optional.of(some_func_may_return_none()).get_or_default('23')You can get_or_raise to raise any exception on abscence:instead of: :scream_cat:try:thing=some_func_returning_an_optional()returnthing.get()exceptOptionalAccessOfEmptyException:raiseMyCustomException()you can do: :heart_eyes_cat:returnsome_func_returning_an_optional().get_or_raise(MyCustomException())Best Usage:You can chain on presence:instead of: :scream_cat:ifthingisnotNone:print(thing)you can do: :heart_eyes_cat:thing=some_func_returning_an_optional()thing.if_present(lambdathing:print(thing))Best Usage:You can chain on non presence:instead of: :scream_cat:ifthingisnotNone:print(thing)else:print("PANTS!")you can do: :heart_eyes_cat:thing=some_func_returning_an_optional()thing.if_present(lambdathing:print(thing)).or_else(lambda_:print("PANTS!"))Note that the lambdas here can be swapped out for actual function names.Best Usage:You can run a supplier on non presence:instead of: :scream_cat:thing=some_func_returning_an_empty_optional()ifthing.is_empty():thing=Optional.of("pants")print(thing.get())# Prints "pants"you can do: :heart_eyes_cat:defsome_supplier():return"pants"thing=some_func_returning_an_empty_optional().or_else(some_supplier)print(thing.get())# Prints "pants"Best Usage:You can raise on non presence:instead of: :scream_cat:ifthingisNone:raiseSomeException("Boom!")you can do: :heart_eyes_cat:thing=some_func_returning_an_optional()thing.if_present(lambdathing:print(thing)).or_else_raise(SomeException("Boom!"))Best Usage:You can map a function: :heart_eyes_cat:defmapping_func(thing):returnthing+"PANTS"thing_to_map=Optional.of("thing")mapped_thing=thing_to_map.map(mapping_func)# returns Optional.of("thingPANTS")Notethat if the mapping function returnsNonethen the map call will returnOptional.empty(). Also
if you callmapon an empty optional it will returnOptional.empty().Best Usage:You can flat map a function whichalready returns an Optional: :heart_eyes_cat:defflat_mapping_func(thing):returnOptional.of(thing+"PANTS")thing_to_map=Optional.of("thing")mapped_thing=thing_to_map.map(mapping_func)# returns Optional.of("thingPANTS")Notethat this does not return an Optional of an Optional.Use this for mapping functions which return optionals.If the mapping function you use with this does not return an Optional, callingflat_mapwill raise aFlatMapFunctionDoesNotReturnOptionalException.You can compare two optionals: :smile_cat:Optional.empty()==Optional.empty()# TrueOptional.of("thing")==Optional.of("thing")# TrueOptional.of("thing")==Optional.empty()# FalseOptional.of("thing")==Optional.of("PANTS")# FalseTestsThere is complete test coverage and they pass in both python 2 and 3.Running Unit TestsFirst, installpoetryusing the instructions locatedhere.Then, install the requirements using:$poetryinstallYou can run the tests using:$poetryrunpytestTest CoverageYou can check the code coverage using:$poetryrunpytest--cov=optional