{"id":13800396,"url":"https://github.com/mplanchard/safetywrap","last_synced_at":"2026-01-16T10:23:31.836Z","repository":{"id":57463569,"uuid":"187387888","full_name":"mplanchard/safetywrap","owner":"mplanchard","description":"Fully typesafe, Rust-like Result and Option types for Python","archived":false,"fork":false,"pushed_at":"2020-09-23T23:03:51.000Z","size":302,"stargazers_count":46,"open_issues_count":0,"forks_count":3,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-10-19T17:39:01.203Z","etag":null,"topics":["option","python","python3","result","result-type","rust","rust-option","rust-result","typesafe","typing"],"latest_commit_sha":null,"homepage":"https://pypi.org/project/safetywrap/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mplanchard.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-05-18T17:40:31.000Z","updated_at":"2025-05-30T18:45:16.000Z","dependencies_parsed_at":"2022-09-13T10:50:29.553Z","dependency_job_id":null,"html_url":"https://github.com/mplanchard/safetywrap","commit_stats":null,"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/mplanchard/safetywrap","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mplanchard%2Fsafetywrap","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mplanchard%2Fsafetywrap/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mplanchard%2Fsafetywrap/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mplanchard%2Fsafetywrap/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mplanchard","download_url":"https://codeload.github.com/mplanchard/safetywrap/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mplanchard%2Fsafetywrap/sbom","scorecard":{"id":662931,"data":{"date":"2025-08-11","repo":{"name":"github.com/mplanchard/safetywrap","commit":"a517b6ef9a4fdfab7aefd3f5a276de127cea0579"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":4.1,"checks":[{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Code-Review","score":0,"reason":"Found 1/24 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: Apache License 2.0: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":8,"reason":"4 out of the last 4 releases have a total of 4 signed artifacts.","details":["Info: signed release artifact: safetywrap-1.4.0-py3-none-any.whl.asc: https://github.com/mplanchard/safetywrap/releases/tag/v1.4.0","Info: signed release artifact: safetywrap-1.3.1-py3-none-any.whl.asc: https://github.com/mplanchard/safetywrap/releases/tag/v1.3.1","Info: signed release artifact: safetywrap-1.3.0-py3-none-any.whl.asc: https://github.com/mplanchard/safetywrap/releases/tag/v1.3.0","Info: signed release artifact: safetywrap-1.2.0-py3-none-any.whl.asc: https://github.com/mplanchard/safetywrap/releases/tag/v1.2.0","Warn: release artifact v1.4.0 does not have provenance: https://api.github.com/repos/mplanchard/safetywrap/releases/24364793","Warn: release artifact v1.3.1 does not have provenance: https://api.github.com/repos/mplanchard/safetywrap/releases/23892621","Warn: release artifact v1.3.0 does not have provenance: https://api.github.com/repos/mplanchard/safetywrap/releases/22772216","Warn: release artifact v1.2.0 does not have provenance: https://api.github.com/repos/mplanchard/safetywrap/releases/22723083"],"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 7 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-21T16:47:49.298Z","repository_id":57463569,"created_at":"2025-08-21T16:47:49.298Z","updated_at":"2025-08-21T16:47:49.298Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28478049,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-16T06:30:42.265Z","status":"ssl_error","status_checked_at":"2026-01-16T06:30:16.248Z","response_time":107,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["option","python","python3","result","result-type","rust","rust-option","rust-result","typesafe","typing"],"created_at":"2024-08-04T00:01:12.226Z","updated_at":"2026-01-16T10:23:31.813Z","avatar_url":"https://github.com/mplanchard.png","language":"Python","funding_links":[],"categories":["Awesome Functional Python","Additional types"],"sub_categories":["Libraries"],"readme":"# safetywrap\n\n[![Build Status](https://dev.azure.com/msplanchard/safetywrap/_apis/build/status/mplanchard.safetywrap?branchName=master)](https://dev.azure.com/msplanchard/safetywrap/_build/latest?definitionId=3\u0026branchName=master)\n[![coverage report](https://img.shields.io/azure-devops/coverage/msplanchard/safetywrap/3)](https://dev.azure.com/msplanchard/safetywrap/_build?definitionId=3)\n\nFully typesafe, Rust-inspired wrapper types for Python values\n\n## Summary\n\nThis library provides two main wrappers: `Result` and `Option`. These types\nallow you to specify typesafe code that effectively handles errors or\nabsent data, without resorting to deeply nested if-statements and lots\nof try-except blocks.\n\nThis is accomplished by allowing you to operate on an `Option` or `Result`\nin a sort of quantum superposition, where an `Option` could be `Some` or\n`Nothing` or a `Result` could be `Ok` or `Err`. In either case, all of the\nmethods on the type work just the same, allowing you to handle both cases\nelegantly.\n\nA `Result[T, E]` may be an instance of `Ok[T]` or `Err[E]`, while an `Option[T]`\nmay be an instance of `Some[T]` or `Nothing`. Either way, you get to treat\nthem just the same until you really need to get the wrapped value.\n\nSo, rather than this:\n\n```py\nfor something in \"value\", None:\n    if something is not None:\n        val = something.upper()\n    else:\n        val = \"DEFAULT\"\n    print(val)\n```\n\nYou can do this:\n\n```py\nfor something in Some(\"value\"), Nothing():\n    print(something.map(str.upper).unwrap_or(\"DEFAULT\"))\n```\n\nAnd rather than this:\n\n```py\nfor jsondata in '{\"value\": \"myvalue\"}', '{badjson':\n    try:\n        config = capitalize_keys(json.loads(jsondata))\n    except Exception:\n        config = get_default_config()\n    print(config[\"value\"])\n```\n\nYou can do this:\n\n```py\nfor jsondata in '{\"value\": \"myvalue\"}', '{badjson':\n    print(\n        Result.of(json.loads, jsondata)\n        .map(capitalize_keys)\n        .unwrap_or_else(get_default_config)[\"value\"]\n    )\n```\n\nThese two examples are super minimal samples of how using these typesafe\nwrappers can make things easier to write and reason about. Please see the\n[Examples](#examples) section for more, and [Usage](#usage) for the full\nsuite of offered functionality.\n\nThese types are heavily influenced by the [Result][rust-result] and\n[Option][rust-option] types in Rust.\n\nThorough type specifications for mypy or your favorite python type-checker\nare provided, so that you can decorate function inputs and outputs as\nreturning `Result` and `Option` types and get useful feedback when supplying\narguments or passing return values.\n\n### Sponsorship\n\nThis project was developed for and is graciously sponsored by my employer,\n[Bestow, Inc.](https://hellobestow.com/). At Bestow, we aim to democratize life\ninsurance by providing simple, easy coverage, purchasable online in five minutes\nwith no doctors' visits and no hassles.\n\nWe're pretty much always hiring great developers, so if you'd like to work\nwith us, please check out [our careers page](https://hellobestow.com/careers/)!\n\n## Table of Contents\n\n- [safetywrap](#safetywrap)\n  - [Summary](#summary)\n    - [Sponsorship](#sponsorship)\n  - [Table of Contents](#table-of-contents)\n  - [Examples](#examples)\n    - [Get an enum member by its value, returning the member or None](#get-an-enum-member-by-its-value-returning-the-member-or-none)\n    - [Get an enum member by its value, returning an Option](#get-an-enum-member-by-its-value-returning-an-option)\n    - [Serialize a dict that may be missing keys, using default values](#serialize-a-dict-that-may-be-missing-keys-using-default-values)\n    - [Make an HTTP request, and if the status code is 200, convert the body to JSON and return the `data` key. If there is an error or the `data` key does not exist, return an error string](#make-an-http-request-and-if-the-status-code-is-200-convert-the-body-to-json-and-return-the-data-key-if-there-is-an-error-or-the-data-key-does-not-exist-return-an-error-string)\n  - [Usage](#usage)\n    - [Result[T, E]](#resultt-e)\n      - [Result Constructors](#result-constructors)\n        - [Ok](#ok)\n        - [Err](#err)\n        - [Result.of](#resultof)\n        - [Result.collect](#resultcollect)\n        - [Result.err_if](#resulterr_if)\n        - [Result.ok_if](#resultok_if)\n      - [Result Methods](#result-methods)\n        - [Result.and_](#resultand_)\n        - [Result.or_](#resultor_)\n        - [Result.and_then](#resultand_then)\n        - [Result.flatmap](#resultflatmap)\n        - [Result.or_else](#resultor_else)\n        - [Result.err](#resulterr)\n        - [Result.ok](#resultok)\n        - [Result.expect](#resultexpect)\n        - [Result.raise_if_err](#resultraise_if_err)\n        - [Result.expect_err](#resultexpect_err)\n        - [Result.is_err](#resultis_err)\n        - [Result.is_ok](#resultis_ok)\n        - [Result.iter](#resultiter)\n        - [Result.map](#resultmap)\n        - [Result.map_err](#resultmap_err)\n        - [Result.unwrap](#resultunwrap)\n        - [Result.unwrap_err](#resultunwrap_err)\n        - [Result.unwrap_or](#resultunwrap_or)\n        - [Result.unwrap_or_else](#resultunwrap_or_else)\n      - [Result Magic Methods](#result-magic-methods)\n    - [Option[T]](#optiont)\n      - [Option Constructors](#option-constructors)\n        - [Some](#some)\n        - [Nothing](#nothing)\n        - [Option.of](#optionof)\n        - [Option.nothing_if](#optionnothing_if)\n        - [Option.some_if](#optionsome_if)\n        - [Option.collect](#optioncollect)\n      - [Option Methods](#option-methods)\n        - [Option.and_](#optionand_)\n        - [Option.or_](#optionor_)\n        - [Option.xor](#optionxor)\n        - [Option.and_then](#optionand_then)\n        - [Option.flatmap](#optionflatmap)\n        - [Option.or_else](#optionor_else)\n        - [Option.expect](#optionexpect)\n        - [Option.raise_if_nothing](#optionraise_if_nothing)\n        - [Option.filter](#optionfilter)\n        - [Option.is_nothing](#optionis_nothing)\n        - [Option.is_some](#optionis_some)\n        - [Option.iter](#optioniter)\n        - [Option.map](#optionmap)\n        - [Option.map_or](#optionmap_or)\n        - [Option.map_or_else](#optionmap_or_else)\n        - [Option.ok_or](#optionok_or)\n        - [Option.ok_or_else](#optionok_or_else)\n        - [Option.unwrap](#optionunwrap)\n        - [Option.unwrap_or](#optionunwrap_or)\n        - [Option.unwrap_or_else](#optionunwrap_or_else)\n      - [Option Magic Methods](#option-magic-methods)\n  - [Performance](#performance)\n    - [Results](#results)\n    - [Discussion](#discussion)\n  - [Contributing](#contributing)\n\n## Examples\n\nIn general, these examples build from simple to complex. See [Usage](#usage)\nbelow for the full API specification.\n\n### Get an enum member by its value, returning the member or None\n\n```py\nimport typing as t\nfrom enum import Enum\n\nfrom result_types import Option, Result, Some\n\nT = t.TypeVar(\"T\", bound=Enum)\n\ndef enum_member_for_val(enum: t.Type[T], value: t.Any) -\u003e t.Optional[t.Any]:\n    \"\"\"Return Some(enum_member) or Nothing().\"\"\"\n    # Enums throw a `ValueError` if the value isn't present, so\n    # we'll either have `Ok(enum_member)` or `Err(ValueError)`.\n    # We unwrap and return the member if it's Ok, otherwise, we just\n    # return None\n    return Result.of(enum, value).unwrap_or(None)\n```\n\n### Get an enum member by its value, returning an Option\n\n```py\nimport typing as t\nfrom enum import Enum\n\nfrom result_types import Option, Result, Some\n\nT = t.TypeVar(\"T\", bound=Enum)\n\ndef enum_member_for_val(enum: t.Type[T], value: t.Any) -\u003e Option[T]:\n    \"\"\"Return Some(enum_member) or Nothing().\"\"\"\n    # Enums throw a `ValueError` if the value isn't present, so\n    # we'll either have `Ok(enum_member)` or `Err(ValueError)`.\n    # Calling `ok()` on a `Result` returns an `Option`\n    return Result.of(enum, value).ok()\n```\n\n### Serialize a dict that may be missing keys, using default values\n\n```py\nimport json\nfrom result_types import Result\n\ndef serialize(data: t.Dict[str, t.Union[int, str, float]]) -\u003e str:\n    \"\"\"Serialize the data.\n\n    Absent keys are \"[absent]\", rather than null. This allows us to maintain\n    information about whether a key was present or actually set to None.\n    \"\"\"\n    keys = (\"first\", \"second\", \"third\", \"fourth\")\n    # We can even use Result to catch any JSON serialization errors, so that\n    # this function will _always_ return a string!\n    Result.of(\n        json.dumps,\n        # Result.of() will intercept the KeyError and return an Err. We use\n        # `unwrap_or()` to discard the error and return the \"[absent]\" string\n        # instead; if the key was present, the Result was Ok, and we just\n        # return that value.\n        {k: Result.of(lambda: data[k]).unwrap_or(\"[absent]\") for k in keys}\n    ).unwrap_or(\"Could not serialize JSON from data!\")\n```\n\n### Make an HTTP request, and if the status code is 200, convert the body to JSON and return the `data` key. If there is an error or the `data` key does not exist, return an error string\n\n```py\nfrom functools import partial\n\nimport requests\nfrom requests import Response\nfrom result_types import Option, Result\n\n\ndef get_data(url: str) -\u003e str:\n    \"\"\"Get the data!\"\"\"\n    # We need to do manual type assignment sometimes when the code\n    # we're wrapping does not provide types.\n    # If the wrapped function raises any Exception, `res` will be\n    # Err(Exception). Otherwise it will be `Ok(Response)`.\n    res: Result[Response, Exception] = Result.of(requests.get, url)\n    return (\n        # We start as a `Result[Response, Exception]`\n        res\n        # And if we were an Err, map to a `Result[Response, str]`\n        .map_err(str)\n        # If we were Ok, and_then (aka flatmap) to a new `Result[Response, str]`\n        .and_then(lambda res: (\n            # Our return value starts as a `Result[Response, Response]`\n            Result.ok_if(lambda r: r.status_code == 200, res).map_err(\n                # So we map it to a `Result[Response, str]`\n                lambda r: str(f\"Bad status code: {r.status_code}\")\n            )\n        ))\n        # We are now a `Result[Response, str]`, where we are only Ok if\n        # our status code was 200.\n        # Now we transition to a `Result[dict, str]`\n        .and_then(lambda res: Result.of(res.json).map_err(str))\n        # And to a `Result[Option[str], str]`\n        .map(lambda js: Option.of(js.get(\"data\")).map(str))\n        # And to a `Result[str, str]`\n        .and_then(lambda data: data.ok_or(\"No data key in JSON!\"))\n        # If we are an error, convert us to an Ok with the error string\n        .or_else(Ok)\n        # And now we get either the Ok string or the Err string!\n        .unwrap()\n    )\n```\n\n## Usage\n\n### Result[T, E]\n\nA Result represents some value that may either be in an `Ok` state or\nan `Err` state.\n\n#### Result Constructors\n\n##### Ok\n\n`Ok(value: T) -\u003e Result[T, E]`\n\nConstruct an `Ok` Result directly with the value.\n\nExample:\n\n```py\ndef check_value_not_negative(val: int) -\u003e Result[int, str]:\n    \"\"\"Check that a value is not negative, or return an Err.\"\"\"\n    if val \u003e= 0:\n        return Ok(val)\n    return Err(f\"{val} is negative!\")\n```\n\n##### Err\n\n`Err(value: E) -\u003e Result[T, E]`\n\nConstruct an `Err` Result directly with the value.\n\nExample:\n\n```py\ndef check_value_not_negative(val: int) -\u003e Result[int, str]:\n    \"\"\"Check that a value is not negative, or return an Err.\"\"\"\n    if val \u003e= 0:\n        return Ok(val)\n    return Err(f\"{val} is negative!\")\n```\n\n##### Result.of\n\n`Result.of(fn: Callable[..., T], *args: t.Any, catch: t.Type[E], **kwargs) -\u003e Result[T, E]`\n\nCall a function with the provided arguments. If no error is thrown, return\n`Ok(result)`. Otherwise, return `Err(exception)`. By default, `Exception`\nis caught, but different error types may be provided with the `catch`\nkeyword argument.\n\nThe type of `E` MUST be `Exception` or one of its subclasses.\n\nThis constructor is designed to be useful in wrapping other APIs, builtin\nfunctions, etc.\n\nNote that due to a bug in mypy (see https://github.com/python/mypy/issues/3737),\nsometimes you need to explicitly specify the `catch` keyword argument,\neven if you're setting it to its default (`Exception`). This does not\nhappen consistently, but when it does, you will see mypy thinking\nthat the type of the `Result` is `Result[SomeType, \u003cnothing\u003e]`.\n\nExample:\n\n```py\nimport json\n\ndef parse_json(string: str) -\u003e Result[dict, Exception]:\n    \"\"\"Parse a JSON object into a dict.\"\"\"\n    return Result.of(json.loads, string)\n```\n\n##### Result.collect\n\n`Result.collect(iterable: Iterable[T, E]) -\u003e Result[Tuple[T, ...], E]`\n\nConvert an iterable of Results into a single Result. If all Results were\nOk, Ok values are collected into a Tuple in the final Result. If any Results\nwere Err, the Err result is returned directly.\n\nExample:\n\n```py\nassert Result.collect([Ok(1), Ok(2), Ok(3)]) == Ok((1, 2, 3))\nassert Result.collect([Ok(1), Err(\"no\"), Ok(3)]) == Err(\"no\")\n```\n\n##### Result.err_if\n\n`Result.err_if(predicate: t.Callable[[T], bool], value: T) -\u003e Result[T, T]`\n\nRun a predicate on some value, and return `Err(val)` if the predicate returns\nTrue, or `Ok(val)` if the predicate returns False.\n\nExample:\n\n```py\nfrom requests import Response\n\ndef checked_response(response: Response) -\u003e Result[Response, Response]:\n    \"\"\"Try to get a response from the server.\"\"\"\n    return Result.err_if(lambda r: r.status_code \u003e= 300, response)\n```\n\n##### Result.ok_if\n\n`Result.ok_if(predicate: t.Callable[[T], bool], value: T) -\u003e Result[T, T]`\n\nRun a predicate on some value, and return `Ok(val)` if the predicate returns\nTrue, or `Err(val)` if the predicate returns False.\n\nExample:\n\n```py\ndef checked_data(data: dict) -\u003e Result[dict, dict]:\n    \"\"\"Check if data has expected keys.\"\"\"\n    expected_keys = (\"one\", \"two\", \"three\")\n    return Result.ok_if(lambda d: all(k in d for k in expected_keys), data)\n```\n\n#### Result Methods\n\n##### Result.and_\n\n`Result.and_(self, res: Result[U, E]) -\u003e Result[U, E]`\n\nIf this Result is `Ok`, return `res`. If this result is `Err`, return this\nResult. This can be used to short circuit a chain of Results on encountering\nthe first error.\n\nExample:\n\n```py\nassert Ok(5).and_(Ok(6)) == Ok(6)\nassert Err(1).and_(Ok(6)) == Err(1)\nassert Err(1).and_(Err(2)).and_(Ok(5)) == Err(1)\nassert Ok(5).and_(Err(1)).and_(Ok(6)) == Err(1)\n```\n\n##### Result.or_\n\n`Result.or_(self, res: Result[T, F]) -\u003e Result[T, F]`\n\nIf this Result is `Err`, return `res`. Otherwise, return this Result.\n\nExample:\n\n```py\nassert Err(1).or_(Ok(5)) == Ok(5)\nassert Err(1).or_(Err(2)) == Err(2)\nassert Ok(5).or_(Ok(6)) == Ok(5)\nassert Ok(5).or_(Err(1)) == Ok(5)\n```\n\n##### Result.and_then\n\n`Result.and_then(self, fn: t.Callable[[T], Result[U, E]]) -\u003e Result[U, E]`\n\nIf this Result is `Ok`, call the provided function with the wrapped value of\nthis Result and return the Result of that function. This allows easily\nchaining multiple Result-generating calls together to yield a final\nResult. This method is an alias of [`Result.flatmap`](#resultflatmap)\n\nExample:\n\n```py\nassert Ok(5).and_then(lambda val: Ok(val + 1)) == Ok(6)\nassert Err(1).and_then(lambda val: Ok(val + 1)) == Err(1)\n```\n\n##### Result.flatmap\n\n`Result.flatmap(self, fn: t.Callable[[T], Result[U, E]]) -\u003e Result[U, E]`\n\nIf this Result is `Ok`, call the provided function with the wrapped value of\nthis Result and return the Result of that function. This allows easily\nchaining multiple Result-generating calls together to yield a final\nResult. This method is an alias of [`Result.and_then`](#resultand_then)\n\nExample:\n\n```py\nassert Ok(5).flatmap(lambda val: Ok(val + 1)) == Ok(6)\nassert Err(1).flatmap(lambda val: Ok(val + 1)) == Err(1)\n```\n\n##### Result.or_else\n\n`Result.or_else(self, fn: t.Callable[[E], Result[T, F]]) -\u003e Result[T, F])`\n\nIf this result is `Err`, call the provided function with the wrapped error\nvalue of this Result and return the Result of that function. This allows\neasily handling potential errors in a way that still returns a final Result.\n\nExample:\n\n```py\nassert Ok(5).or_else(Ok) == Ok(5)\nassert Err(1).or_else(Ok) == Ok(1)\n```\n\n##### Result.err\n\n`Result.err(self) -\u003e Option[E]`\n\nConvert this Result into an Option, returning Some(err_val) if this Result\nis `Err`, or Nothing() if this Result is `Ok`.\n\nExample:\n\n```py\nassert Ok(5).err() == Nothing()\nassert Err(1).err() == Some(1)\n```\n\n##### Result.ok\n\n`Result.ok(self) -\u003e Option[T]`\n\nConvert this Result into an Option, returning `Some(val)` if this Result is\n`Ok`, or `Nothing()` if this result is `Err`.\n\nExample:\n\n```py\nassert Ok(5).ok() == Some(5)\nassert Err(1).ok() == Nothing()\n```\n\n##### Result.expect\n\n`Result.expect(self, msg: str, exc_cls: t.Type[Exception] = RuntimeError) -\u003e T`\n\nReturn the wrapped value if this Result is `Ok`. Otherwise, raise an error,\ninstantiated with the provided message and the stringified error value.\nBy default, a `RuntimeError` is raised, but an alternative error may be\nprovided using the `exc_cls` keyword argument. This method is an alias for\n[`Result.raise_if_err`](#resultraise_if_err).\n\nExample:\n\n```py\nimport pytest\n\nwith pytest.raises(RuntimeError) as exc:\n    Err(5).expect(\"Bad value\")\n    assert str(exc.value) == \"Bad value: 5\"\n\nassert Ok(1).expect(\"Bad value\") == 1\n```\n\n##### Result.raise_if_err\n\n`Result.raise_if_err(self, msg: str, exc_cls: t.Type[Exception] = RuntimeError) -\u003e T`\n\nReturn the wrapped value if this Result is `Ok`. Otherwise, raise an error,\ninstantiated with the provided message and the stringified error value.\nBy default, a `RuntimeError` is raised, but an alternative error may be\nprovided using the `exc_cls` keyword argument. This method is an alias for\n[`Result.expect`](#resultexpect).\n\nExample:\n\n```py\nimport pytest\n\nwith pytest.raises(RuntimeError) as exc:\n    Err(5).raise_if_err(\"Bad value\")\n    assert str(exc.value) == \"Bad value: 5\"\n\nassert Ok(1).raise_if_err(\"Bad value\") == 1\n```\n\n##### Result.expect_err\n\n`Result.expect_err(self, msg: str, exc_cls: t.Type[Exception] = RuntimeError) -\u003e E`\n\nReturn the wrapped value if this Result is `Err`. Otherwise, raise an error,\ninstantiated with the provided message and the stringified Ok value.\nBy default, a `RuntimeError` is raised, but an alternative error may be\nprovided using the `exc_cls` keyword argument.\n\nExample:\n\n```py\nimport pytest\n\nwith pytest.raises(RuntimeError) as exc:\n    Ok(5).expect_err(\"Unexpected good value\")\n    assert str(exc.value) == \"Unexpected good value: 5\"\n\nassert Err(1).expect_err(\"Unexpected good value\") == 1\n```\n\n##### Result.is_err\n\n`Result.is_err(self) -\u003e bool`\n\nReturn True if this Result is `Err`, or `False` if this Result is `Ok`.\n\nExample:\n\n```py\nassert Err(1).is_err() is True\nassert Ok(1).is_err() is False\n```\n\n##### Result.is_ok\n\n`Result.is_ok(self) -\u003e bool`\n\nReturn True if this Result is `Ok`, or `False` if this Result is `Err`.\n\nExample:\n\n```py\nassert Ok(1).is_err() is True\nassert Err(1).is_err() is False\n```\n\n##### Result.iter\n\n`Result.iter(self) -\u003e Iterator[T]`\n\nReturn an iterator with length 1 over the wrapped value if this Result is `Ok`.\nOtherwise, return a 0-length iterator.\n\nExample:\n\n```py\nassert tuple(Ok(1).iter()) == (1,)\nassert tuple(Err(1).iter()) == ()\n```\n\n##### Result.map\n\n`Result.map(self, fn: t.Callable[[T], U]) -\u003e Result[U, E]`\n\nIf this Result is `Ok`, apply the provided function to the wrapped value,\nand return a new `Ok` Result with the result of the function. If this Result\nis `Err`, do not apply the function and return this Result unchanged.\n\n**Warning:** no error checking is performed while applying the provided\nfunction, and exceptions applying the function are not caught. If you need\nto map with error handling, consider using `and_then` (aka `flatmap`) in\nconjunction with the `Result.of` constructor, e.g.\n`assert Ok(0).and_then(partial(Result.of, lambda i: 10 / i)) == Err(ZeroDivisionError('division by zero'))`\n\nExample:\n\n```py\nassert Ok(1).map(str) == Ok(\"1\")\nassert Err(1).map(str) == Err(1)\n```\n\n##### Result.map_err\n\n`Result.map_err(self, fn: t.Callable[[E], F]) -\u003e Result[T, F]`\n\nIf this Result is `Err`, apply the provided function to the wrapped value,\nand return a new `Err` Result with the result of the function. If this Result\nis `Ok`, do not apply the function and return this Result unchanged.\n\n**Warning:** no error checking is performed while applying the provided\nfunction, and exceptions applying the function are not caught.\n\nExample:\n\n```py\nassert Err(1).map_err(lambda i: i + 1) == Err(2)\nassert Ok(1).map_err(lambda i: i + 1) == Ok(1)\n```\n\n##### Result.unwrap\n\n`Result.unwrap(self) -\u003e T`\n\nIf this Result is `Ok`, return the wrapped value. If this Result is `Err`,\nthrow a `RuntimeError`.\n\nExample:\n\n```py\nimport pytest\n\nassert Ok(1).unwrap() == 1\n\nwith pytest.raises(RuntimeError):\n    Err(1).unwrap()\n```\n\n##### Result.unwrap_err\n\n`Result.unwrap_err(self) -\u003e E`\n\nIf this Result is `Err`, return the wrapped value. If this Result is `Ok`,\nthrow a `RuntimeError`.\n\nExample:\n\n```py\nimport pytest\n\nassert Err(1).unwrap() == 1\n\nwith pytest.raises(RuntimeError):\n    Ok(1).unwrap()\n```\n\n##### Result.unwrap_or\n\n`Result.unwrap_or(self, alternative: U) -\u003e t.Union[T, U]`\n\nIf this Result is `Ok`, return the wrapped value. Otherwise, if this Result\nis `Err`, return the provided alternative.\n\nExample:\n\n```py\nassert Ok(1).unwrap_or(5) == 1\nassert Err(1).unwrap_or(5) == 5\n```\n\n##### Result.unwrap_or_else\n\n`Result.unwrap_or_else(self, fn: t.Callable[[E], U]) -\u003e t.Union[T, U]`\n\nIf this Result is `Ok`, return the wrapped value. Otherwise, if this Result\nis `Err`, call the supplied function with the wrapped error value and return\nthe result.\n\nExample:\n\n```py\nassert Ok(1).unwrap_or_else(str) == 1\nassert Err(1).unwrap_or_else(str) == \"1\"\n```\n\n#### Result Magic Methods\n\n##### Result.__iter__  \u003c!-- omit in toc --\u003e\n\n`Result.__iter__(self) -\u003e t.Iterator[T]`\n\nImplement the iterator protocol, allowing iteration over the results of\n[`Result.iter`](#resultiter). If this Result is `Ok`, return an iterator\nof length 1 containing the wrapped value. Otherwise, if this Result is `Err`,\nreturn a 0-length iterator.\n\nExample:\n\n```py\n# Can be passed to methods that take iterators\nassert tuple(Ok(1)) == (1,)\nassert tuple(Err(1)) == ()\n\n# Can be used in `for in` constructs, including comprehensions\nassert [val for val in Ok(5)] == [5]\nassert [val for val in Err(5)] == []\n\n\n# More for-in usage.\nfor val in Ok(5):\n    pass\nassert val == 5\n\nval = None\nfor val in Err(1):\n    pass\nassert val is None\n```\n\n##### Result.__eq__ \u003c!-- omit in toc --\u003e\n\n`Result.__eq__(self, other: Any) -\u003e bool`\n\nEnable equality checking using `==`.\n\nCompare the Result with `other`. Return True if `other` is the same type of\nResult with the same wrapped value. Otherwise, return False.\n\nExample:\n\n```py\nassert (Ok(5) == Ok(5)) is True\nassert (Ok(5) == Ok(6)) is False\nassert (Ok(5) == Err(5)) is False\nassert (Ok(5) == 5) is False\n```\n\n##### Result.__ne__ \u003c!-- omit in toc --\u003e\n\n`Result.__ne__(self, other: Any) -\u003e bool`\n\nEnable inequality checking using `!=`.\n\nCompare the Result with `other`. Return False if `other` is the same type of\nResult with the same wrapped value. Otherwise, return True.\n\nExample:\n\n```py\nassert (Ok(5) != Ok(5)) is False\nassert (Ok(5) != Ok(6)) is True\nassert (Ok(5) != Err(5)) is True\nassert (Ok(5) != 5) is True\n```\n\n##### Result.__str__ \u003c!-- omit in toc --\u003e\n\n`Result.__str__(self) -\u003e str`\n\nEnable useful stringification via `str()`.\n\nExample:\n\n```py\nassert str(Ok(5)) == \"Ok(5)\"\nassert str(Err(5)) == \"Err(5)\"\n```\n\n##### Result.__repr__ \u003c!-- omit in toc --\u003e\n\n`Result.__repr__(self) -\u003e str`\n\nEnable useful stringification via `repr()`.\n\nExample:\n\n```py\nassert repr(Ok(5)) == \"Ok(5)\"\nassert repr(Err(5)) == \"Err(5)\"\n```\n\n### Option[T]\n\nAn Option represents either `Some` value or `Nothing`.\n\n#### Option Constructors\n\n##### Some\n\n`Some(value: T) -\u003e Option[T]`\n\nConstruct a `Some` Option directly with a value.\n\nExample:\n\n```py\ndef file_contents(path: str) -\u003e Option[str]:\n    \"\"\"Return the file contents or Nothing.\"\"\"\n    try:\n        with open(path) as f:\n            return Some(f.read())\n    except IOError:\n        return Nothing()\n```\n\n##### Nothing\n\n`Nothing() -\u003e Option[T]`\n\nConstruct a `Nothing` Option directly with a value.\n\nNote: as an implementation detail, `Nothing` is implemented as a singleton,\nto avoid instantiation time for any `Nothing` created after the first.\nHowever since this is an implementation detail, `Nothing` Options should\nstill be compared with `==` rather than `is`.\n\nExample:\n\n```py\ndef file_contents(path: str) -\u003e Option[str]:\n    \"\"\"Return the file contents or Nothing.\"\"\"\n    try:\n        with open(path) as f:\n            return Some(f.read())\n    except IOError:\n        return Nothing()\n```\n\n##### Option.of\n\n`Option.of(value: t.Optional[T]) -\u003e Option[T]`\n\nConvert an optional value into an Option. If the value is not `None`, return\n`Some(value)`. Otherwise, if the value is `None`, return `Nothing()`.\n\nExample:\n\n```py\nassert Option.of(None) == Nothing()\nassert Option.of({}.get(\"a\")) == Nothing()\nassert Option.of(\"a\") == Some(\"a\")\nassert Option.of({\"a\": \"b\"}) == Some(\"b\")\n```\n\n##### Option.nothing_if\n\n`Option.nothing_if(predicate: t.Callable[[T], bool], value: T) -\u003e Option[T]`\n\nCall the provided predicate function with the provided value. If the predicate\nreturns True, return `Nothing()`. If the predicate returns False, return\n`Some(value)`.\n\nExample:\n\n```py\nassert Option.nothing_if(lambda val: val.startswith(\"_\"), \"_private\") == Nothing()\nassert Option.nothing_if(lambda val: val.startswith(\"_\"), \"public\") == Some(\"public\")\n```\n\n##### Option.some_if\n\n`Option.some_if(predicate: t.Callable[[T], bool], value: T) -\u003e Option[T]`\n\nCall the provided predicate function with the provided value. If the predicate\nreturns True, return `Some(value)`. If the predicate returns False, return\n`Nothing()`.\n\nExample:\n\n```py\nassert Option.some_if(bool, [1, 2, 3]) == Some([1, 2, 3])\nassert Option.some_if(bool, []) == Nothing()\n```\n\n##### Option.collect\n\n`Option.collect(options: t.Iterable[Option[T]]) -\u003e Option[t.Tuple[T, ...]]`\n\nCollect a series of Options into single Option.\n\nIf all options are `Some[T]`, the result is `Some[Tuple[T, ...j]]`. If\nany options are `Nothing`, the result is `Nothing`.\n\nExample:\n\n```py\nassert Option.collect([Some(1), Some(2), Some(3)]) == Some((1, 2, 3))\nassert Option.collect([Some(1), Nothing(), Some(3)]) == Nothing()\n```\n\n#### Option Methods\n\n##### Option.and_\n\n`Option.and_(alternative: Option[U]) -\u003e Option[U]`\n\nIf this Option is `Nothing`, return it unchanged. Otherwise, if this Option\nis `Some`, return the provided `alternative` Option.\n\nExample:\n\n```py\nassert Some(1).and_(Some(2)) == Some(2)\nassert Nothing().and_(Some(2)) == Nothing()\nassert Some(1).and_(Nothing()) == Nothing()\nassert Nothing().and_(Nothing()) == Nothing()\nassert Some(1).and_(Nothing()).and_(Some(2)) == Nothing()\n```\n\n##### Option.or_\n\n`Option.or_(alternative: Option[T]) -\u003e Option[T]`\n\nIf this Option is `Nothing`, return the provided `alternative` Option.\nOtherwise, if this Option is `Some`, return it unchanged.\n\nExample:\n\n```py\nassert Some(1).or_(Some(2)) == Some(1)\nassert Some(1).or_(Nothing()) == Some(1)\nassert Nothing().or_(Some(1)) == Some(1)\nassert Nothing().or_(Nothing()) == Nothing()\n```\n\n##### Option.xor\n\n`Option.xor(alternative: Option[T]) -\u003e Option[T]`\n\nExclusive or. Return `Some` Option iff (if and only if) exactly one of\nthis Option and hte provided `alternative` are Some. Otherwise, return\n`Nothing`.\n\nExample:\n\n```py\nassert Some(1).xor(Nothing()) == Some(1)\nassert Nothing().xor(Some(1)) == Some(1)\nassert Some(1).xor(Some(2)) == Nothing()\nassert Nothing().xor(Nothing()) == Nothing()\n```\n\n##### Option.and_then\n\n`Option.and_then(self, fn: t.Callable[[T], Option[U]]) -\u003e Option[U]`\n\nIf this Option is `Some`, call the provided, Option-returning function with\nthe contained value and return whatever Option it returns. If this Option\nis `Nothing`, return it unchanged. This method is an alias for\n[`Option.flatmap`](#optionflatmap)\n\nExample:\n\n```py\nassert Some(1).and_then(lambda i: Some(i + 1)) == Some(2)\nassert Nothing().and_then(lambda i: Some(i + 1)) == Nothing()\n```\n\n##### Option.flatmap\n\n`Option.flatmap(self, fn: t.Callable[[T], Option[U]]) -\u003e Option[U]`\n\nIf this Option is `Some`, call the provided, Option-returning function with\nthe contained value and return whatever Option it returns. If this Option\nis `Nothing`, return it unchanged. This method is an alias for\n[`Option.and_then`](#optionand_then)\n\nExample:\n\n```py\nassert Some(1).flatmap(Some) == Some(1)\nassert Nothing().flatmap(Some) == Nothing()\n```\n\n##### Option.or_else\n\n`Option.or_else(self, fn: t.Callable[[], Option[T]]) -\u003e Option[T]`\n\nIf this Option is `Nothing`, call the provided, Option-returning function\nand return whatever Option it returns. If this Option is `Some`, return it\nunchanged.\n\nExample:\n\n```py\nassert Nothing().or_else(lambda: Some(1)) == Some(1)\nassert Some(1).or_else(lambda: Some(2)) == Some(1)\n```\n\n##### Option.expect\n\n`Option.expect(self, msg: str, exc_cls: t.Type[Exception] = RuntimeError) -\u003e T`\n\nIf this Option is `Some`, return the wrapped value. Otherwise, if this\nOption is `Nothing`, raise an error instantiated with the provided message.\nBy default, a `RuntimeError` is raised, but a custom exception class may be\nprovided via the `exc_cls` keyword argument. This method is an alias\nof [`Option.raise_if_nothing`](#optionraise_if_nothing).\n\nExample:\n\n```py\nimport pytest\n\nwith pytest.raises(RuntimeError) as exc:\n    Nothing().expect(\"Nothing here\")\n    assert str(exc.value) == \"Nothing here\"\n\nassert Some(1).expect(\"Nothing here\") == 1\n```\n\n##### Option.raise_if_nothing\n\n`Option.raise_if_nothing(self, msg: str, exc_cls: t.Type[Exception] = RuntimeError) -\u003e T`\n\nIf this Option is `Some`, return the wrapped value. Otherwise, if this\nOption is `Nothing`, raise an error instantiated with the provided message.\nBy default, a `RuntimeError` is raised, but a custom exception class may be\nprovided via the `exc_cls` keyword argument. This method is an alias\nof [`Option.expect`](#optionexpect).\n\nExample:\n\n```py\nimport pytest\n\nwith pytest.raises(RuntimeError) as exc:\n    Nothing().raise_if_nothing(\"Nothing here\")\n    assert str(exc.value) == \"Nothing here\"\n\nassert Some(1).raise_if_nothing(\"Nothing here\") == 1\n```\n\n##### Option.filter\n\n`Option.filter(self, predicate: t.Callable[[T], bool]) -\u003e Option[T]`\n\nIf this Option is `Some`, call the provided predicate function with the wrapped\nvalue. If the predicate returns True, return `Some` containing the wrapped\nvalue of this Option. If the predicate returns False, return `Nothing`. If\nthis Option is `Nothing`, return it unchanged.\n\nExample:\n\n```py\ndef is_even(val: int) -\u003e bool:\n    \"\"\"Return whether the value is even.\"\"\"\n    return val % 2 == 0\n\nassert Some(2).filter(is_even) == Some(2)\nassert Some(1).filter(is_even) == Nothing()\nassert Nothing().filter(is_even) == Nothing()\n```\n\n##### Option.is_nothing\n\n`Option.is_nothing(self) -\u003e bool`\n\nIf this Option is `Nothing`, return True. Otherwise, if this Option is\n`Some`, return False.\n\nExample:\n\n```py\nassert Nothing().is_nothing() is True\nassert Some(1).is_nothing() is False\n```\n\n##### Option.is_some\n\n`Option.is_some(self) -\u003e bool`\n\nIf this Option is `Some`. Otherwise, if this Option is `Nothing`, return False.\n\nExample:\n\n```py\nassert Some(1).is_some() is True\nassert Nothing().is_some() is False\n```\n\n##### Option.iter\n\n`Option.iter(self) -\u003e t.Iterator[T]`\n\nIf this Option is `Some`, return an iterator of length one over the wrapped\nvalue. Otherwise, if this Option is `Nothing`, return a 0-length iterator.\n\nExample:\n\n```py\nassert tuple(Some(1).iter()) == (1,)\nassert tuple(Nothing().iter()) == ()\n```\n\n##### Option.map\n\n`Option.map(self, fn: t.Callable[[T], U]) -\u003e Option[U]`\n\nIf this Option is `Some`, apply the provided function to the wrapped value,\nand return `Some` wrapping the result of the function. If this Option is\n`Nothing`, return this Option unchanged.\n\nExample:\n\n```py\nassert Some(1).map(str) == Some(\"1\")\nassert Nothing().map(str) == Nothing()\nassert Some(1).map(str).map(lambda x: x + \"a\").map(str.upper) == Some(\"1A\")\n```\n\n##### Option.map_or\n\n`Option.map_or(self, default: U, fn: t.Callable[[T], U]) -\u003e U`\n\nIf this Option is `Some`, apply the provided function to the wrapped value\nand return the result. If this Option is `Nothing`, return the provided\ndefault value.\n\nExample:\n\n```py\nassert Some(1).map_or(\"no value\", str) == \"1\"\nassert Nothing().map_or(\"no value\", str) == \"no value\"\n```\n\n##### Option.map_or_else\n\n`Option.map_or_else(self, default: t.Callable[[], U], fn: t.Callable[[T], U]) -\u003e U`\n\nIf this Option is `Some`, apply the provided function to the wrapped value and\nreturn the result. If this Option is `Nothing`, call the provided default\nfunction with no arguments and return the result.\n\nExample:\n\n```py\nfrom datetime import datetime, date\n\nassert Some(\"2005-08-28\").map_or_else(\n    date.today,\n    lambda t: datetime.strptime(t, \"%Y-%m-%d\").date()\n) == datetime(2005, 8, 28).date()\n\nassert Nothing().map_or_else(\n    date.today,\n    lambda t: datetime.strptime(t, \"%Y-%m-%d\").date()\n) == date.today()\n```\n\n##### Option.ok_or\n\n`Option.ok_or(self, err: E) -\u003e Result[T, E]`\n\nIf this Option is `Some`, return an `Ok` Result wrapping the contained\nvalue. Otherwise, return an `Err` result wrapping the provided error.\n\nExample:\n\n```py\nassert Some(1).ok_or(\"no value!\") == Ok(1)\nassert Nothing().ok_or(\"no value!\") == Err(\"no value!\")\n```\n\n##### Option.ok_or_else\n\n`Option.ok_or_else(self, err_fn: t.Callable[[], E]) -\u003e Result[T, E]`\n\nIf this Option is `Some`, return an `Ok` Result wrapping the contained\nvalue. Otherwise, call the provided `err_fn` and wrap its return value\nin an `Err` Result.\n\nExample:\n\n```py\nfrom functools import partial\n\ndef make_err_msg(msg: str) -\u003e str:\n    \"\"\"Make an error message with some starting text.\"\"\"\n    return f\"[MY_APP_ERROR] -- {msg}\"\n\nassert Some(1).ok_or_else(partial(make_err_msg, \"no value!\")) == Ok(1)\nassert Nothing().ok_or_else(partial(make_err_msg, \"no value!\")) == Err(\n    \"[MY_APP_ERROR] -- no value!\"\n)\n```\n\n##### Option.unwrap\n\n`Option.unwrap(self) -\u003e T`\n\nIf this Option is `Some`, return the wrapped value. Otherwise, raise a\n`RuntimeError`.\n\nExample:\n\n```py\nimport pytest\n\nassert Some(1).unwrap() == 1\n\nwith pytest.raises(RuntimeError):\n    Nothing().unwrap()\n```\n\n##### Option.unwrap_or\n\n`Option.unwrap_or(self, default: U) -\u003e t.Union[T, U]`\n\nIf this Option is `Some`, return the wrapped value. Otherwise, return the\nprovided default.\n\nExample:\n\n```py\nassert Some(1),unwrap_or(-1) == 1\nassert Nothing().unwrap_or(-1) == -1\n```\n\n##### Option.unwrap_or_else\n\n`Option.unwrap_or_else(self, fn: t.Callable[[], U]) -\u003e t.Union[T, U]`\n\nIf this Option is `Some`, return the wrapped value. Otherwise, return the\nresult of the provided function.\n\nExample:\n\n```py\nfrom datetime import date\n\nassert Some(date(2001, 1, 1)).unwrap_or_else(date.today) == date(2001, 1, 1)\nassert Nothing().unwrap_or_else(date.today) == date.today()\n```\n\n#### Option Magic Methods\n\n##### Option.__iter__ \u003c!-- omit in toc --\u003e\n\n`Option.__iter__(self) -\u003e t.Iterator[T]`\n\nImplement the iterator protocol, allowing iteration over the results of\n[`Option.iter`](#optioniter). If this Option is `Ok`, return an iterator\nof length 1 containing the wrapped value. Otherwise, if this Option is `Nothing`,\nreturn a 0-length iterator.\n\nExample:\n\n```py\n# Can be passed to methods that take iterators\nassert tuple(Some(1)) == (1,)\nassert tuple(Nothing()j) == ()\n\n# Can be used in `for in` constructs, including comprehensions\nassert [val for val in Some(1)] == [1]\nassert [val for val in Nothing()] == []\n\n\n# More for-in usage.\nfor val in Some(1):\n    pass\nassert val == 1\n\nval = None\nfor val in Nothing():\n    pass\nassert val is None\n```\n\n##### Option.__eq__ \u003c!-- omit in toc --\u003e\n\n`Option.__eq__(self, other: Any) -\u003e bool`\n\nEnable equality checking using `==`.\n\nCompare this Option with `other`. Return True if `other` is the same type of\nOption with the same wrapped value. Otherwise, return False.\n\nExample:\n\n```py\nassert (Some(1) == Some(1)) is True\nassert (Some(1) == Some(2)) is False\nassert (Some(1) == Nothing()) is False\nassert (Some(1) == 1) is False\n```\n\n##### Option.__ne__ \u003c!-- omit in toc --\u003e\n\n`Option.__ne__(self, other: Any) -\u003e bool`\n\nEnable inequality checking using `!=`.\n\nCompare the Option with `other`. Return False if `other` is the same type of\nOption with the same wrapped value. Otherwise, return True.\n\nExample:\n\n```py\nassert (Some(1) != Some(1)) is False\nassert (Some(1) != Some(2)) is True\nassert (Some(1) != Nothing()) is True\nassert (Some(1) != 1) is True\n```\n\n##### Option.__str__ \u003c!-- omit in toc --\u003e\n\n`Option.__str__(self) -\u003e str`\n\nEnable useful stringification via `str()`.\n\nExample:\n\n```py\nassert str(Some(1)) == \"Some(1)\"\nassert str(Nothing()) == \"Nothing()\"\n```\n\n##### Option.__repr__ \u003c!-- omit in toc --\u003e\n\n`Option.__repr__(self) -\u003e str`\n\nEnable useful stringification via `repr()`.\n\nExample:\n\n```py\nassert repr(Some(1)) == \"Some(1)\"\nassert repr(Nothing()) == \"Nothing()\"\n```\n\n## Performance\n\nBenchmarks may be run with `make bench`. Benchmarking utilities are provided\nin [`bench/`](/bench).\n\nCurrently, the [`sample.py`](/bench/sample.py) benchmark defines two data\nstores, one using classical python error handling (or lack thereof), and\nthe other using this library's wrapper types. Some simple operations\nare performed using each data store for comparison.\n\n[`runner.sh`](/bench/runner.sh) runs the benchmarks two ways. First, it uses\n[hyperfine] to run the benchmarks as a normal python script 100 times and\ndisplay information about the run time. It then uses python's builtin\n[timeit](https://docs.python.org/3/library/timeit.html) module to measure\nthe code execution time in isolation over one million runs, without the\nadded overhead of spinning up the interpreter to parse and run the script.\n\n### Results\n\nThe `Result` and `Option` wrapper types add minimal overhead to\nexecution time, which will not be noticeable for most real-world workloads.\nHowever, care should be taken if using these types in \"hot paths.\"\n\nRun in isolation, the sample code using `Result` and `Option` types is\nabout six times slower than builtin exception handling:\n\n| Method    | Number of Executions | Average Execution Time | Relative to Classical |\n| --------- | -------------------- | ---------------------- | --------------------- |\n| Classical | 1,000,000 (1E6)      | 3.79E-6 s (3.79 \u0026mu;s) | 1x                    |\n| Wrapper   | 1,000,000 (1E6)      | 2.31E-5 s (23.1 \u0026mu;s) | 6.09x                 |\n\nWhen run as part of a Python script, there is no significant difference\nbetween using code with these wrapper types versus code that uses builtin\nexception handling and nested if statements.\n\n| Method    | Number of Executions | Average Execution Time | Relative to Classical |\n| --------- | -------------------- | ---------------------- | --------------------- |\n| Classical | 100                  | 32.2 ms                | 1x                    |\n| Wrapper   | 100                  | 32.5 ms                | 1.01x                 |\n\n### Discussion\n\nCare has been taken to make the wrapper types in this library as performant\nas possible. All types use `__slots__` to avoid allocating a dictionary for\ninstance variables, and wrapper variants (e.g. `Ok` and `Err` for `Result`)\nare implemented as separate subclasses of `Result` rather than a shared\nclass in order to avoid needing to perform if/else branching or `isinstance()`\nchecks, which are notoriously slow in Python.\n\nThat being said, using these types _is_ doing more than the builtin error\nhandling! Instances are being constructed and methods are being accessed.\nBoth of these are relatively quick in Python, but definitely not quicker\nthan doing nothing, so this library will probably never be quite as performant\nas raw exception handling. That being said, that is not its aim! The goal\nis to be as quick as possible, preferably within striking distance of\nregular old idiomatic python, while providing significantly more ergonomics\nand type safety around handling errors and absent data.\n\n## Contributing\n\nContributions are welcome! To get started, you'll just need a local install\nof Python 3.\n\nOnce you've forked and cloned the repo, you can run:\n\n- `make test` - run tests using your local interpreter\n- `make fmt` - format code using [black](https://github.com/python/black)\n- `make lint` - check code with a variety of analysis tools\n- `make bench` - run benchmarks\n\nSee the [`Makefile`](Makefile) for other commands.\n\nThe CI system requires that `make lint` and `make test` run successfully\n(exit status of 0) in order to merge code.\n\n`result_types` is compatible with Python \u003e= 3.6. You can run against\nall supported python versions with `make test-all-versions`. This requires\nthat `docker` be installed on your local system. Alternatively, if you\nhave all required Python versions installed, you may run `make tox` to\nrun against your local interpreters.\n\n[hyperfine]: https://github.com/sharkdp/hyperfine\n[rust-result]: https://doc.rust-lang.org/std/result/\n[rust-option]: https://doc.rust-lang.org/std/option/\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmplanchard%2Fsafetywrap","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmplanchard%2Fsafetywrap","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmplanchard%2Fsafetywrap/lists"}