{"id":24016459,"url":"https://github.com/zechcodes/tramp","last_synced_at":"2025-02-25T20:17:15.923Z","repository":{"id":241980323,"uuid":"808367554","full_name":"ZechCodes/Tramp","owner":"ZechCodes","description":"No idea but we have a result type!","archived":false,"fork":false,"pushed_at":"2024-11-21T16:16:48.000Z","size":46,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-01-08T08:51:24.607Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ZechCodes.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-05-30T23:36:31.000Z","updated_at":"2024-11-21T16:13:55.000Z","dependencies_parsed_at":"2024-05-31T01:35:34.926Z","dependency_job_id":"dfcba8a4-b076-44c7-9e0e-cd1cf73f1b02","html_url":"https://github.com/ZechCodes/Tramp","commit_stats":null,"previous_names":["zechcodes/tramp"],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ZechCodes%2FTramp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ZechCodes%2FTramp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ZechCodes%2FTramp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ZechCodes%2FTramp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ZechCodes","download_url":"https://codeload.github.com/ZechCodes/Tramp/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240738118,"owners_count":19849549,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":[],"created_at":"2025-01-08T08:51:28.547Z","updated_at":"2025-02-25T20:17:15.904Z","avatar_url":"https://github.com/ZechCodes.png","language":"Python","readme":"from tramp.as_completed import AsCompleted\n\n# Tramp\n\nA collection of useful utilities that can be used in any project.\n\n## Installation\n\n```python\npip install tramp\n```\n\n## Annotations\n\nA utility for evaluating string annotations with support for forward references when a name cannot be evaluated. Forward references can be evaluated later by calling the forward reference's `evaluate` method. This is a similar API to [PEP 649](https://peps.python.org/pep-0649/), although it is not fully compatible and only emulates the behavior of `Format.FORWARDREF`. Certain things are not possible (at least not without some serious hacking) such as using `ForwardRef` instances as that would break many annotation types from the `typing` module. To help with this limitation the `tramp.annotations.ForwardRefMeta` type overrides the `isinstance` check to return `True` when Tramp's version of the `ForwardRef` class is used in an instance check (`isinstance` or a match/case). The goal is to implement the most essential parts of the PEP to begin reaping the benefits of forward references now with the least necessary refactoring later.\n\nOn Python 3.14 Tramp falls through to the PEP 649 implementation.\n\n```python\nfrom tramp.annotations import get_annotations\n\n\nclass Foo:\n    bar: \"Bar\"\n\n\nannotations = get_annotations(Foo)  # {'bar': \u003cForwardRef 'Bar'\u003e}\n\n\nclass Bar:\n    pass\n\n\nannotations[\"bar\"].evaluate()  # \u003cclass '__main__.Bar'\u003e\n```\nIt supports generic types, metadata, function calls/class instantiation, etc.\n```python\nclass Foo:\n    bar: \"list[int]\"\n    baz: \"Callable[[int], str]\"\n    qux: \"Annotated[int, Bar('baz')]\"\n```\n\n## As Completed\n\nThe `AsCompleted` type is a wrapper around `asyncio.as_completed` that adds an async iterator over the results from each task. This simplifies iterating over tasks, eliminating the need to await the next result.\n\n```py\nfrom tramp.as_completed import AsCompleted\n...\ntasks = [...]\nasync for result in AsCompleted(*tasks):\n    ...\n```\n\nAdditionally it is possible to use `AsCompleted` in the same way that `as_completed` operates.\n\n```py\nfor next_result in AsCompleted(*tasks):\n    result = await next_result\n```\n\n## Async Batch Iterators\n\nThe `AsyncBatchIterator` type is an async iterator that yields results one at a time from batches. It takes a coroutine that returns batches at a batch index. The coroutine can return either a `Iterable` or an `AsyncIterable`. If the coroutine returns `None` or an empty batch, the batched iterator stops.\n\n```py\nasync def get_batch(batch_index: int) -\u003e Iterable[int] | None:\n    if batch_index \u003e 1:\n        return\n    \n    return range(batch_index * 2, (batch_index + 1) * 2)\n    \nasync def main():\n    async for result in AsyncBatchIterator(get_batch):\n        print(result)\n```\n\n## Containers\n\nA container acts a reference to a changeable value.\n\n```python\nfrom tramp.containers import Container\n\ncontainer = Container[int](0)\ncontainer.set(1)\n\nprint(container.value)  # 1\n```\n\nAn empty container can also be created. Attempting to access the value raises a `ValueError`. The error can be avoided by using the `value_or` method or by checking the `never_set` boolean property.\n\n## Modules\n\nHelper functions for working with modules\n\n```python\nfrom tramp import modules\nfrom typing import Any\n\nns: dict[str, Any] = modules.get_module_namespace(\"some_module\")\n```\n\n## Optionals\n\nAn optional type that can be used with match statements.\n\n```python\nfrom tramp.optionals import Optional\n\ndef foo(x: int) -\u003e Optional[int]:\n    if x \u003e 0:\n        return Optional.Some(x)\n        \n    return Optional.Nothing()\n\nresult = foo(1)\nprint(result.value) # 1\n\nresult = foo(-1)\nprint(result.value) # Raises an exception\n\nresult = foo(-1)\nprint(result.value_or(0)) # 0\n\n...\n\nmatch foo(1):\n    case Optional.Some(x):\n        print(x)\n\n    case Optional.Nothing():\n        print(\"Nothing\")\n\n# Output: 1\n\nmatch foo(-1):\n    case Optional.Some(x):\n        print(x)\n\n    case Optional.Nothing():\n        print(\"Nothing\")\n\n# Output: Nothing\n```\n\n## Protected Strings\n\nA protected string type that can be used to store sensitive information. The string is redacted when rendered into a string. The value can be accessed using the `value` property.\n\n```python\nfrom tramp.protected_strings import ProtectedString\n\npassword = ProtectedString(\"password\", name=\"password\")\nprint(password)  # \u003cRedacted\u003e\n\nprint(f\"Password: {password}\")  # Password: \u003cRedacted\u003e\nprint(f\"Password: {password.value}\")  # Password: password\nprint(f\"Password: {password:***}\")  # Password: ***\nprint(f\"Password: {password:***$password}\")  # Password: password\nprint(f\"Password: {password:$password}\")  # Password: password\n```\n\n`ProtectedString`s can be combined with other strings to create a `ProtectedStringBuilder` which can combine and format multiple protected strings with each other and normal strings.\n\n```python\nfrom tramp.protected_strings import ProtectedString\n\nfoo = ProtectedString(\"Hello\", name=\"foo\")\nbar = ProtectedString(\"World\", name=\"bar\")\nbuilder = foo + \" \" + bar + \"!!!\"\nprint(f\"{builder}\")  # \u003cRedacted Foo\u003e \u003cRedacted Bar\u003e!!!\nprint(f\"{builder:***}\")  # *** ***!!!\nprint(f\"{builder:$foo}\")  # Hello \u003cRedacted Bar\u003e!!!\nprint(f\"{builder:***$foo}\")  # Hello ***!!!\nprint(f\"{builder:$foo,bar}\")  # Hello World!!!\nprint(f\"{builder:***$foo,bar}\")  # Hello World!!!\n```\n\n## Results\n\nA result type that can be used with match statements. Works the same as Optionals with an added `error` property.\n\n```python\nfrom tramp.results import Result\n\nwith Result.build() as result:\n    result.value = 1\n\nprint(result.value) # 1\nprint(result.error) # None\n\nwith Result.build() as result:\n    raise Execption(\"Error\")\n\nprint(result.value) # Raises an exception\nprint(result.value_or(0)) # 0\nprint(result.error) # Exception(\"Error\")\n```\n\n## Sentinel\n\nA sentinel value that can be used to represent a unique value. Useful for creating `NotSet` types. Instantiating any\nsentinel type will always return the same singleton instance of that type allowing for `is` checks.\n\n```python\nfrom tramp.sentinels import sentinel\n\nNotSet = sentinel(\"NotSet\")\n\n\ndef foo(x: int | NotSet = NotSet()) -\u003e int:\n    if x is NotSet():\n        return 0\n\n    return x\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzechcodes%2Ftramp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzechcodes%2Ftramp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzechcodes%2Ftramp/lists"}