funcy-bear 0.0.2__py3-none-any.whl
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.
- funcy_bear/__init__.py +7 -0
- funcy_bear/__main__.py +14 -0
- funcy_bear/_internal/__init__.py +0 -0
- funcy_bear/_internal/_info.py +169 -0
- funcy_bear/_internal/_version.py +3 -0
- funcy_bear/_internal/_version.pyi +6 -0
- funcy_bear/_internal/_versioning.py +111 -0
- funcy_bear/_internal/cli.py +66 -0
- funcy_bear/_internal/debug.py +105 -0
- funcy_bear/api.py +70 -0
- funcy_bear/constants/__init__.py +69 -0
- funcy_bear/constants/binary_types.py +258 -0
- funcy_bear/constants/exceptions.py +90 -0
- funcy_bear/constants/exit_code.py +30 -0
- funcy_bear/constants/file_size.py +67 -0
- funcy_bear/context/__init__.py +14 -0
- funcy_bear/context/arg_helpers.py +122 -0
- funcy_bear/context/di/README.md +127 -0
- funcy_bear/context/di/__init__.py +26 -0
- funcy_bear/context/di/_container_meta.py +115 -0
- funcy_bear/context/di/_param.py +98 -0
- funcy_bear/context/di/container.py +90 -0
- funcy_bear/context/di/container_attrs.py +65 -0
- funcy_bear/context/di/plugin_containers.py +147 -0
- funcy_bear/context/di/plugins.py +132 -0
- funcy_bear/context/di/provides.py +118 -0
- funcy_bear/context/di/provides.pyi +38 -0
- funcy_bear/context/di/resources.py +243 -0
- funcy_bear/context/di/types.py +30 -0
- funcy_bear/context/di/wiring.py +136 -0
- funcy_bear/exceptions.py +27 -0
- funcy_bear/ops/__init__.py +1 -0
- funcy_bear/ops/_di_containers.py +22 -0
- funcy_bear/ops/binarystuffs.py +189 -0
- funcy_bear/ops/collections_ops/__init__.py +1 -0
- funcy_bear/ops/collections_ops/dict_stuffs.py +128 -0
- funcy_bear/ops/collections_ops/iter_stuffs.py +549 -0
- funcy_bear/ops/collections_ops/key_counts.py +95 -0
- funcy_bear/ops/curried_ops.py +520 -0
- funcy_bear/ops/dispatch.py +109 -0
- funcy_bear/ops/func_stuffs.py +298 -0
- funcy_bear/ops/math/README.md +74 -0
- funcy_bear/ops/math/__init__.py +30 -0
- funcy_bear/ops/math/general.py +168 -0
- funcy_bear/ops/math/infinity.py +123 -0
- funcy_bear/ops/strings/README.md +100 -0
- funcy_bear/ops/strings/__init__.py +1 -0
- funcy_bear/ops/strings/dot_template.py +172 -0
- funcy_bear/ops/strings/flatten_data.py +110 -0
- funcy_bear/ops/strings/manipulation.py +337 -0
- funcy_bear/ops/strings/sorting_name.py +206 -0
- funcy_bear/ops/strings/string_stuffs.py +29 -0
- funcy_bear/ops/value_stuffs.py +79 -0
- funcy_bear/py.typed +0 -0
- funcy_bear/randoms/README.md +63 -0
- funcy_bear/randoms/__init__.py +17 -0
- funcy_bear/randoms/_rnd.py +269 -0
- funcy_bear/randoms/dice.py +317 -0
- funcy_bear/randoms/random_bits.py +22 -0
- funcy_bear/sentinels.py +168 -0
- funcy_bear/system_bools.py +100 -0
- funcy_bear/tools/__init__.py +10 -0
- funcy_bear/tools/constant.py +122 -0
- funcy_bear/tools/currying.py +52 -0
- funcy_bear/tools/dispatcher.py +112 -0
- funcy_bear/tools/freezing.py +91 -0
- funcy_bear/tools/freezing.pyi +38 -0
- funcy_bear/tools/general_base.py +186 -0
- funcy_bear/tools/list_merger.py +83 -0
- funcy_bear/tools/lru_cache.py +94 -0
- funcy_bear/tools/names.py +222 -0
- funcy_bear/tools/priority_queue.py +113 -0
- funcy_bear/tools/simple_queue.py +48 -0
- funcy_bear/tools/simple_stack.py +28 -0
- funcy_bear/type_stuffs/__init__.py +142 -0
- funcy_bear/type_stuffs/builtin_tools.py +49 -0
- funcy_bear/type_stuffs/constants.py +120 -0
- funcy_bear/type_stuffs/conversions/__init__.py +24 -0
- funcy_bear/type_stuffs/conversions/str_to_bool.py +21 -0
- funcy_bear/type_stuffs/conversions/string_eval.py +68 -0
- funcy_bear/type_stuffs/conversions/to_type.py +154 -0
- funcy_bear/type_stuffs/conversions/type_to_string.py +70 -0
- funcy_bear/type_stuffs/hint.py +28 -0
- funcy_bear/type_stuffs/inference/__init__.py +5 -0
- funcy_bear/type_stuffs/inference/runtime.py +245 -0
- funcy_bear/type_stuffs/introspection/__init__.py +21 -0
- funcy_bear/type_stuffs/introspection/_helpers.py +155 -0
- funcy_bear/type_stuffs/introspection/general.py +177 -0
- funcy_bear/type_stuffs/validate.py +73 -0
- funcy_bear/type_stuffs/validators/__init__.py +1 -0
- funcy_bear/type_stuffs/validators/annotations.py +46 -0
- funcy_bear/type_stuffs/validators/helpers.py +107 -0
- funcy_bear/type_stuffs/validators/predicates.py +319 -0
- funcy_bear-0.0.2.dist-info/METADATA +30 -0
- funcy_bear-0.0.2.dist-info/RECORD +97 -0
- funcy_bear-0.0.2.dist-info/WHEEL +4 -0
- funcy_bear-0.0.2.dist-info/entry_points.txt +2 -0
funcy_bear/__init__.py
ADDED
funcy_bear/__main__.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""Entry-point module, in case you use `python -m funcy_bear`.
|
|
2
|
+
|
|
3
|
+
Why does this file exist, and why `__main__`? For more info, read:
|
|
4
|
+
|
|
5
|
+
- https://www.python.org/dev/peps/pep-0338/
|
|
6
|
+
- https://docs.python.org/3/using/cmdline.html#cmdoption-m
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import sys
|
|
10
|
+
|
|
11
|
+
from funcy_bear._internal.cli import main
|
|
12
|
+
|
|
13
|
+
if __name__ == "__main__":
|
|
14
|
+
sys.exit(main(sys.argv[1:]))
|
|
File without changes
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
from importlib.metadata import PackageNotFoundError, distribution, version
|
|
3
|
+
from typing import Literal
|
|
4
|
+
|
|
5
|
+
from funcy_bear._internal._version import __commit_id__, __version__, __version_tuple__
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass(slots=True)
|
|
9
|
+
class _Package:
|
|
10
|
+
"""Dataclass to store package information."""
|
|
11
|
+
|
|
12
|
+
name: str
|
|
13
|
+
"""Package name."""
|
|
14
|
+
version: str = "0.0.0"
|
|
15
|
+
"""Package version."""
|
|
16
|
+
description: str = "No description available."
|
|
17
|
+
"""Package description."""
|
|
18
|
+
|
|
19
|
+
def __str__(self) -> str:
|
|
20
|
+
"""String representation of the package information."""
|
|
21
|
+
return f"{self.name} v{self.version}: {self.description}"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _get_package_info(dist: str) -> _Package:
|
|
25
|
+
"""Get package information for the given distribution.
|
|
26
|
+
|
|
27
|
+
Parameters:
|
|
28
|
+
dist: A distribution name.
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
Package information with version, name, and description.
|
|
32
|
+
"""
|
|
33
|
+
return _Package(name=dist, version=_get_version(dist), description=_get_description(dist))
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _get_version(dist: str = "funcy-bear") -> str:
|
|
37
|
+
"""Get version of the given distribution or the current package.
|
|
38
|
+
|
|
39
|
+
Parameters:
|
|
40
|
+
dist: A distribution name.
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
A version number.
|
|
44
|
+
"""
|
|
45
|
+
try:
|
|
46
|
+
return version(dist)
|
|
47
|
+
except PackageNotFoundError:
|
|
48
|
+
return "0.0.0"
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _get_description(dist: str = "funcy-bear") -> str:
|
|
52
|
+
"""Get description of the given distribution or the current package.
|
|
53
|
+
|
|
54
|
+
Parameters:
|
|
55
|
+
dist: A distribution name.
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
A description string.
|
|
59
|
+
"""
|
|
60
|
+
try:
|
|
61
|
+
return distribution(dist).metadata.get("summary", "No description available.")
|
|
62
|
+
except PackageNotFoundError:
|
|
63
|
+
return "No description available."
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
# fmt: off
|
|
67
|
+
@dataclass(slots=True, frozen=True)
|
|
68
|
+
class _ProjectName:
|
|
69
|
+
"""A class to represent the project name and its metadata as literals for type safety.
|
|
70
|
+
|
|
71
|
+
This is done this way to make it easier to see the values in the IDE and to ensure that the values are consistent throughout the codebase.
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
package_distribution: Literal["funcy-bear"] = "funcy-bear"
|
|
75
|
+
project: Literal["funcy_bear"] = "funcy_bear"
|
|
76
|
+
project_upper: Literal["FUNCY_BEAR"] = "FUNCY_BEAR"
|
|
77
|
+
env_variable: Literal["FUNCY_BEAR_ENV"] = ("FUNCY_BEAR_ENV")
|
|
78
|
+
# fmt: on
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@dataclass(slots=True, frozen=True)
|
|
82
|
+
class _ProjectVersion:
|
|
83
|
+
"""A class to represent the project version."""
|
|
84
|
+
|
|
85
|
+
string: str
|
|
86
|
+
ver_tuple: tuple[int, int, int] = field(default=__version_tuple__)
|
|
87
|
+
commit_id: str = field(default=__commit_id__)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
@dataclass(slots=True, frozen=True)
|
|
91
|
+
class _ModulePaths:
|
|
92
|
+
"""A class to hold the module import paths, mostly for the CLI."""
|
|
93
|
+
|
|
94
|
+
_internal: str = "funcy_bear._internal"
|
|
95
|
+
_commands: str = f"{_internal}._cmds"
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
@dataclass(slots=True)
|
|
99
|
+
class _ProjectMetadata:
|
|
100
|
+
"""Dataclass to store the current project metadata."""
|
|
101
|
+
|
|
102
|
+
_version: _ProjectVersion
|
|
103
|
+
_name: _ProjectName = field(default_factory=_ProjectName)
|
|
104
|
+
_paths: _ModulePaths = field(default_factory=_ModulePaths)
|
|
105
|
+
|
|
106
|
+
def __str__(self) -> str:
|
|
107
|
+
"""String representation of the project metadata."""
|
|
108
|
+
return f"{self.full_version}: {self.description}"
|
|
109
|
+
|
|
110
|
+
@property
|
|
111
|
+
def cmds(self) -> str:
|
|
112
|
+
"""Get the commands module path."""
|
|
113
|
+
return self._paths._commands
|
|
114
|
+
|
|
115
|
+
@property
|
|
116
|
+
def version(self) -> str:
|
|
117
|
+
"""Get the project version as a string."""
|
|
118
|
+
return self._version.string
|
|
119
|
+
|
|
120
|
+
@property
|
|
121
|
+
def version_tuple(self) -> tuple[int, int, int]:
|
|
122
|
+
"""Get the project version as a tuple."""
|
|
123
|
+
return self._version.ver_tuple
|
|
124
|
+
|
|
125
|
+
@property
|
|
126
|
+
def commit_id(self) -> str:
|
|
127
|
+
"""Get the Git commit ID of the current version."""
|
|
128
|
+
return self._version.commit_id
|
|
129
|
+
|
|
130
|
+
@property
|
|
131
|
+
def full_version(self) -> str:
|
|
132
|
+
"""Get the full version string."""
|
|
133
|
+
return f"{self.name} v{self._version.string}"
|
|
134
|
+
|
|
135
|
+
@property
|
|
136
|
+
def description(self) -> str:
|
|
137
|
+
"""Get the project description from the distribution metadata."""
|
|
138
|
+
return _get_description(self.name)
|
|
139
|
+
|
|
140
|
+
@property
|
|
141
|
+
def name(self) -> Literal["funcy-bear"]:
|
|
142
|
+
"""Get the package distribution name."""
|
|
143
|
+
return self._name.package_distribution
|
|
144
|
+
|
|
145
|
+
@property
|
|
146
|
+
def name_upper(self) -> Literal["FUNCY_BEAR"]:
|
|
147
|
+
"""Get the project name in uppercase with underscores."""
|
|
148
|
+
return self._name.project_upper
|
|
149
|
+
|
|
150
|
+
@property
|
|
151
|
+
def project_name(self) -> Literal["funcy_bear"]:
|
|
152
|
+
"""Get the project name."""
|
|
153
|
+
return self._name.project
|
|
154
|
+
|
|
155
|
+
@property
|
|
156
|
+
def env_variable(self) -> Literal["FUNCY_BEAR_ENV"]:
|
|
157
|
+
"""Get the environment variable name for the project.
|
|
158
|
+
|
|
159
|
+
Used to check if the project is running in a specific environment.
|
|
160
|
+
"""
|
|
161
|
+
return self._name.env_variable
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
_version = _ProjectVersion(string=__version__ if __version__ != "0.0.0" else _get_version())
|
|
165
|
+
|
|
166
|
+
METADATA = _ProjectMetadata(_version=_version)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
__all__ = ["METADATA", "_Package"]
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from enum import IntEnum
|
|
5
|
+
from typing import Literal
|
|
6
|
+
|
|
7
|
+
from funcy_bear.constants.exit_code import ExitCode
|
|
8
|
+
|
|
9
|
+
type BumpType = Literal["major", "minor", "patch"]
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class VersionParts(IntEnum): # pragma: no cover
|
|
13
|
+
"""Enumeration for version parts."""
|
|
14
|
+
|
|
15
|
+
MAJOR = 0
|
|
16
|
+
MINOR = 1
|
|
17
|
+
PATCH = 2
|
|
18
|
+
|
|
19
|
+
@classmethod
|
|
20
|
+
def choices(cls) -> list[str]:
|
|
21
|
+
"""Return a list of valid version parts."""
|
|
22
|
+
return [part.name.lower() for part in cls]
|
|
23
|
+
|
|
24
|
+
@classmethod
|
|
25
|
+
def parts(cls) -> int:
|
|
26
|
+
"""Return the total number of version parts."""
|
|
27
|
+
return len(cls.choices())
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass(slots=True)
|
|
31
|
+
class Version: # pragma: no cover
|
|
32
|
+
"""Model to represent a version string."""
|
|
33
|
+
|
|
34
|
+
major: int = field(default=0)
|
|
35
|
+
minor: int = field(default=0)
|
|
36
|
+
patch: int = field(default=0)
|
|
37
|
+
post: str | None = field(default=None)
|
|
38
|
+
|
|
39
|
+
def increment(self, attr_name: str) -> None:
|
|
40
|
+
"""Increment the specified part of the version."""
|
|
41
|
+
if hasattr(self, attr_name.lower()):
|
|
42
|
+
current_value: int = getattr(self, attr_name.lower())
|
|
43
|
+
setattr(self, attr_name.lower(), current_value + 1)
|
|
44
|
+
|
|
45
|
+
def default(self, part: str) -> None:
|
|
46
|
+
"""Clear the specified part of the version.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
part: The part of the version to clear.
|
|
50
|
+
"""
|
|
51
|
+
if hasattr(self, part):
|
|
52
|
+
setattr(self, part, 0)
|
|
53
|
+
|
|
54
|
+
def new_version(self, bump_type: str) -> Version:
|
|
55
|
+
"""Return a new version string based on the bump type."""
|
|
56
|
+
bump_part: VersionParts = VersionParts[bump_type.upper()]
|
|
57
|
+
self.increment(bump_part.name)
|
|
58
|
+
for part in VersionParts:
|
|
59
|
+
if part.value > bump_part.value:
|
|
60
|
+
self.default(part.name)
|
|
61
|
+
return self
|
|
62
|
+
|
|
63
|
+
def __repr__(self) -> str:
|
|
64
|
+
"""Return a string representation of the Version instance."""
|
|
65
|
+
return (
|
|
66
|
+
f"{self.major}.{self.minor}.{self.patch}.{self.post}"
|
|
67
|
+
if self.post
|
|
68
|
+
else f"{self.major}.{self.minor}.{self.patch}"
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
def __str__(self) -> str:
|
|
72
|
+
"""Return a string representation of the Version instance."""
|
|
73
|
+
return self.__repr__()
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
VALID_BUMP_TYPES: list[str] = VersionParts.choices() # pragma: no cover
|
|
77
|
+
ALL_PARTS: int = VersionParts.parts() # pragma: no cover
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def cli_bump(b: BumpType, v: str | tuple[int, int, int]) -> ExitCode: # pragma: no cover
|
|
81
|
+
"""Bump the version of the current package.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
b: The type of bump ("major", "minor", or "patch").
|
|
85
|
+
p: The name of the package.
|
|
86
|
+
v: The current version string or tuple of version parts.
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
An ExitCode indicating success or failure.
|
|
90
|
+
"""
|
|
91
|
+
if b not in VALID_BUMP_TYPES:
|
|
92
|
+
print(f"Invalid argument '{b}'. Use one of: {', '.join(VALID_BUMP_TYPES)}.")
|
|
93
|
+
return ExitCode.FAILURE
|
|
94
|
+
if not isinstance(v, tuple):
|
|
95
|
+
raise TypeError("Version must be a tuple of integers.")
|
|
96
|
+
try:
|
|
97
|
+
parts: list[int] = list(v)
|
|
98
|
+
version: Version = Version(
|
|
99
|
+
major=parts[0],
|
|
100
|
+
minor=parts[1] if ALL_PARTS > 1 else 0,
|
|
101
|
+
patch=parts[2] if ALL_PARTS > 2 else 0, # noqa: PLR2004
|
|
102
|
+
)
|
|
103
|
+
new_version: Version = version.new_version(b)
|
|
104
|
+
print(str(new_version))
|
|
105
|
+
return ExitCode.SUCCESS
|
|
106
|
+
except ValueError:
|
|
107
|
+
print(f"Invalid version tuple: {v}")
|
|
108
|
+
return ExitCode.FAILURE
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
__all__ = ["Version", "VersionParts"]
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from argparse import ArgumentParser, Namespace
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
from funcy_bear._internal._versioning import BumpType, ExitCode, cli_bump
|
|
7
|
+
from funcy_bear._internal.debug import METADATA, _print_debug_info
|
|
8
|
+
from funcy_bear.context.arg_helpers import args_inject
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _debug_info() -> ExitCode: # pragma: no cover
|
|
12
|
+
"""CLI command to print debug information."""
|
|
13
|
+
_print_debug_info()
|
|
14
|
+
return ExitCode.SUCCESS
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _bump(bump_type: BumpType) -> ExitCode: # pragma: no cover
|
|
18
|
+
"""CLI command to bump the version of the package."""
|
|
19
|
+
return cli_bump(bump_type, METADATA.version_tuple)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _version(name: bool = False) -> ExitCode: # pragma: no cover
|
|
23
|
+
"""CLI command to get the current version of the package."""
|
|
24
|
+
print(f"{METADATA.name} {METADATA.version}" if name else METADATA.version)
|
|
25
|
+
return ExitCode.SUCCESS
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _to_args(args: list[str]) -> Namespace: # pragma: no cover
|
|
29
|
+
"""Convert a list of arguments into a Namespace object."""
|
|
30
|
+
parser = ArgumentParser(prog=METADATA.name, description="Lazy Bear CLI")
|
|
31
|
+
subparsers = parser.add_subparsers(dest="command", required=True)
|
|
32
|
+
subparsers.add_parser("debug-info", help="Print debug information")
|
|
33
|
+
version_parser: ArgumentParser = subparsers.add_parser("version", help="Get the current version")
|
|
34
|
+
version_parser.add_argument("--name", action="store_true", help="Include the package name in the output")
|
|
35
|
+
bump_parser: ArgumentParser = subparsers.add_parser("bump", help="Bump the version of the package")
|
|
36
|
+
bump_parser.add_argument("bump_type", choices=["major", "minor", "patch"], help="Type of version bump")
|
|
37
|
+
return parser.parse_args(args)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@args_inject(process=_to_args)
|
|
41
|
+
def main(args: Namespace) -> ExitCode:
|
|
42
|
+
"""Entry point for the CLI application.
|
|
43
|
+
|
|
44
|
+
This function is executed when you type `lazy_bear` or `python -m lazy_bear`.
|
|
45
|
+
|
|
46
|
+
Parameters:
|
|
47
|
+
args: Arguments passed from the command line.
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
An exit code.
|
|
51
|
+
"""
|
|
52
|
+
command: str = args.command
|
|
53
|
+
match command:
|
|
54
|
+
case "debug-info":
|
|
55
|
+
return _debug_info()
|
|
56
|
+
case "version":
|
|
57
|
+
return _version(name=args.name)
|
|
58
|
+
case "bump":
|
|
59
|
+
return _bump(bump_type=args.bump_type)
|
|
60
|
+
case _: # pragma: no cover
|
|
61
|
+
print(f"Unknown command: {command}", file=sys.stderr)
|
|
62
|
+
return ExitCode.FAILURE
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
if __name__ == "__main__":
|
|
66
|
+
sys.exit(main(sys.argv[1:]))
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from importlib.metadata import distributions
|
|
5
|
+
from os import environ, getenv
|
|
6
|
+
import platform
|
|
7
|
+
import sys
|
|
8
|
+
|
|
9
|
+
from funcy_bear._internal._info import METADATA, _get_package_info, _Package
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class _Variable:
|
|
14
|
+
"""Dataclass describing an environment variable."""
|
|
15
|
+
|
|
16
|
+
name: str
|
|
17
|
+
"""Variable name."""
|
|
18
|
+
value: str
|
|
19
|
+
"""Variable value."""
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass
|
|
23
|
+
class _Environment:
|
|
24
|
+
"""Dataclass to store environment information."""
|
|
25
|
+
|
|
26
|
+
interpreter_name: str
|
|
27
|
+
"""Python interpreter name."""
|
|
28
|
+
interpreter_version: str
|
|
29
|
+
"""Python interpreter version."""
|
|
30
|
+
interpreter_path: str
|
|
31
|
+
"""Path to Python executable."""
|
|
32
|
+
platform: str
|
|
33
|
+
"""Operating System."""
|
|
34
|
+
packages: list[_Package]
|
|
35
|
+
"""Installed packages."""
|
|
36
|
+
variables: list[_Variable]
|
|
37
|
+
"""Environment variables."""
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _interpreter_name_version() -> tuple[str, str]:
|
|
41
|
+
if hasattr(sys, "implementation"):
|
|
42
|
+
impl: sys._version_info = sys.implementation.version
|
|
43
|
+
version: str = f"{impl.major}.{impl.minor}.{impl.micro}"
|
|
44
|
+
kind = impl.releaselevel
|
|
45
|
+
if kind != "final":
|
|
46
|
+
version += kind[0] + str(impl.serial)
|
|
47
|
+
return sys.implementation.name, version
|
|
48
|
+
return "", "0.0.0"
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _get_debug_info() -> _Environment:
|
|
52
|
+
"""Get debug/environment information.
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
Environment information.
|
|
56
|
+
"""
|
|
57
|
+
py_name, py_version = _interpreter_name_version()
|
|
58
|
+
environ[f"{METADATA.name_upper}_DEBUG"] = "1"
|
|
59
|
+
variables: list[str] = ["PYTHONPATH", *[var for var in environ if var.startswith(METADATA.name_upper)]]
|
|
60
|
+
return _Environment(
|
|
61
|
+
interpreter_name=py_name,
|
|
62
|
+
interpreter_version=py_version,
|
|
63
|
+
interpreter_path=sys.executable,
|
|
64
|
+
platform=platform.platform(),
|
|
65
|
+
variables=[_Variable(var, val) for var in variables if (val := getenv(var))],
|
|
66
|
+
packages=_get_installed_packages(),
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _get_installed_packages() -> list[_Package]:
|
|
71
|
+
"""Get all installed packages in current environment"""
|
|
72
|
+
packages: list[_Package] = []
|
|
73
|
+
for dist in distributions():
|
|
74
|
+
packages.append(_get_package_info(dist.metadata["Name"]))
|
|
75
|
+
return packages
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _print_debug_info() -> None:
|
|
79
|
+
"""Print debug/environment information with minimal clean formatting."""
|
|
80
|
+
info: _Environment = _get_debug_info()
|
|
81
|
+
sections: list[tuple[str, list[tuple[str, str]]]] = [
|
|
82
|
+
(
|
|
83
|
+
"SYSTEM",
|
|
84
|
+
[
|
|
85
|
+
("Platform", info.platform),
|
|
86
|
+
("Python", f"{info.interpreter_name} {info.interpreter_version}"),
|
|
87
|
+
("Location", info.interpreter_path),
|
|
88
|
+
],
|
|
89
|
+
),
|
|
90
|
+
("ENVIRONMENT", [(var.name, var.value) for var in info.variables]),
|
|
91
|
+
("PACKAGES", [(pkg.name, f"v{pkg.version}") for pkg in info.packages]),
|
|
92
|
+
]
|
|
93
|
+
|
|
94
|
+
for i, (section_name, items) in enumerate(sections):
|
|
95
|
+
if items:
|
|
96
|
+
print(f"{section_name}")
|
|
97
|
+
for key, value in items:
|
|
98
|
+
print(key, end=": ")
|
|
99
|
+
print(value)
|
|
100
|
+
if i != len(sections) - 1:
|
|
101
|
+
print()
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
if __name__ == "__main__":
|
|
105
|
+
_print_debug_info()
|
funcy_bear/api.py
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""Funcy Bear: A collection of functional programming utilities."""
|
|
2
|
+
|
|
3
|
+
from funcy_bear.ops.collections_ops.dict_stuffs import key_counts, merge as merge_dicts
|
|
4
|
+
from funcy_bear.ops.collections_ops.iter_stuffs import merge, merge_lists, merge_sets, merge_tuples, pairwise, window
|
|
5
|
+
from funcy_bear.ops.curried_ops import (
|
|
6
|
+
abs, # noqa: A004
|
|
7
|
+
add,
|
|
8
|
+
append,
|
|
9
|
+
clamp,
|
|
10
|
+
decrement,
|
|
11
|
+
default,
|
|
12
|
+
delete,
|
|
13
|
+
div,
|
|
14
|
+
extend,
|
|
15
|
+
format, # noqa: A004
|
|
16
|
+
if_else,
|
|
17
|
+
increment,
|
|
18
|
+
lower,
|
|
19
|
+
mod,
|
|
20
|
+
multiply,
|
|
21
|
+
pop,
|
|
22
|
+
pow, # noqa: A004
|
|
23
|
+
prepend,
|
|
24
|
+
push,
|
|
25
|
+
replace,
|
|
26
|
+
setter,
|
|
27
|
+
subtract,
|
|
28
|
+
toggle,
|
|
29
|
+
upper,
|
|
30
|
+
)
|
|
31
|
+
from funcy_bear.ops.func_stuffs import if_in_list
|
|
32
|
+
from funcy_bear.type_stuffs.constants import LitFalse, LitTrue
|
|
33
|
+
|
|
34
|
+
__all__ = [
|
|
35
|
+
"LitFalse",
|
|
36
|
+
"LitTrue",
|
|
37
|
+
"abs",
|
|
38
|
+
"add",
|
|
39
|
+
"append",
|
|
40
|
+
"clamp",
|
|
41
|
+
"decrement",
|
|
42
|
+
"default",
|
|
43
|
+
"delete",
|
|
44
|
+
"div",
|
|
45
|
+
"extend",
|
|
46
|
+
"format",
|
|
47
|
+
"if_else",
|
|
48
|
+
"if_in_list",
|
|
49
|
+
"increment",
|
|
50
|
+
"key_counts",
|
|
51
|
+
"lower",
|
|
52
|
+
"merge",
|
|
53
|
+
"merge_dicts",
|
|
54
|
+
"merge_lists",
|
|
55
|
+
"merge_sets",
|
|
56
|
+
"merge_tuples",
|
|
57
|
+
"mod",
|
|
58
|
+
"multiply",
|
|
59
|
+
"pairwise",
|
|
60
|
+
"pop",
|
|
61
|
+
"pow",
|
|
62
|
+
"prepend",
|
|
63
|
+
"push",
|
|
64
|
+
"replace",
|
|
65
|
+
"setter",
|
|
66
|
+
"subtract",
|
|
67
|
+
"toggle",
|
|
68
|
+
"upper",
|
|
69
|
+
"window",
|
|
70
|
+
]
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""A set of helpful constants used throughout the Bear Dereth and beyond!"""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from .exit_code import ExitCode
|
|
6
|
+
from .file_size import (
|
|
7
|
+
FILE_SIZES,
|
|
8
|
+
GIGABYTES,
|
|
9
|
+
KILOBYTES,
|
|
10
|
+
MEGABYTES,
|
|
11
|
+
TERABYTES,
|
|
12
|
+
Gigabytes,
|
|
13
|
+
Kilobytes,
|
|
14
|
+
Megabytes,
|
|
15
|
+
Terabytes,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def get_config_path() -> Path:
|
|
20
|
+
"""Get the path to the configuration directory based on the operating system."""
|
|
21
|
+
import os # noqa: PLC0415
|
|
22
|
+
|
|
23
|
+
if "XDG_CONFIG_HOME" in os.environ:
|
|
24
|
+
return Path(os.environ["XDG_CONFIG_HOME"])
|
|
25
|
+
if "APPDATA" in os.environ:
|
|
26
|
+
return Path(os.environ["APPDATA"])
|
|
27
|
+
return Path.home() / ".config"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
PATH_TO_DOWNLOADS: Path = Path.home() / "Downloads"
|
|
31
|
+
"""Path to the Downloads folder."""
|
|
32
|
+
PATH_TO_PICTURES: Path = Path.home() / "Pictures"
|
|
33
|
+
"""Path to the Pictures folder."""
|
|
34
|
+
PATH_TO_DOCUMENTS: Path = Path.home() / "Documents"
|
|
35
|
+
"""Path to the Documents folder."""
|
|
36
|
+
PATH_TO_HOME: Path = Path.home()
|
|
37
|
+
"""Path to the user's home directory."""
|
|
38
|
+
PATH_TO_CONFIG: Path = get_config_path()
|
|
39
|
+
"""Path to the configuration directory based on the operating system."""
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
VIDEO_EXTS: list[str] = [".mp4", ".mov", ".avi", ".mkv"]
|
|
43
|
+
"""Extensions for video files."""
|
|
44
|
+
IMAGE_EXTS: list[str] = [".jpg", ".jpeg", ".png", ".gif", ".webp"]
|
|
45
|
+
"""Extensions for image files."""
|
|
46
|
+
FILE_EXTS: list[str] = IMAGE_EXTS + VIDEO_EXTS
|
|
47
|
+
"""Extensions for both image and video files."""
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
__all__ = [
|
|
51
|
+
"FILE_EXTS",
|
|
52
|
+
"FILE_SIZES",
|
|
53
|
+
"GIGABYTES",
|
|
54
|
+
"IMAGE_EXTS",
|
|
55
|
+
"KILOBYTES",
|
|
56
|
+
"MEGABYTES",
|
|
57
|
+
"PATH_TO_CONFIG",
|
|
58
|
+
"PATH_TO_DOCUMENTS",
|
|
59
|
+
"PATH_TO_DOWNLOADS",
|
|
60
|
+
"PATH_TO_HOME",
|
|
61
|
+
"PATH_TO_PICTURES",
|
|
62
|
+
"TERABYTES",
|
|
63
|
+
"VIDEO_EXTS",
|
|
64
|
+
"ExitCode",
|
|
65
|
+
"Gigabytes",
|
|
66
|
+
"Kilobytes",
|
|
67
|
+
"Megabytes",
|
|
68
|
+
"Terabytes",
|
|
69
|
+
]
|