randpal 0.2__tar.gz → 0.4__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.
- {randpal-0.2 → randpal-0.4}/PKG-INFO +2 -2
- {randpal-0.2 → randpal-0.4}/pyproject.toml +3 -0
- randpal-0.4/src/randpal/__init__.py +26 -0
- randpal-0.4/src/randpal/__main__.py +11 -0
- randpal-0.4/src/randpal/cli/__init__.py +3 -0
- randpal-0.4/src/randpal/cli/base.py +32 -0
- randpal-0.4/src/randpal/cli/commands.py +45 -0
- randpal-0.4/src/randpal/cli/parser.py +153 -0
- randpal-0.4/src/randpal/rng/__init__.py +17 -0
- randpal-0.4/src/randpal/rng/base.py +142 -0
- randpal-0.4/src/randpal/rng/python.py +25 -0
- randpal-0.4/src/randpal/rng/secrets.py +25 -0
- randpal-0.2/src/randpal/__init__.py +0 -9
- randpal-0.2/src/randpal/__main__.py +0 -43
- {randpal-0.2 → randpal-0.4}/.github/workflows/publish-version.yaml +0 -0
- {randpal-0.2 → randpal-0.4}/.github/workflows/quality-assurance.yaml +0 -0
- {randpal-0.2 → randpal-0.4}/.gitignore +0 -0
- {randpal-0.2 → randpal-0.4}/.pre-commit-config.yaml +0 -0
- {randpal-0.2 → randpal-0.4}/LICENSE +0 -0
- {randpal-0.2 → randpal-0.4}/README.md +0 -0
- {randpal-0.2 → randpal-0.4}/src/randpal/py.typed +0 -0
- {randpal-0.2 → randpal-0.4}/tests/dummy_test.py +0 -0
- {randpal-0.2 → randpal-0.4}/tox.toml +0 -0
- {randpal-0.2 → randpal-0.4}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: randpal
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4
|
|
4
4
|
Summary: Your friendly random pal - at your random service.
|
|
5
5
|
Project-URL: Repository, https://github.com/soucelover/randpal.git
|
|
6
6
|
Project-URL: Issues, https://github.com/soucelover/randpal/issues
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from importlib import metadata
|
|
2
|
+
|
|
3
|
+
from .rng import (
|
|
4
|
+
FloatGenerator,
|
|
5
|
+
IntegerGenerator,
|
|
6
|
+
PythonRandom,
|
|
7
|
+
RandomProvider,
|
|
8
|
+
SecretsRandom,
|
|
9
|
+
SequencesGenerator,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"FloatGenerator",
|
|
14
|
+
"IntegerGenerator",
|
|
15
|
+
"PythonRandom",
|
|
16
|
+
"RandomProvider",
|
|
17
|
+
"SecretsRandom",
|
|
18
|
+
"SequencesGenerator",
|
|
19
|
+
"__version__",
|
|
20
|
+
]
|
|
21
|
+
PACKAGE_NAME = "randpal"
|
|
22
|
+
|
|
23
|
+
try:
|
|
24
|
+
__version__ = metadata.version(PACKAGE_NAME)
|
|
25
|
+
except metadata.PackageNotFoundError:
|
|
26
|
+
__version__ = "0.1.dev1+UNKNOWN"
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from argparse import Namespace
|
|
4
|
+
from collections.abc import Callable
|
|
5
|
+
from typing import Literal, TypeAlias
|
|
6
|
+
|
|
7
|
+
from randpal.rng import PythonRandom, SecretsRandom
|
|
8
|
+
|
|
9
|
+
CommandFunction: TypeAlias = Callable[[Namespace], None]
|
|
10
|
+
GeneratorCommandFunction: TypeAlias = Callable[
|
|
11
|
+
["GeneratorCommandArgs", "RandomGeneratorFactory"], None
|
|
12
|
+
]
|
|
13
|
+
ProviderCode: TypeAlias = Literal["python", "secrets"]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class RandomGeneratorFactory:
|
|
17
|
+
def create_python_rng(self) -> PythonRandom:
|
|
18
|
+
return PythonRandom()
|
|
19
|
+
|
|
20
|
+
def create_secrets_rng(self) -> SecretsRandom:
|
|
21
|
+
return SecretsRandom()
|
|
22
|
+
|
|
23
|
+
def create_rng(self, code: ProviderCode) -> PythonRandom | SecretsRandom:
|
|
24
|
+
match code:
|
|
25
|
+
case "python":
|
|
26
|
+
return self.create_python_rng()
|
|
27
|
+
case "secrets":
|
|
28
|
+
return self.create_secrets_rng()
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class GeneratorCommandArgs(Namespace):
|
|
32
|
+
provider: ProviderCode
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
if TYPE_CHECKING:
|
|
6
|
+
from randpal.rng import IntegerGenerator, SequencesGenerator
|
|
7
|
+
|
|
8
|
+
from .base import GeneratorCommandArgs, RandomGeneratorFactory
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
VERBOSE_MODE = True
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def command_int(
|
|
15
|
+
args: GeneratorCommandArgs, factory: RandomGeneratorFactory
|
|
16
|
+
) -> None:
|
|
17
|
+
generator: IntegerGenerator = factory.create_rng(args.provider)
|
|
18
|
+
number = generator.randint(args.a, args.b)
|
|
19
|
+
|
|
20
|
+
match args.output_format:
|
|
21
|
+
case "concise":
|
|
22
|
+
print(number) # noqa: T201
|
|
23
|
+
case "verbose":
|
|
24
|
+
print(f"And so, the random number is... {number}!", end="\n\n") # noqa: T201
|
|
25
|
+
case _:
|
|
26
|
+
raise NotImplementedError
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def command_pick(
|
|
30
|
+
args: GeneratorCommandArgs, factory: RandomGeneratorFactory
|
|
31
|
+
) -> None:
|
|
32
|
+
generator: SequencesGenerator = factory.create_rng(args.provider)
|
|
33
|
+
item = generator.choice(args.items)
|
|
34
|
+
|
|
35
|
+
match args.output_format:
|
|
36
|
+
case "concise":
|
|
37
|
+
print(item) # noqa: T201
|
|
38
|
+
case "verbose":
|
|
39
|
+
print( # noqa: T201
|
|
40
|
+
f'The barrel with cards was shaken and a card with "{item}" '
|
|
41
|
+
"written on it has fallen out.",
|
|
42
|
+
end="\n\n",
|
|
43
|
+
)
|
|
44
|
+
case _:
|
|
45
|
+
raise NotImplementedError
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import inspect
|
|
4
|
+
from argparse import ArgumentParser, RawDescriptionHelpFormatter
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
import randpal
|
|
8
|
+
|
|
9
|
+
from .base import GeneratorCommandArgs, RandomGeneratorFactory
|
|
10
|
+
from .commands import command_int, command_pick
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from argparse import (
|
|
14
|
+
_SubParsersAction, # pyright: ignore[reportPrivateUsage]
|
|
15
|
+
)
|
|
16
|
+
from collections.abc import Sequence
|
|
17
|
+
|
|
18
|
+
from .base import CommandFunction, GeneratorCommandFunction
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def get_docs_header(obj: object) -> tuple[str, str] | tuple[None, None]:
|
|
22
|
+
"""Get header of object's docstring.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
obj: Source of docstring.
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
The first line of the dosctring and an original docstring itself.
|
|
29
|
+
"""
|
|
30
|
+
docs = inspect.getdoc(obj)
|
|
31
|
+
|
|
32
|
+
if docs is None:
|
|
33
|
+
return None, None
|
|
34
|
+
|
|
35
|
+
header, _, _body = docs.partition("\n")
|
|
36
|
+
return header, docs
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def add_command( # noqa: PLR0913
|
|
40
|
+
commands: _SubParsersAction[ArgumentParser],
|
|
41
|
+
name: str,
|
|
42
|
+
function: CommandFunction,
|
|
43
|
+
*,
|
|
44
|
+
help: str | None = None,
|
|
45
|
+
epilog: str | None = None,
|
|
46
|
+
aliases: Sequence[str] = (),
|
|
47
|
+
) -> ArgumentParser:
|
|
48
|
+
"""Register a single command in `SubParsersAction`.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
commands: `ArgumentParser`'s action representing commands
|
|
52
|
+
name: Name for the command.
|
|
53
|
+
function: Function implementing command and containing its docstrings.
|
|
54
|
+
aliases: Optional list of aliases for the command.
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
A newly created parser of the command.
|
|
58
|
+
"""
|
|
59
|
+
header, docs = get_docs_header(function)
|
|
60
|
+
|
|
61
|
+
if help is None:
|
|
62
|
+
help = header
|
|
63
|
+
|
|
64
|
+
parser = commands.add_parser(
|
|
65
|
+
name,
|
|
66
|
+
help=help,
|
|
67
|
+
description=docs,
|
|
68
|
+
epilog=epilog,
|
|
69
|
+
aliases=aliases,
|
|
70
|
+
formatter_class=RawDescriptionHelpFormatter,
|
|
71
|
+
)
|
|
72
|
+
parser.set_defaults(command=function)
|
|
73
|
+
return parser
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def add_generator_command( # noqa: PLR0913
|
|
77
|
+
commands: _SubParsersAction[ArgumentParser],
|
|
78
|
+
name: str,
|
|
79
|
+
function: GeneratorCommandFunction,
|
|
80
|
+
*,
|
|
81
|
+
providers: Sequence[str] | None = ("python", "secrets"),
|
|
82
|
+
output_formats: Sequence[str] | None = ("verbose", "concise"),
|
|
83
|
+
help: str | None = None,
|
|
84
|
+
epilog: str | None = None,
|
|
85
|
+
aliases: Sequence[str] = (),
|
|
86
|
+
) -> ArgumentParser:
|
|
87
|
+
def wrapper(args: GeneratorCommandArgs) -> None:
|
|
88
|
+
factory = RandomGeneratorFactory()
|
|
89
|
+
|
|
90
|
+
return function(args, factory)
|
|
91
|
+
|
|
92
|
+
parser = add_command(
|
|
93
|
+
commands,
|
|
94
|
+
name,
|
|
95
|
+
wrapper, # type: ignore [reportArgumentType]
|
|
96
|
+
help=help,
|
|
97
|
+
epilog=epilog,
|
|
98
|
+
aliases=aliases,
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
if providers:
|
|
102
|
+
parser.add_argument(
|
|
103
|
+
"--provider",
|
|
104
|
+
default=providers[0],
|
|
105
|
+
choices=providers,
|
|
106
|
+
help="Provider chosen as a source of entropy "
|
|
107
|
+
"for random generation.",
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
if output_formats:
|
|
111
|
+
parser.add_argument(
|
|
112
|
+
"--output-format",
|
|
113
|
+
"--format",
|
|
114
|
+
default=output_formats[0],
|
|
115
|
+
choices=output_formats,
|
|
116
|
+
help="Output format for the generation results.",
|
|
117
|
+
dest="output_format",
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
return parser
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def register_commands(parser: ArgumentParser) -> None:
|
|
124
|
+
commands = parser.add_subparsers(required=True)
|
|
125
|
+
|
|
126
|
+
com_int = add_generator_command(commands, "int", command_int)
|
|
127
|
+
com_int.add_argument("a", type=int)
|
|
128
|
+
com_int.add_argument("b", type=int)
|
|
129
|
+
|
|
130
|
+
com_choose = add_generator_command(commands, "pick", command_pick)
|
|
131
|
+
com_choose.add_argument("items", nargs="+", metavar="item")
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def setup_parser() -> ArgumentParser:
|
|
135
|
+
parser = ArgumentParser(formatter_class=RawDescriptionHelpFormatter)
|
|
136
|
+
|
|
137
|
+
parser.add_argument(
|
|
138
|
+
"-V",
|
|
139
|
+
"--version",
|
|
140
|
+
action="version",
|
|
141
|
+
version=f"randpal {randpal.__version__}",
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
register_commands(parser)
|
|
145
|
+
|
|
146
|
+
return parser
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def cli(args: list[str] | None = None) -> None:
|
|
150
|
+
parser = setup_parser()
|
|
151
|
+
|
|
152
|
+
parsed = parser.parse_args(args)
|
|
153
|
+
parsed.command(parsed)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from .base import (
|
|
2
|
+
FloatGenerator,
|
|
3
|
+
IntegerGenerator,
|
|
4
|
+
RandomProvider,
|
|
5
|
+
SequencesGenerator,
|
|
6
|
+
)
|
|
7
|
+
from .python import PythonRandom
|
|
8
|
+
from .secrets import SecretsRandom
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"FloatGenerator",
|
|
12
|
+
"IntegerGenerator",
|
|
13
|
+
"PythonRandom",
|
|
14
|
+
"RandomProvider",
|
|
15
|
+
"SecretsRandom",
|
|
16
|
+
"SequencesGenerator",
|
|
17
|
+
]
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import operator
|
|
4
|
+
from abc import abstractmethod
|
|
5
|
+
from typing import TYPE_CHECKING, Protocol, SupportsIndex, TypeVar, overload
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from collections.abc import Sequence
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"FloatGenerator",
|
|
12
|
+
"IntegerGenerator",
|
|
13
|
+
"RandomProvider",
|
|
14
|
+
"SequencesGenerator",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
_T = TypeVar("_T")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class RandomProvider(Protocol):
|
|
22
|
+
@abstractmethod
|
|
23
|
+
def randbytes(self, n: int) -> bytes:
|
|
24
|
+
raise NotImplementedError
|
|
25
|
+
|
|
26
|
+
# NOTE: It's possible to implement random buffering in future
|
|
27
|
+
def randbits(self, k: int) -> int:
|
|
28
|
+
n = (k + 7) // 8
|
|
29
|
+
value = int.from_bytes(self.randbytes(n), "big")
|
|
30
|
+
return value >> (n * 8 - k)
|
|
31
|
+
|
|
32
|
+
def _randbelow(self, n: int) -> int:
|
|
33
|
+
if n <= 0:
|
|
34
|
+
msg = "Empty range -- no values to choose from"
|
|
35
|
+
raise ValueError(msg)
|
|
36
|
+
|
|
37
|
+
k = n.bit_length()
|
|
38
|
+
randbits = self.randbits
|
|
39
|
+
r = randbits(k)
|
|
40
|
+
|
|
41
|
+
while r >= n:
|
|
42
|
+
r = randbits(k)
|
|
43
|
+
|
|
44
|
+
return r
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class IntegerGenerator(RandomProvider, Protocol):
|
|
48
|
+
@overload
|
|
49
|
+
def randrange(self, stop: SupportsIndex, /) -> int: ...
|
|
50
|
+
@overload
|
|
51
|
+
def randrange(
|
|
52
|
+
self, start: SupportsIndex, stop: SupportsIndex, /
|
|
53
|
+
) -> int: ...
|
|
54
|
+
@overload
|
|
55
|
+
def randrange(
|
|
56
|
+
self, start: SupportsIndex, stop: SupportsIndex, step: SupportsIndex, /
|
|
57
|
+
) -> int: ...
|
|
58
|
+
|
|
59
|
+
def randrange(
|
|
60
|
+
self,
|
|
61
|
+
start: SupportsIndex,
|
|
62
|
+
stop: SupportsIndex | None = None,
|
|
63
|
+
step: SupportsIndex = 1,
|
|
64
|
+
/,
|
|
65
|
+
) -> int:
|
|
66
|
+
try:
|
|
67
|
+
istart = operator.index(start) # Validate start
|
|
68
|
+
except TypeError as exc:
|
|
69
|
+
tname = type(start).__name__
|
|
70
|
+
msg = f"Arg 1 of type {tname} couldn't be interpreted as integer"
|
|
71
|
+
raise TypeError(msg) from exc
|
|
72
|
+
|
|
73
|
+
# Fast path: [0; start)
|
|
74
|
+
if stop is None:
|
|
75
|
+
if step != 1:
|
|
76
|
+
msg = "Step was provided without a non-None stop argument"
|
|
77
|
+
raise TypeError(msg)
|
|
78
|
+
|
|
79
|
+
return self._randbelow(istart)
|
|
80
|
+
|
|
81
|
+
try:
|
|
82
|
+
istop = operator.index(stop) # Validate stop
|
|
83
|
+
except TypeError as exc:
|
|
84
|
+
tname = type(stop).__name__
|
|
85
|
+
msg = f"Arg 2 of type {tname} couldn't be interpreted as integer"
|
|
86
|
+
raise TypeError(msg) from exc
|
|
87
|
+
|
|
88
|
+
try:
|
|
89
|
+
istep = operator.index(step) # Validate stop
|
|
90
|
+
except TypeError as exc:
|
|
91
|
+
tname = type(step).__name__
|
|
92
|
+
msg = f"Arg 3 of type {tname} couldn't be interpreted as integer"
|
|
93
|
+
raise TypeError(msg) from exc
|
|
94
|
+
|
|
95
|
+
width = istop - istart
|
|
96
|
+
|
|
97
|
+
# Fast path: [start; stop)
|
|
98
|
+
if istep == 1:
|
|
99
|
+
return istart + self._randbelow(width)
|
|
100
|
+
|
|
101
|
+
if istep == 0:
|
|
102
|
+
msg = "Zero step was provided"
|
|
103
|
+
raise ValueError(msg)
|
|
104
|
+
|
|
105
|
+
# Divide width and round in direction from zero
|
|
106
|
+
n = (width + istep + 1 - (istep > 0) * 2) // istep
|
|
107
|
+
|
|
108
|
+
return istart + istep * self._randbelow(n)
|
|
109
|
+
|
|
110
|
+
def randint(self, a: SupportsIndex, b: SupportsIndex) -> int:
|
|
111
|
+
try:
|
|
112
|
+
ia = operator.index(a) # Validate a
|
|
113
|
+
except TypeError as exc:
|
|
114
|
+
tname = type(a).__name__
|
|
115
|
+
msg = f"Arg 1 of type {tname} couldn't be interpreted as integer"
|
|
116
|
+
raise TypeError(msg) from exc
|
|
117
|
+
|
|
118
|
+
try:
|
|
119
|
+
ib = operator.index(b) # Validate b
|
|
120
|
+
except TypeError as exc:
|
|
121
|
+
tname = type(b).__name__
|
|
122
|
+
msg = f"Arg 2 of type {tname} couldn't be interpreted as integer"
|
|
123
|
+
raise TypeError(msg) from exc
|
|
124
|
+
|
|
125
|
+
width = ib - ia
|
|
126
|
+
return ia + self._randbelow(width + 1)
|
|
127
|
+
|
|
128
|
+
def randbool(self) -> bool:
|
|
129
|
+
return bool(self.randbits(1))
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
class FloatGenerator(Protocol):
|
|
133
|
+
def uniform(self, a: float, b: float) -> float:
|
|
134
|
+
raise NotImplementedError
|
|
135
|
+
|
|
136
|
+
def chance(self, probability: float) -> bool:
|
|
137
|
+
raise NotImplementedError
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
class SequencesGenerator(RandomProvider, Protocol):
|
|
141
|
+
def choice(self, seq: Sequence[_T]) -> _T:
|
|
142
|
+
return seq[self._randbelow(len(seq))]
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import random
|
|
4
|
+
|
|
5
|
+
from typing_extensions import override
|
|
6
|
+
|
|
7
|
+
from .base import IntegerGenerator, RandomProvider, SequencesGenerator
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class PythonRandom(SequencesGenerator, IntegerGenerator, RandomProvider):
|
|
11
|
+
_generator: random.Random
|
|
12
|
+
|
|
13
|
+
def __init__(self, generator: random.Random | None = None) -> None:
|
|
14
|
+
if generator is None:
|
|
15
|
+
generator = random.Random() # noqa: S311
|
|
16
|
+
|
|
17
|
+
self._generator = generator
|
|
18
|
+
|
|
19
|
+
@override
|
|
20
|
+
def randbytes(self, n: int) -> bytes:
|
|
21
|
+
return self._generator.randbytes(n)
|
|
22
|
+
|
|
23
|
+
@override
|
|
24
|
+
def randbits(self, k: int) -> int:
|
|
25
|
+
return self._generator.getrandbits(k)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import secrets
|
|
4
|
+
|
|
5
|
+
from typing_extensions import override
|
|
6
|
+
|
|
7
|
+
from .base import IntegerGenerator, RandomProvider, SequencesGenerator
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class SecretsRandom(SequencesGenerator, IntegerGenerator, RandomProvider):
|
|
11
|
+
_generator: secrets.SystemRandom
|
|
12
|
+
|
|
13
|
+
def __init__(self, generator: secrets.SystemRandom | None = None) -> None:
|
|
14
|
+
if generator is None:
|
|
15
|
+
generator = secrets.SystemRandom()
|
|
16
|
+
|
|
17
|
+
self._generator = generator
|
|
18
|
+
|
|
19
|
+
@override
|
|
20
|
+
def randbytes(self, n: int) -> bytes:
|
|
21
|
+
return self._generator.randbytes(n)
|
|
22
|
+
|
|
23
|
+
@override
|
|
24
|
+
def randbits(self, k: int) -> int:
|
|
25
|
+
return self._generator.getrandbits(k)
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import argparse
|
|
2
|
-
import random
|
|
3
|
-
|
|
4
|
-
VERBOSE_MODE = True
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
def command_int(args: argparse.Namespace) -> None:
|
|
8
|
-
if VERBOSE_MODE:
|
|
9
|
-
print("Random number is ", end="") # noqa: T201
|
|
10
|
-
|
|
11
|
-
print(random.randint(args.a, args.b), end="\n\n") # noqa: S311, T201
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def command_choose(args: argparse.Namespace) -> None:
|
|
15
|
-
if VERBOSE_MODE:
|
|
16
|
-
print("Random chosen item is ", end="") # noqa: T201
|
|
17
|
-
|
|
18
|
-
print(random.choice(args.item), end="\n\n") # noqa: S311, T201
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
def parse_args() -> argparse.Namespace:
|
|
22
|
-
parser = argparse.ArgumentParser()
|
|
23
|
-
commands = parser.add_subparsers(required=True)
|
|
24
|
-
|
|
25
|
-
com_int = commands.add_parser("int")
|
|
26
|
-
com_int.add_argument("a", type=int)
|
|
27
|
-
com_int.add_argument("b", type=int)
|
|
28
|
-
com_int.set_defaults(command=command_int)
|
|
29
|
-
|
|
30
|
-
com_choose = commands.add_parser("choose")
|
|
31
|
-
com_choose.add_argument("item", nargs="+")
|
|
32
|
-
com_choose.set_defaults(command=command_choose)
|
|
33
|
-
|
|
34
|
-
return parser.parse_args()
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
def main() -> None:
|
|
38
|
-
args = parse_args()
|
|
39
|
-
args.command(args)
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
if __name__ == "__main__":
|
|
43
|
-
main()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|