tuttifrutti 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,22 @@
1
+ Metadata-Version: 2.3
2
+ Name: tuttifrutti
3
+ Version: 0.1.0
4
+ Summary: A collection of tiny, reusable Python utilities.
5
+ Author: Kunal Goel
6
+ Author-email: Kunal Goel <hexcodex101@gmail.com>
7
+ Classifier: Development Status :: 4 - Beta
8
+ Classifier: Intended Audience :: Developers
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
13
+ Classifier: Typing :: Typed
14
+ Maintainer: Kunal Goel
15
+ Maintainer-email: Kunal Goel <hexcodex101@gmail.com>
16
+ Requires-Python: >=3.12
17
+ Project-URL: Homepage, https://github.com/kgcodex/tuttifrutti
18
+ Project-URL: Documentation, https://github.com/kgcodex/tuttifrutti#readme
19
+ Project-URL: Repository, https://github.com/kgcodex/tuttifrutti
20
+ Project-URL: Issues, https://github.com/kgcodex/tuttifrutti/issues
21
+ Description-Content-Type: text/markdown
22
+
File without changes
@@ -0,0 +1,86 @@
1
+ [project]
2
+ name = "tuttifrutti"
3
+ version = "0.1.0"
4
+ description = "A collection of tiny, reusable Python utilities."
5
+ readme = "README.md"
6
+ authors = [
7
+ { name = "Kunal Goel", email = "hexcodex101@gmail.com" }
8
+ ]
9
+ maintainers = [
10
+ { name = "Kunal Goel", email = "hexcodex101@gmail.com" }
11
+ ]
12
+ requires-python = ">=3.12"
13
+ dependencies = []
14
+ classifiers = [
15
+ "Development Status :: 4 - Beta",
16
+ "Intended Audience :: Developers",
17
+ "License :: OSI Approved :: MIT License",
18
+ "Programming Language :: Python :: 3",
19
+ "Programming Language :: Python :: 3.12",
20
+ "Topic :: Software Development :: Libraries :: Python Modules",
21
+ "Typing :: Typed",
22
+ ]
23
+
24
+ [project.urls]
25
+ Homepage = "https://github.com/kgcodex/tuttifrutti"
26
+ Documentation = "https://github.com/kgcodex/tuttifrutti#readme"
27
+ Repository = "https://github.com/kgcodex/tuttifrutti"
28
+ Issues = "https://github.com/kgcodex/tuttifrutti/issues"
29
+
30
+ [build-system]
31
+ requires = ["uv_build>=0.11.8,<0.12.0"]
32
+ build-backend = "uv_build"
33
+
34
+ [dependency-groups]
35
+ dev = [
36
+ "mypy>=2.1.0",
37
+ "pre-commit>=4.6.0",
38
+ "pytest>=9.1.1",
39
+ "ruff>=0.15.20",
40
+ ]
41
+
42
+
43
+ [tool.mypy]
44
+ python_version = "3.12"
45
+ strict = true
46
+ warn_return_any = true
47
+ warn_unused_configs = true
48
+ disallow_untyped_defs = true
49
+ disallow_any_generics = true
50
+ check_untyped_defs = true
51
+
52
+ mypy_path = "src"
53
+ explicit_package_bases = true
54
+
55
+
56
+ [[tool.mypy.overrides]]
57
+ module = "pytest.*"
58
+ ignore_missing_imports = true
59
+
60
+
61
+ [tool.ruff]
62
+ target-version = "py312"
63
+ line-length = 88
64
+
65
+ [tool.ruff.lint]
66
+ select = [
67
+ "E",
68
+ "W",
69
+ "F",
70
+ "I",
71
+ "UP",
72
+ "B",
73
+ "SIM",
74
+ "N",
75
+ "ASYNC",
76
+ "PTH",
77
+ ]
78
+ # ignore = ["E501"]
79
+
80
+ [tool.ruff.lint.isort]
81
+ combine-as-imports = true
82
+ force-single-line = false
83
+
84
+ [tool.ruff.format]
85
+ quote-style = "double"
86
+ indent-style = "space"
@@ -0,0 +1,17 @@
1
+ from importlib import import_module
2
+ from importlib.metadata import version
3
+ from typing import TYPE_CHECKING, Any
4
+
5
+ __version__ = version("tuttifrutti")
6
+
7
+ if TYPE_CHECKING:
8
+ from ._optional_chain import OptionalChain
9
+
10
+ __all__ = ["OptionalChain"]
11
+
12
+
13
+ def __getattr__(name: str) -> Any:
14
+ if name == "OptionalChain":
15
+ return import_module("._optional_chain", __name__).OptionalChain
16
+
17
+ raise AttributeError(f"module '{__name__}' has not attribute {name}")
@@ -0,0 +1,44 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Callable
4
+ from typing import Any
5
+
6
+
7
+ class OptionalChain[T]:
8
+ def __init__(self, obj: T | None) -> None:
9
+ self._obj = obj
10
+
11
+ def __repr__(self) -> str:
12
+ return (
13
+ f"OptionalChain("
14
+ f"{self._obj.__class__.__name__ if self._obj is not None else None}"
15
+ f")"
16
+ )
17
+
18
+ def __getattr__(self, attr: str) -> OptionalChain[Any]:
19
+ if self._obj is None:
20
+ return OptionalChain(None)
21
+
22
+ if isinstance(self._obj, dict):
23
+ return OptionalChain(self._obj.get(attr, None))
24
+
25
+ return OptionalChain(getattr(self._obj, attr, None))
26
+
27
+ def get(self) -> T | None:
28
+ return self._obj
29
+
30
+ def __getitem__(self, key: Any) -> OptionalChain[Any]:
31
+ if self._obj is None:
32
+ return OptionalChain(None)
33
+ try:
34
+ return OptionalChain(self._obj[key]) # type:ignore[index]
35
+ except (KeyError, IndexError, TypeError):
36
+ return OptionalChain(None)
37
+
38
+ def or_else[K](self, default: K) -> T | K:
39
+ return self._obj if self._obj is not None else default
40
+
41
+ def map[R](self, func: Callable[[T], R]) -> OptionalChain[R]:
42
+ if self._obj is None:
43
+ return OptionalChain(None)
44
+ return OptionalChain(func(self._obj))
File without changes