Skip to main content

A basic implementation of a maybe/option type in Python, largely inspired by Rust's Option.

Project description

py-maybetype

Linting & testing

Documentation: https://py-maybetype.readthedocs.io/en/latest/

PyPI: https://pypi.org/project/py-maybetype/

[!WARNING] I'm not considering any version before 1.0 stable, and breaking changes are likely with each 0.x release. Though I'm using it in my own projects, I wouldn't consider it "production-ready" until a 1.0.0 release is made.

A basic implementation of a maybe/option type in Python, largely inspired by Rust's Option. This was created as part of a separate project I had been working on, but I decided to make it into its own package as I wanted to use it elsewhere and its scope grew. This is not meant to be a 1:1 replication or replacement for Rust's Option or Haskell's Maybe, but rather an interperetation of the idea that I feel works for Python.

This package also implements a Result type which can be used to wrap either a success (Ok) or failure (Err) value.

Usage

Install with pip:

pip install py-maybetype

Call the maybe() function with a T | None value to return a Maybe[T]—either a Some instance containing the wrapped value, or the Nothing singleton. You can also directly use the Some constructor or the Nothing singleton explicitly e.g. when returning a value from a function. Maybe only serves as a superclass to provide methods for Some and Nothing and to be used for typing, it should not be instanced directly. If it is, a warning is emitted.

Nothing is just an instance of the NothingType class, and should be used instead of creating new NothingType instances since all are functionally identical. A warning is emitted if the class is instanced more than once.

from maybetype import Maybe, maybe

# Only the maybe() function should be used,
# the Maybe class is only imported here for type annotations

def try_int(x: str) -> int | None:
    """Attempts to convert a string of digits into an `int`, returning `None` if not possible."""
    try:
        return int(x)
    except ValueError:
        return None

num1: Maybe[int] = maybe(try_int('5'))
num2: Maybe[int] = maybe(try_int('five'))

print(num1.unwrap()) # 5
print(num2.unwrap()) # (raises ValueError)

# Some() instances are always truthy, Nothing is falsy

assert bool(num1) is True
assert bool(num2) is False

This example in particular can also be done with the Maybe.try_int() class method:

num1: Maybe[int] = Maybe.try_int('5')
num2: Maybe[int] = Maybe.try_int('five')

The maybe constructor can be given an optional predicate argument to specify a custom condition for which Some(value) is returned. This argument must be a Callable that returns bool, where returning False causes the constructor to return Nothing.

[!NOTE] maybe(None) will always return Nothing, even if predicate(None) would return True

import re
import uuid

from maybetype import maybe

def is_valid_uuid(s: str) -> bool:
    return re.match(r"[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}|[0-9a-f]{32}", s) is not None

assert maybe('3b1bcc3a-41d5-49a5-8273-10cc605e31f9', is_valid_uuid)
assert maybe('3b1bcc3a41d549a5827310cc605e31f9', is_valid_uuid)
assert not maybe('qwertyuiopasdfghjklzxcvbnm', is_valid_uuid)
assert not maybe('nf0cmmdq-l0gt-rq5a-upry-706trht3ocv9', is_valid_uuid)

Maybe instances can also be used in match/case pattern matching to access the wrapped value, like so:

from maybetype import maybe, Some

match maybe(1):
    case Some(val):
        print('Value: ', val)
    case _: # "case Nothing:" also works, but just matching else in this case will be identical
        print('No value')

Other examples

Converting a str | None timestamp into a datetime object if not None, otherwise returning None:

from datetime import datetime
from maybetype import maybe

assert maybe('2025-09-06T030000').then(datetime.fromisoformat) == datetime(2025, 9, 6, 3, 0)

assert maybe(None).then(datetime.fromisoformat) is None

assert maybe('' or None).then(datetime.fromisoformat) is None
# Maybe does not treat falsy values as None, only strictly x-is-None values
# Without `or None` here, datetime.fromisoformat would have raised a ValueError

Converting a str | None timestamp into a datetime object if not None, then ensuring that date meets certain criteria:

from datetime import datetime
from maybetype import maybe

assert maybe('2025-09-06T030000').and_then(datetime.fromisoformat).test(lambda dt: dt.year > 2024)

assert not maybe('2024-09-06T030000').and_then(datetime.fromisoformat).test(lambda dt: dt.year > 2024)

match maybe('2025-09-06T030000').and_then(datetime.fromisoformat).test(lambda dt: dt.year > 2024):
    case Some(date):
        ... # Do something with the date
    case _:
        ... # Do something else

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

py_maybetype-0.11.0.tar.gz (60.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

py_maybetype-0.11.0-py3-none-any.whl (10.1 kB view details)

Uploaded Python 3

File details

Details for the file py_maybetype-0.11.0.tar.gz.

File metadata

  • Download URL: py_maybetype-0.11.0.tar.gz
  • Upload date:
  • Size: 60.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for py_maybetype-0.11.0.tar.gz
Algorithm Hash digest
SHA256 df3d7ae05af491d23a467c88c67d4ef68adb6e814a08eb6d9ccc95f83db76b97
MD5 b7ef1652614dbce9999fccebc352460f
BLAKE2b-256 b09abdf878763e8c1b7d534badbb96018b75cd532eb54008c57e429d963a1d06

See more details on using hashes here.

Provenance

The following attestation bundles were made for py_maybetype-0.11.0.tar.gz:

Publisher: publish-to-pypi.yml on svioletg/py-maybetype

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file py_maybetype-0.11.0-py3-none-any.whl.

File metadata

  • Download URL: py_maybetype-0.11.0-py3-none-any.whl
  • Upload date:
  • Size: 10.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for py_maybetype-0.11.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ac0c6d1afe85552337e8973f2c5b71737430c75fa8849dc0b9f8fc1557905b03
MD5 c29a0486d6a5247c71f016c17988d6d3
BLAKE2b-256 c8f2816f9720352dbf82303dd034a0e476ac5a785d43cd98e1f64fdb1d4be657

See more details on using hashes here.

Provenance

The following attestation bundles were made for py_maybetype-0.11.0-py3-none-any.whl:

Publisher: publish-to-pypi.yml on svioletg/py-maybetype

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page