passmaker 0.1.1__tar.gz → 0.2__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.
- {passmaker-0.1.1 → passmaker-0.2}/.github/workflows/publish-version.yaml +10 -10
- passmaker-0.1.1/.github/workflows/static-checks.yaml → passmaker-0.2/.github/workflows/quality-assurance.yaml +18 -14
- passmaker-0.2/.pre-commit-config.yaml +20 -0
- passmaker-0.2/PKG-INFO +10 -0
- {passmaker-0.1.1 → passmaker-0.2}/pyproject.toml +7 -1
- passmaker-0.2/src/passmaker/__init__.py +23 -0
- passmaker-0.2/src/passmaker/__main__.py +19 -0
- passmaker-0.2/src/passmaker/cli.py +111 -0
- passmaker-0.2/src/passmaker/passgen.py +118 -0
- passmaker-0.2/src/passmaker/rules.py +66 -0
- {passmaker-0.1.1 → passmaker-0.2}/uv.lock +56 -14
- passmaker-0.1.1/.pre-commit-config.yaml +0 -19
- passmaker-0.1.1/PKG-INFO +0 -8
- passmaker-0.1.1/src/passmaker/__init__.py +0 -0
- passmaker-0.1.1/src/passmaker/__main__.py +0 -211
- {passmaker-0.1.1 → passmaker-0.2}/.gitignore +0 -0
- {passmaker-0.1.1 → passmaker-0.2}/README.md +0 -0
|
@@ -1,24 +1,26 @@
|
|
|
1
|
-
name:
|
|
1
|
+
name: Publish new package version
|
|
2
2
|
|
|
3
3
|
on:
|
|
4
4
|
push:
|
|
5
5
|
# On version pushes
|
|
6
6
|
tags:
|
|
7
7
|
- 'v[0-9]+.[0-9]+.[0-9]+'
|
|
8
|
+
- 'v[0-9]+.[0-9]+'
|
|
8
9
|
|
|
9
10
|
workflow_dispatch:
|
|
10
11
|
|
|
11
|
-
permissions:
|
|
12
|
-
contents: write
|
|
13
|
-
|
|
14
12
|
jobs:
|
|
15
13
|
verify-checks:
|
|
16
|
-
uses: ./.github/workflows/
|
|
14
|
+
uses: ./.github/workflows/quality-assurance.yaml
|
|
17
15
|
|
|
18
16
|
publish:
|
|
19
17
|
name: Build and publish Python Package
|
|
20
18
|
needs: verify-checks
|
|
21
19
|
runs-on: ubuntu-latest
|
|
20
|
+
permissions:
|
|
21
|
+
id-token: write
|
|
22
|
+
contents: write
|
|
23
|
+
environment: release
|
|
22
24
|
|
|
23
25
|
steps:
|
|
24
26
|
- uses: actions/checkout@v4
|
|
@@ -41,7 +43,7 @@ jobs:
|
|
|
41
43
|
shell: bash
|
|
42
44
|
run: |
|
|
43
45
|
VERSION="$(hatchling version)"
|
|
44
|
-
if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
|
46
|
+
if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ && ! "$VERSION" =~ ^[0-9]+\.[0-9]+$ ]]; then
|
|
45
47
|
echo "::error::Expected a pure release version, got: $VERSION"
|
|
46
48
|
exit 1
|
|
47
49
|
fi
|
|
@@ -52,8 +54,6 @@ jobs:
|
|
|
52
54
|
uv build
|
|
53
55
|
|
|
54
56
|
- name: Publish package to PyPI
|
|
55
|
-
env:
|
|
56
|
-
UV_PUBLISH_TOKEN: ${{ secrets.PYPI_TOKEN }}
|
|
57
57
|
run: |
|
|
58
58
|
uv publish
|
|
59
59
|
|
|
@@ -63,5 +63,5 @@ jobs:
|
|
|
63
63
|
VERSION: ${{ env.VERSION }}
|
|
64
64
|
run: |
|
|
65
65
|
gh release create "v$VERSION" \
|
|
66
|
-
--generate-notes \
|
|
67
|
-
|
|
66
|
+
--generate-notes --verify-tag \
|
|
67
|
+
./dist/*.tar.gz ./dist/*.whl
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
name:
|
|
1
|
+
name: Run quality assurance checks
|
|
2
2
|
|
|
3
3
|
on:
|
|
4
4
|
pull_request:
|
|
5
|
-
types:
|
|
5
|
+
types:
|
|
6
|
+
- opened
|
|
7
|
+
- reopened
|
|
8
|
+
- synchronize
|
|
9
|
+
- ready_for_review
|
|
6
10
|
|
|
7
11
|
push:
|
|
8
12
|
branches:
|
|
@@ -16,16 +20,17 @@ jobs:
|
|
|
16
20
|
if: github.event.pull_request.draft == false
|
|
17
21
|
runs-on: ubuntu-latest
|
|
18
22
|
steps:
|
|
19
|
-
- uses: actions/checkout@
|
|
23
|
+
- uses: actions/checkout@v7
|
|
20
24
|
|
|
21
25
|
- name: Check formatting
|
|
22
|
-
uses: astral-sh/ruff-action@
|
|
26
|
+
uses: astral-sh/ruff-action@v4.1.0
|
|
23
27
|
with:
|
|
24
|
-
|
|
28
|
+
version-file: uv.lock
|
|
29
|
+
args: format --check --diff
|
|
25
30
|
|
|
26
31
|
- name: Check linting
|
|
27
32
|
run: |
|
|
28
|
-
ruff check --output-format=github
|
|
33
|
+
ruff check --output-format=github
|
|
29
34
|
|
|
30
35
|
type-checking:
|
|
31
36
|
name: Static type checking
|
|
@@ -35,27 +40,26 @@ jobs:
|
|
|
35
40
|
strategy:
|
|
36
41
|
fail-fast: true
|
|
37
42
|
matrix:
|
|
38
|
-
python-version:
|
|
43
|
+
python-version:
|
|
44
|
+
- "3.14"
|
|
39
45
|
|
|
40
46
|
steps:
|
|
41
|
-
- uses: actions/checkout@
|
|
47
|
+
- uses: actions/checkout@v7
|
|
42
48
|
|
|
43
|
-
- uses: actions/setup-python@
|
|
49
|
+
- uses: actions/setup-python@v7
|
|
44
50
|
with:
|
|
45
51
|
python-version: ${{ matrix.python-version }}
|
|
46
52
|
|
|
47
53
|
- name: Setup uv
|
|
48
|
-
uses: astral-sh/setup-uv@
|
|
54
|
+
uses: astral-sh/setup-uv@v10.0.1
|
|
49
55
|
with:
|
|
50
56
|
activate-environment: true
|
|
51
|
-
enable-cache: true
|
|
52
57
|
|
|
53
58
|
- name: Synchronise dependencies
|
|
54
59
|
run: |
|
|
55
60
|
uv sync --locked --all-extras --all-groups --dev
|
|
56
61
|
|
|
57
62
|
- name: Check with Pyright
|
|
58
|
-
uses: jakebailey/pyright-action@
|
|
63
|
+
uses: jakebailey/pyright-action@v3
|
|
59
64
|
with:
|
|
60
|
-
version:
|
|
61
|
-
annotate: all
|
|
65
|
+
version: PATH
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: local
|
|
3
|
+
hooks:
|
|
4
|
+
- id: pyrefly-check
|
|
5
|
+
name: Pyrefly Check
|
|
6
|
+
entry: uv run --dev pyrefly check
|
|
7
|
+
language: system
|
|
8
|
+
pass_filenames: false
|
|
9
|
+
|
|
10
|
+
- id: ruff-check
|
|
11
|
+
name: Ruff Check
|
|
12
|
+
entry: uv run --dev ruff check
|
|
13
|
+
language: system
|
|
14
|
+
files: .*[.]py[i]?$
|
|
15
|
+
|
|
16
|
+
- id: ruff-format
|
|
17
|
+
name: Ruff Format
|
|
18
|
+
entry: uv run --dev ruff format --check
|
|
19
|
+
language: system
|
|
20
|
+
files: .*[.]py[i]?$
|
passmaker-0.2/PKG-INFO
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: passmaker
|
|
3
|
+
Version: 0.2
|
|
4
|
+
Summary: Generate strong passwords with ease and peace.
|
|
5
|
+
Author-email: Soucelover <mr.ktotov@gmail.com>
|
|
6
|
+
Requires-Python: >=3.14
|
|
7
|
+
Requires-Dist: randpal>=0.5
|
|
8
|
+
Provides-Extra: cli
|
|
9
|
+
Requires-Dist: click-option-group>=0.5.9; extra == 'cli'
|
|
10
|
+
Requires-Dist: click>=8.3.1; extra == 'cli'
|
|
@@ -12,6 +12,11 @@ dynamic = ["version"]
|
|
|
12
12
|
# ===================
|
|
13
13
|
requires-python = ">=3.14"
|
|
14
14
|
dependencies = [
|
|
15
|
+
"randpal>=0.5",
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
[project.optional-dependencies]
|
|
19
|
+
cli = [
|
|
15
20
|
"click>=8.3.1",
|
|
16
21
|
"click-option-group>=0.5.9",
|
|
17
22
|
]
|
|
@@ -19,7 +24,8 @@ dependencies = [
|
|
|
19
24
|
[dependency-groups]
|
|
20
25
|
dev = [
|
|
21
26
|
"pre-commit>=4.6.0",
|
|
22
|
-
"pyrefly>=1.
|
|
27
|
+
"pyrefly>=1.2.0",
|
|
28
|
+
"pyright>=1.1.411",
|
|
23
29
|
"ruff>=0.15.15",
|
|
24
30
|
]
|
|
25
31
|
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from .passgen import (
|
|
2
|
+
PasswordGenerator,
|
|
3
|
+
build_alphabet,
|
|
4
|
+
generate_passwords,
|
|
5
|
+
passgen,
|
|
6
|
+
)
|
|
7
|
+
from .rules import (
|
|
8
|
+
ALL_RULES,
|
|
9
|
+
no_duplicates,
|
|
10
|
+
no_sequence_characters,
|
|
11
|
+
no_similar_characters,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"ALL_RULES",
|
|
16
|
+
"PasswordGenerator",
|
|
17
|
+
"build_alphabet",
|
|
18
|
+
"generate_passwords",
|
|
19
|
+
"no_duplicates",
|
|
20
|
+
"no_sequence_characters",
|
|
21
|
+
"no_similar_characters",
|
|
22
|
+
"passgen",
|
|
23
|
+
]
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
def main() -> None:
|
|
2
|
+
try:
|
|
3
|
+
from .cli import cli # noqa: PLC0415
|
|
4
|
+
except ImportError as exc:
|
|
5
|
+
if exc.name is None or not exc.name.startswith("click"):
|
|
6
|
+
raise
|
|
7
|
+
|
|
8
|
+
print( # noqa: T201
|
|
9
|
+
"Missing optional dependency `cli`. Try installing with command:"
|
|
10
|
+
"\n\n pip install passmaker[cli]",
|
|
11
|
+
end="\n\n",
|
|
12
|
+
)
|
|
13
|
+
raise SystemExit(1) from exc
|
|
14
|
+
|
|
15
|
+
cli()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
if __name__ == "__main__":
|
|
19
|
+
main()
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# Optional depenencies group `cli` is required for:
|
|
2
|
+
# - click
|
|
3
|
+
# - click_option_group
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
import click
|
|
7
|
+
from click_option_group import optgroup
|
|
8
|
+
|
|
9
|
+
from .passgen import generate_passwords
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@click.command()
|
|
13
|
+
@click.option(
|
|
14
|
+
"-l", "--length", default=22, type=int, help="Length of each password."
|
|
15
|
+
)
|
|
16
|
+
@click.option(
|
|
17
|
+
"-c",
|
|
18
|
+
"--count",
|
|
19
|
+
default=5,
|
|
20
|
+
type=int,
|
|
21
|
+
help="Count of passwords to generate in one take.",
|
|
22
|
+
)
|
|
23
|
+
@optgroup.group(
|
|
24
|
+
"Password alphabet",
|
|
25
|
+
help="Options controlling what set of symbols can be used in a password.",
|
|
26
|
+
)
|
|
27
|
+
@optgroup.option(
|
|
28
|
+
"-d/-D",
|
|
29
|
+
"--digits/--no-digits",
|
|
30
|
+
"use_digits",
|
|
31
|
+
default=True,
|
|
32
|
+
help="Where to use digits in password or no (default: yes)",
|
|
33
|
+
)
|
|
34
|
+
@optgroup.option(
|
|
35
|
+
"-a/-A",
|
|
36
|
+
"--alpha/--no-alpha",
|
|
37
|
+
"use_alpha",
|
|
38
|
+
default=True,
|
|
39
|
+
help="Where to use ASCII alphabet symbols in password or no "
|
|
40
|
+
"(default: yes)",
|
|
41
|
+
)
|
|
42
|
+
@optgroup.option(
|
|
43
|
+
"-S",
|
|
44
|
+
"--no-symbols",
|
|
45
|
+
default=False,
|
|
46
|
+
is_flag=True,
|
|
47
|
+
flag_value=True,
|
|
48
|
+
help="Don't use punctuation symbols for password generation "
|
|
49
|
+
"(overrides 'symbols' option).",
|
|
50
|
+
)
|
|
51
|
+
@optgroup.option(
|
|
52
|
+
"-s",
|
|
53
|
+
"--symbols",
|
|
54
|
+
type=str,
|
|
55
|
+
help="Use these exact symbols for password generation.",
|
|
56
|
+
)
|
|
57
|
+
@optgroup.group(
|
|
58
|
+
"Generation rules", help="Set of rules to control password generation."
|
|
59
|
+
)
|
|
60
|
+
@optgroup.option(
|
|
61
|
+
"--no-duplicates/--duplicates",
|
|
62
|
+
"use_duplicates",
|
|
63
|
+
default=True,
|
|
64
|
+
help="Allow or disallow duplicate characters.",
|
|
65
|
+
)
|
|
66
|
+
@optgroup.option(
|
|
67
|
+
"--no-sequence/--sequence",
|
|
68
|
+
"use_sequence",
|
|
69
|
+
default=True,
|
|
70
|
+
help="Allow or disallow sequential characters.",
|
|
71
|
+
)
|
|
72
|
+
@optgroup.option(
|
|
73
|
+
"--no-similar/--similar",
|
|
74
|
+
"use_similar",
|
|
75
|
+
default=True,
|
|
76
|
+
help="Allow or disallow similar looking characters.",
|
|
77
|
+
)
|
|
78
|
+
def cli( # noqa: PLR0913
|
|
79
|
+
length: int,
|
|
80
|
+
count: int,
|
|
81
|
+
*,
|
|
82
|
+
use_digits: bool,
|
|
83
|
+
use_alpha: bool,
|
|
84
|
+
symbols: str,
|
|
85
|
+
no_symbols: bool,
|
|
86
|
+
use_duplicates: bool,
|
|
87
|
+
use_sequence: bool,
|
|
88
|
+
use_similar: bool,
|
|
89
|
+
) -> None:
|
|
90
|
+
"""Simple password generator. Safe to use, generates quite strong
|
|
91
|
+
passwords.
|
|
92
|
+
"""
|
|
93
|
+
if no_symbols and symbols:
|
|
94
|
+
msg = (
|
|
95
|
+
"The argument '--symbols=...' cannot be used with '--no-symbols'"
|
|
96
|
+
)
|
|
97
|
+
raise click.ClickException(msg)
|
|
98
|
+
|
|
99
|
+
passwords = generate_passwords(
|
|
100
|
+
length,
|
|
101
|
+
count,
|
|
102
|
+
use_digits=use_digits,
|
|
103
|
+
use_alpha=use_alpha,
|
|
104
|
+
symbols=False if no_symbols else symbols or True,
|
|
105
|
+
use_duplicates=use_duplicates,
|
|
106
|
+
use_sequence=use_sequence,
|
|
107
|
+
use_similar=use_similar,
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
for password in passwords:
|
|
111
|
+
click.echo(password)
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
import secrets
|
|
3
|
+
import string
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
from randpal.rng import SecretsRandom
|
|
7
|
+
|
|
8
|
+
from .rules import (
|
|
9
|
+
ALL_RULES,
|
|
10
|
+
no_duplicates,
|
|
11
|
+
no_sequence_characters,
|
|
12
|
+
no_similar_characters,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from collections.abc import Iterable
|
|
17
|
+
|
|
18
|
+
from randpal.rng import SequencesGenerator
|
|
19
|
+
|
|
20
|
+
from .rules import Rule
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class PasswordGenerator:
|
|
24
|
+
_random: SequencesGenerator
|
|
25
|
+
rules: tuple[Rule, ...]
|
|
26
|
+
alphabet: frozenset[str]
|
|
27
|
+
|
|
28
|
+
def __init__(
|
|
29
|
+
self,
|
|
30
|
+
alphabet: str | None = None,
|
|
31
|
+
*,
|
|
32
|
+
rules: Iterable[Rule] = ALL_RULES,
|
|
33
|
+
random_provider: SequencesGenerator | None = None,
|
|
34
|
+
) -> None:
|
|
35
|
+
self._random = random_provider or SecretsRandom()
|
|
36
|
+
self.rules = tuple(rules)
|
|
37
|
+
|
|
38
|
+
if alphabet is None:
|
|
39
|
+
self.alphabet = frozenset(build_alphabet())
|
|
40
|
+
else:
|
|
41
|
+
self.alphabet = frozenset(alphabet)
|
|
42
|
+
|
|
43
|
+
def gen_password(self, length: int) -> str:
|
|
44
|
+
password = ""
|
|
45
|
+
|
|
46
|
+
for _ in range(length):
|
|
47
|
+
allowed_symbols = functools.reduce(
|
|
48
|
+
lambda alpha, rule: rule(password, alpha),
|
|
49
|
+
self.rules,
|
|
50
|
+
self.alphabet,
|
|
51
|
+
)
|
|
52
|
+
password += secrets.choice(list(allowed_symbols))
|
|
53
|
+
|
|
54
|
+
return password
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def passgen(
|
|
58
|
+
length: int,
|
|
59
|
+
alphabet: str | None = None,
|
|
60
|
+
rules: Iterable[Rule] = ALL_RULES,
|
|
61
|
+
) -> str:
|
|
62
|
+
generator = PasswordGenerator(alphabet, rules=rules)
|
|
63
|
+
return generator.gen_password(length)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def build_alphabet(
|
|
67
|
+
*,
|
|
68
|
+
digits: bool = True,
|
|
69
|
+
lowercase: bool = True,
|
|
70
|
+
uppercase: bool = True,
|
|
71
|
+
symbols: bool | str = True,
|
|
72
|
+
) -> str:
|
|
73
|
+
alphabet = ""
|
|
74
|
+
|
|
75
|
+
if digits:
|
|
76
|
+
alphabet += string.digits
|
|
77
|
+
if lowercase:
|
|
78
|
+
alphabet += string.ascii_lowercase
|
|
79
|
+
if uppercase:
|
|
80
|
+
alphabet += string.ascii_uppercase
|
|
81
|
+
if symbols:
|
|
82
|
+
alphabet += (
|
|
83
|
+
symbols if isinstance(symbols, str) else string.punctuation
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
return alphabet
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def generate_passwords( # noqa: PLR0913
|
|
90
|
+
length: int = 22,
|
|
91
|
+
count: int = 5,
|
|
92
|
+
*,
|
|
93
|
+
use_digits: bool = True,
|
|
94
|
+
use_alpha: bool = True,
|
|
95
|
+
symbols: str | bool = True,
|
|
96
|
+
use_duplicates: bool = True,
|
|
97
|
+
use_sequence: bool = True,
|
|
98
|
+
use_similar: bool = True,
|
|
99
|
+
) -> list[str]:
|
|
100
|
+
alphabet = build_alphabet(
|
|
101
|
+
digits=use_digits,
|
|
102
|
+
lowercase=use_alpha,
|
|
103
|
+
uppercase=use_alpha,
|
|
104
|
+
symbols=symbols,
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
rules: list[Rule] = []
|
|
108
|
+
|
|
109
|
+
if use_duplicates:
|
|
110
|
+
rules.append(no_duplicates)
|
|
111
|
+
if use_sequence:
|
|
112
|
+
rules.append(no_sequence_characters)
|
|
113
|
+
if use_similar:
|
|
114
|
+
rules.append(no_similar_characters)
|
|
115
|
+
|
|
116
|
+
generator = PasswordGenerator(alphabet, rules=rules)
|
|
117
|
+
|
|
118
|
+
return [generator.gen_password(length) for _ in range(count)]
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import contextlib
|
|
2
|
+
from collections.abc import Callable
|
|
3
|
+
from collections.abc import Set as AbstractSet
|
|
4
|
+
|
|
5
|
+
# Rule is a filter to an alphabet
|
|
6
|
+
type Rule = Callable[[str, AbstractSet[str]], AbstractSet[str]]
|
|
7
|
+
QWERTY: str = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM"
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def no_duplicates(
|
|
11
|
+
password: str, alphabet: AbstractSet[str]
|
|
12
|
+
) -> AbstractSet[str]:
|
|
13
|
+
"""Don't use the same character more than once in a password.
|
|
14
|
+
|
|
15
|
+
This rule removes from alphabet every char from password.
|
|
16
|
+
"""
|
|
17
|
+
if not password:
|
|
18
|
+
return alphabet
|
|
19
|
+
|
|
20
|
+
return alphabet - set(password)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def no_sequence_characters(
|
|
24
|
+
password: str, alphabet: AbstractSet[str]
|
|
25
|
+
) -> AbstractSet[str]:
|
|
26
|
+
"""Don't use characters in password in a sequence, e.g. abc, 123, etc.
|
|
27
|
+
|
|
28
|
+
This rule enforces that the next symbol in password cannot be from
|
|
29
|
+
sequence, thus it's removed from alphabet.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
def _get_char(string: str, idx: int) -> str:
|
|
33
|
+
try:
|
|
34
|
+
return string[idx]
|
|
35
|
+
except IndexError:
|
|
36
|
+
return ""
|
|
37
|
+
|
|
38
|
+
if not password:
|
|
39
|
+
return alphabet
|
|
40
|
+
|
|
41
|
+
with contextlib.suppress(ValueError):
|
|
42
|
+
index = QWERTY.index(password[-1])
|
|
43
|
+
|
|
44
|
+
alphabet -= {
|
|
45
|
+
_get_char(QWERTY, index - 1),
|
|
46
|
+
_get_char(QWERTY, index + 1),
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return alphabet - {chr(ord(password[-1]) - 1), chr(ord(password[-1]) + 1)}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def no_similar_characters(
|
|
53
|
+
password: str, alphabet: AbstractSet[str]
|
|
54
|
+
) -> AbstractSet[str]:
|
|
55
|
+
"""Don't use similar characters in password, e.g. i, I, L, l, 1, etc.
|
|
56
|
+
|
|
57
|
+
This rule removes from alphabet these characters: i1Il|, oO0, `.:
|
|
58
|
+
"""
|
|
59
|
+
return alphabet - set("i1Il|oO0`.:")
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
ALL_RULES: list[Rule] = [
|
|
63
|
+
no_duplicates,
|
|
64
|
+
no_sequence_characters,
|
|
65
|
+
no_similar_characters,
|
|
66
|
+
]
|
|
@@ -84,6 +84,11 @@ wheels = [
|
|
|
84
84
|
name = "passmaker"
|
|
85
85
|
source = { editable = "." }
|
|
86
86
|
dependencies = [
|
|
87
|
+
{ name = "randpal" },
|
|
88
|
+
]
|
|
89
|
+
|
|
90
|
+
[package.optional-dependencies]
|
|
91
|
+
cli = [
|
|
87
92
|
{ name = "click" },
|
|
88
93
|
{ name = "click-option-group" },
|
|
89
94
|
]
|
|
@@ -92,19 +97,23 @@ dependencies = [
|
|
|
92
97
|
dev = [
|
|
93
98
|
{ name = "pre-commit" },
|
|
94
99
|
{ name = "pyrefly" },
|
|
100
|
+
{ name = "pyright" },
|
|
95
101
|
{ name = "ruff" },
|
|
96
102
|
]
|
|
97
103
|
|
|
98
104
|
[package.metadata]
|
|
99
105
|
requires-dist = [
|
|
100
|
-
{ name = "click", specifier = ">=8.3.1" },
|
|
101
|
-
{ name = "click-option-group", specifier = ">=0.5.9" },
|
|
106
|
+
{ name = "click", marker = "extra == 'cli'", specifier = ">=8.3.1" },
|
|
107
|
+
{ name = "click-option-group", marker = "extra == 'cli'", specifier = ">=0.5.9" },
|
|
108
|
+
{ name = "randpal", specifier = ">=0.5" },
|
|
102
109
|
]
|
|
110
|
+
provides-extras = ["cli"]
|
|
103
111
|
|
|
104
112
|
[package.metadata.requires-dev]
|
|
105
113
|
dev = [
|
|
106
114
|
{ name = "pre-commit", specifier = ">=4.6.0" },
|
|
107
|
-
{ name = "pyrefly", specifier = ">=1.
|
|
115
|
+
{ name = "pyrefly", specifier = ">=1.2.0" },
|
|
116
|
+
{ name = "pyright", specifier = ">=1.1.411" },
|
|
108
117
|
{ name = "ruff", specifier = ">=0.15.15" },
|
|
109
118
|
]
|
|
110
119
|
|
|
@@ -135,19 +144,34 @@ wheels = [
|
|
|
135
144
|
|
|
136
145
|
[[package]]
|
|
137
146
|
name = "pyrefly"
|
|
138
|
-
version = "1.
|
|
147
|
+
version = "1.2.0"
|
|
148
|
+
source = { registry = "https://pypi.org/simple" }
|
|
149
|
+
sdist = { url = "https://files.pythonhosted.org/packages/89/01/a86e9f24722b095c3f88e3616132b75a21b0df53804bdc6a45314dd4d93c/pyrefly-1.2.0.tar.gz", hash = "sha256:5485f960fc2481617068c918335c39ab1507ef90b6b5bd35bf57726e60e73185", size = 6243654, upload-time = "2026-08-01T02:56:27.592Z" }
|
|
150
|
+
wheels = [
|
|
151
|
+
{ url = "https://files.pythonhosted.org/packages/7d/9d/3c0ef1d4843987b22f996ed381ec9cf5a3b1273e29804db276252e4c95eb/pyrefly-1.2.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:7f46d983ac49ddd2b043694960a01dc6a19a5cfd8eec609d6bd9c42866f91b4e", size = 14026305, upload-time = "2026-08-01T02:56:02.611Z" },
|
|
152
|
+
{ url = "https://files.pythonhosted.org/packages/0a/06/03bbb78fbea54cdc65b626619f3597d5611aca4fdef11e72a4e8360e7e63/pyrefly-1.2.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:756f669b5555090f5c1a4fef30db1785fabe657764f7e4e6dc88994dfb8ca82d", size = 13463880, upload-time = "2026-08-01T02:56:04.93Z" },
|
|
153
|
+
{ url = "https://files.pythonhosted.org/packages/13/5a/7d8bc00a38e93bbc9c3e7bd14d305f7948717e667c9bcddeab9dd42fd255/pyrefly-1.2.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3465812ce5ef4781fb592edbf2724547296f0a3124be115d73c7e8b2401862d", size = 13907329, upload-time = "2026-08-01T02:56:07.104Z" },
|
|
154
|
+
{ url = "https://files.pythonhosted.org/packages/be/94/9e08b4bf799d0b8f36b55a2783c7ba5f51730cf0632a85a67b5b5ed876cd/pyrefly-1.2.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5de7b2ad2bba5c8055181681a84b74143eac2234a48ba5d1b7ed7e7a722b02bd", size = 15039020, upload-time = "2026-08-01T02:56:09.208Z" },
|
|
155
|
+
{ url = "https://files.pythonhosted.org/packages/5b/bd/bca5fd0c80f4daf8ee6903a29df9f3de1feb05ff0946b8f35ec8c5096b13/pyrefly-1.2.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:25822ea9505f589ea8a725e4268b475132fb89e038fbf092e446510443ac142a", size = 14986199, upload-time = "2026-08-01T02:56:11.924Z" },
|
|
156
|
+
{ url = "https://files.pythonhosted.org/packages/97/f7/f07087f3d185ad2eced0c56cef89ca5474dfb4ff25f146cd50a861c97553/pyrefly-1.2.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90efe75e17491ef5d636e10469e9278d7d0256b3b4c5e1f4750069bf3ae0f5d1", size = 14393715, upload-time = "2026-08-01T02:56:14.143Z" },
|
|
157
|
+
{ url = "https://files.pythonhosted.org/packages/d3/70/0d142c320e284b9e3ce35e9b1e58b8ce2ee1f578f2a7234bc30e5022b94f/pyrefly-1.2.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:368aaf7eee4f511ddc0f8e564cf14e01ab2f10b0db9105c6d5b153bf498d07bf", size = 13933008, upload-time = "2026-08-01T02:56:16.525Z" },
|
|
158
|
+
{ url = "https://files.pythonhosted.org/packages/5d/e8/e84f11b6e1f63fd453ad3654213b9a0f6f4de8cef6b58038eef2d0d5955d/pyrefly-1.2.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:d52d5da7bc65fb7675fbaa80eda879d4f8787c494f04cac21603330d3abbdbbe", size = 14431827, upload-time = "2026-08-01T02:56:18.645Z" },
|
|
159
|
+
{ url = "https://files.pythonhosted.org/packages/0f/06/810d31380f66c75e1c0779a408d3b16117b1b368b57894f6aa66bef21686/pyrefly-1.2.0-py3-none-win32.whl", hash = "sha256:8c90751de8506d938e8f802659c74cf35bd7a0036510ee6c634a38eebb280bfa", size = 13229447, upload-time = "2026-08-01T02:56:20.921Z" },
|
|
160
|
+
{ url = "https://files.pythonhosted.org/packages/ed/98/4dafa3c7a1caed2dc8cc708dde09ba27963c7736508f55b626fff3024113/pyrefly-1.2.0-py3-none-win_amd64.whl", hash = "sha256:8a8964c224ccc4882730130955815de21ff443c1ac3f0b90685b19bf63848170", size = 14087387, upload-time = "2026-08-01T02:56:23.188Z" },
|
|
161
|
+
{ url = "https://files.pythonhosted.org/packages/1b/1c/df3cb0a2e5591660ded7a1836cd2f29dc48c91adb1c0a3a700a96f6d09e1/pyrefly-1.2.0-py3-none-win_arm64.whl", hash = "sha256:3a90bb8df39dfbac74b1f3b2e9d7c526b8f80568884c3944d955023a73ebf61e", size = 13430873, upload-time = "2026-08-01T02:56:25.425Z" },
|
|
162
|
+
]
|
|
163
|
+
|
|
164
|
+
[[package]]
|
|
165
|
+
name = "pyright"
|
|
166
|
+
version = "1.1.411"
|
|
139
167
|
source = { registry = "https://pypi.org/simple" }
|
|
140
|
-
|
|
168
|
+
dependencies = [
|
|
169
|
+
{ name = "nodeenv" },
|
|
170
|
+
{ name = "typing-extensions" },
|
|
171
|
+
]
|
|
172
|
+
sdist = { url = "https://files.pythonhosted.org/packages/7e/ab/265f7dc69d28113ebba19092e57b075f41543b2ed048429c5f56e2b88eac/pyright-1.1.411.tar.gz", hash = "sha256:d885a0551f2e763b089a02702174e7f4ba77548cddabc972ab86d1f7f1b0f998", size = 4112861, upload-time = "2026-06-25T02:14:06.37Z" }
|
|
141
173
|
wheels = [
|
|
142
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
143
|
-
{ url = "https://files.pythonhosted.org/packages/82/91/a3cf2a1e87d336eaa804a1e6fc93266faf6dc2a97eecdbc7eae289628022/pyrefly-1.0.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:a7038efc3a40f8294edee339895633cf22db268c0d434cdbcbefc34f78a9ecc3", size = 12599494, upload-time = "2026-05-12T20:12:23.495Z" },
|
|
144
|
-
{ url = "https://files.pythonhosted.org/packages/cd/ab/74d1e11e737e99b1c003ecc5d7d2e846c4ea1f328966bfdbbd0ac63fad0a/pyrefly-1.0.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da331ca515ed1c08791da2b5f664cf9c1294c48fd802133262e7d5d51e0f4416", size = 12995507, upload-time = "2026-05-12T20:12:25.951Z" },
|
|
145
|
-
{ url = "https://files.pythonhosted.org/packages/7c/ac/2df0899f8464c97e5d995f994c97c5cb5b0f58610432aa90d26d924e1db5/pyrefly-1.0.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c74219d8f3e63cdaa5501a0b21d1c9d37011820f9606728d0ed06f09ae86a878", size = 13947693, upload-time = "2026-05-12T20:12:29.188Z" },
|
|
146
|
-
{ url = "https://files.pythonhosted.org/packages/6b/3e/b247c24321e36f04b7d51f9ccf3df93e5009e4b29939524b36ec2e17dc2a/pyrefly-1.0.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c0d05543b1bb6ee6d64149eb5d6b2fb15aa72d3962d6a97abca0afaca8b0c131", size = 13925803, upload-time = "2026-05-12T20:12:31.904Z" },
|
|
147
|
-
{ url = "https://files.pythonhosted.org/packages/61/16/cfa2d61a4aa1e1f7bca48bb37acd01c6a09db4864b16a54f9587092765ff/pyrefly-1.0.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1382d5b1fcdb49a4de9f34d112d2bddf290a78ff93ee8149492ad5f1077ddffc", size = 13470398, upload-time = "2026-05-12T20:12:35.302Z" },
|
|
148
|
-
{ url = "https://files.pythonhosted.org/packages/cb/2b/6372c7dddb326223e24a46b17efd0d4bd7b4fe22c821e523157577eed2d2/pyrefly-1.0.0-py3-none-win32.whl", hash = "sha256:aa8b5d0e47080e3202a2547b39f7a5a61d2c781c712b3b67884f745ca2c759d2", size = 12222643, upload-time = "2026-05-12T20:12:38.618Z" },
|
|
149
|
-
{ url = "https://files.pythonhosted.org/packages/be/ad/1d23be700b6b2ddaeb362360c7145917a8edbbf7240ae428d40541772fce/pyrefly-1.0.0-py3-none-win_amd64.whl", hash = "sha256:c8abcb0f2082e83c890375128f9cff4aa4d3f210b85eea7b3046c1ae764e77f5", size = 13146369, upload-time = "2026-05-12T20:12:41.423Z" },
|
|
150
|
-
{ url = "https://files.pythonhosted.org/packages/8c/38/16589134f3012fd097a10dcc85771555f1a5fb76e04b682597180743af30/pyrefly-1.0.0-py3-none-win_arm64.whl", hash = "sha256:d150fa9e40e8392832be81c3bcfc0497c146674ce4d0f8e04e1ec29e775ffb8c", size = 12538326, upload-time = "2026-05-12T20:12:43.996Z" },
|
|
174
|
+
{ url = "https://files.pythonhosted.org/packages/0a/49/385be530a6a5b78d1cbcd5c2e38debc8959a2fc6bdb716f4e581002979fc/pyright-1.1.411-py3-none-any.whl", hash = "sha256:dc7c72a8e2700c55baa127554040e067041ea53ccfd50bf96308cc4291c7d5d9", size = 6181526, upload-time = "2026-06-25T02:14:04.691Z" },
|
|
151
175
|
]
|
|
152
176
|
|
|
153
177
|
[[package]]
|
|
@@ -189,6 +213,15 @@ wheels = [
|
|
|
189
213
|
{ url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" },
|
|
190
214
|
]
|
|
191
215
|
|
|
216
|
+
[[package]]
|
|
217
|
+
name = "randpal"
|
|
218
|
+
version = "0.5"
|
|
219
|
+
source = { registry = "https://pypi.org/simple" }
|
|
220
|
+
sdist = { url = "https://files.pythonhosted.org/packages/02/10/41011d552baa59a6990424a6bf2d00b3fbbe881b98a301263967d0467fa2/randpal-0.5.tar.gz", hash = "sha256:178e85ef26ea3e322d86cf9b44ad0d2210e6cb871444548d14e896d6d1e6c395", size = 30281, upload-time = "2026-09-04T14:43:57.251Z" }
|
|
221
|
+
wheels = [
|
|
222
|
+
{ url = "https://files.pythonhosted.org/packages/03/62/22326f19d18c34a6064ac29bf662950a06ff3e0c3cd6f667531ee08df931/randpal-0.5-py3-none-any.whl", hash = "sha256:2718ae64d05b51af2e01408e54c15171a3d467ab4655f3794976032a8705b574", size = 9099, upload-time = "2026-09-04T14:43:56.325Z" },
|
|
223
|
+
]
|
|
224
|
+
|
|
192
225
|
[[package]]
|
|
193
226
|
name = "ruff"
|
|
194
227
|
version = "0.15.15"
|
|
@@ -214,6 +247,15 @@ wheels = [
|
|
|
214
247
|
{ url = "https://files.pythonhosted.org/packages/4e/b2/920464c907b191e37469d477a1aa8bc048b8f36c4c1610dfa4ab87b39e18/ruff-0.15.15-py3-none-win_arm64.whl", hash = "sha256:3c8ceca6792f38196b8f589bc92eccd03eef286602da92e5dc05cc42ef6441b7", size = 11138498, upload-time = "2026-05-28T14:16:38.425Z" },
|
|
215
248
|
]
|
|
216
249
|
|
|
250
|
+
[[package]]
|
|
251
|
+
name = "typing-extensions"
|
|
252
|
+
version = "4.16.0"
|
|
253
|
+
source = { registry = "https://pypi.org/simple" }
|
|
254
|
+
sdist = { url = "https://files.pythonhosted.org/packages/f6/cc/6253133b5bb138fc3306cebfbda2c520f545d36b5be2c7255cc528bb45d6/typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5", size = 113555, upload-time = "2026-07-02T08:40:05.92Z" }
|
|
255
|
+
wheels = [
|
|
256
|
+
{ url = "https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8", size = 45571, upload-time = "2026-07-02T08:40:04.659Z" },
|
|
257
|
+
]
|
|
258
|
+
|
|
217
259
|
[[package]]
|
|
218
260
|
name = "virtualenv"
|
|
219
261
|
version = "21.4.1"
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
repos:
|
|
2
|
-
# Ruff hooks
|
|
3
|
-
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
4
|
-
rev: v0.15.5
|
|
5
|
-
hooks:
|
|
6
|
-
# Run the linter
|
|
7
|
-
- id: ruff-check
|
|
8
|
-
args: [--fix]
|
|
9
|
-
# Run the formatter
|
|
10
|
-
- id: ruff-format
|
|
11
|
-
|
|
12
|
-
# Pyrefly hook
|
|
13
|
-
- repo: local
|
|
14
|
-
hooks:
|
|
15
|
-
- id: pyrefly-check
|
|
16
|
-
name: Pyrefly (type checking via uvx)
|
|
17
|
-
entry: uv tool run --from pyrefly pyrefly check
|
|
18
|
-
language: system
|
|
19
|
-
pass_filenames: false
|
passmaker-0.1.1/PKG-INFO
DELETED
|
File without changes
|
|
@@ -1,211 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import contextlib
|
|
4
|
-
import functools
|
|
5
|
-
import secrets
|
|
6
|
-
import string
|
|
7
|
-
from collections.abc import Callable
|
|
8
|
-
from typing import TYPE_CHECKING
|
|
9
|
-
|
|
10
|
-
import click
|
|
11
|
-
from click_option_group import optgroup
|
|
12
|
-
|
|
13
|
-
if TYPE_CHECKING:
|
|
14
|
-
from collections.abc import Sequence
|
|
15
|
-
|
|
16
|
-
# Rule is a filter to an alphabet
|
|
17
|
-
type Rule = Callable[[str, set[str]], set[str]]
|
|
18
|
-
|
|
19
|
-
QWERTY: str = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM"
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
def no_duplicates(password: str, alphabet: set[str]) -> set[str]:
|
|
23
|
-
"""Don't use the same character more than once in a password.
|
|
24
|
-
|
|
25
|
-
This rule removes from alphabet every char from password."""
|
|
26
|
-
if not password:
|
|
27
|
-
return alphabet
|
|
28
|
-
|
|
29
|
-
return alphabet - set(password)
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
def no_sequence_characters(password: str, alphabet: set[str]) -> set[str]:
|
|
33
|
-
"""Don't use characters in password in a sequence, e.g. abc, 123, etc.
|
|
34
|
-
|
|
35
|
-
This rule enforces that the next symbol in password cannot be from
|
|
36
|
-
sequence, thus it's removed from alphabet."""
|
|
37
|
-
|
|
38
|
-
def _get_char(string: str, idx: int) -> str:
|
|
39
|
-
try:
|
|
40
|
-
return string[idx]
|
|
41
|
-
except IndexError:
|
|
42
|
-
return ""
|
|
43
|
-
|
|
44
|
-
if not password:
|
|
45
|
-
return alphabet
|
|
46
|
-
|
|
47
|
-
with contextlib.suppress(ValueError):
|
|
48
|
-
index = QWERTY.index(password[-1])
|
|
49
|
-
|
|
50
|
-
alphabet -= {
|
|
51
|
-
_get_char(QWERTY, index - 1),
|
|
52
|
-
_get_char(QWERTY, index + 1),
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
return alphabet - {chr(ord(password[-1]) - 1), chr(ord(password[-1]) + 1)}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
def no_similar_characters(password: str, alphabet: set[str]) -> set[str]:
|
|
59
|
-
"""Don't use similar characters in password, e.g. i, I, L, l, 1, etc.
|
|
60
|
-
|
|
61
|
-
This rule removes from alphabet these characters: i1Il|, oO0, `.:"""
|
|
62
|
-
return alphabet - set("i1Il|oO0`.:")
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
all_rules = [no_duplicates, no_sequence_characters, no_similar_characters]
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
def build_alphabet(
|
|
69
|
-
*,
|
|
70
|
-
digits: bool = True,
|
|
71
|
-
lowercase: bool = True,
|
|
72
|
-
uppercase: bool = True,
|
|
73
|
-
symbols: bool | str = True,
|
|
74
|
-
) -> str:
|
|
75
|
-
alphabet = ""
|
|
76
|
-
|
|
77
|
-
if digits:
|
|
78
|
-
alphabet += string.digits
|
|
79
|
-
if lowercase:
|
|
80
|
-
alphabet += string.ascii_lowercase
|
|
81
|
-
if uppercase:
|
|
82
|
-
alphabet += string.ascii_uppercase
|
|
83
|
-
if symbols:
|
|
84
|
-
alphabet += (
|
|
85
|
-
symbols if isinstance(symbols, str) else string.punctuation
|
|
86
|
-
)
|
|
87
|
-
|
|
88
|
-
return alphabet
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
def generate_password(
|
|
92
|
-
length: int,
|
|
93
|
-
alphabet: str | None = None,
|
|
94
|
-
rules: Sequence[Rule] = all_rules,
|
|
95
|
-
) -> str:
|
|
96
|
-
if alphabet is None:
|
|
97
|
-
alphabet = build_alphabet()
|
|
98
|
-
|
|
99
|
-
password = ""
|
|
100
|
-
|
|
101
|
-
for _ in range(length):
|
|
102
|
-
allowed_symbols = functools.reduce(
|
|
103
|
-
lambda alpha, rule: rule(password, alpha), rules, set(alphabet)
|
|
104
|
-
)
|
|
105
|
-
|
|
106
|
-
password += secrets.choice(list(allowed_symbols))
|
|
107
|
-
|
|
108
|
-
return password
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
@click.command()
|
|
112
|
-
@click.option(
|
|
113
|
-
"-l", "--length", default=22, type=int, help="Length of each password."
|
|
114
|
-
)
|
|
115
|
-
@click.option(
|
|
116
|
-
"-c",
|
|
117
|
-
"--count",
|
|
118
|
-
default=1,
|
|
119
|
-
type=int,
|
|
120
|
-
help="Count of passwords to generate in one take.",
|
|
121
|
-
)
|
|
122
|
-
@optgroup.group(
|
|
123
|
-
"Password alphabet",
|
|
124
|
-
help="Options controlling what set of symbols can be used in a password.",
|
|
125
|
-
)
|
|
126
|
-
@optgroup.option(
|
|
127
|
-
"-d/-D",
|
|
128
|
-
"--digits/--no-digits",
|
|
129
|
-
"use_digits",
|
|
130
|
-
default=True,
|
|
131
|
-
help="Where to use digits in password or no (default: yes)",
|
|
132
|
-
)
|
|
133
|
-
@optgroup.option(
|
|
134
|
-
"-a/-A",
|
|
135
|
-
"--alpha/--no-alpha",
|
|
136
|
-
"use_alpha",
|
|
137
|
-
default=True,
|
|
138
|
-
help="Where to use ASCII alphabet symbols in password or no "
|
|
139
|
-
"(default: yes)",
|
|
140
|
-
)
|
|
141
|
-
@optgroup.option(
|
|
142
|
-
"-S",
|
|
143
|
-
"--no-symbols",
|
|
144
|
-
default=False,
|
|
145
|
-
help="Don't use punctuation symbols for password generation "
|
|
146
|
-
"(overrides 'symbols' option).",
|
|
147
|
-
)
|
|
148
|
-
@optgroup.option(
|
|
149
|
-
"-s",
|
|
150
|
-
"--symbols",
|
|
151
|
-
type=str,
|
|
152
|
-
help="Use these exact symbols for password generation.",
|
|
153
|
-
)
|
|
154
|
-
@optgroup.group(
|
|
155
|
-
"Generation rules", help="Set of rules to control password generation."
|
|
156
|
-
)
|
|
157
|
-
@optgroup.option(
|
|
158
|
-
"--no-duplicates/--duplicates",
|
|
159
|
-
"use_duplicates",
|
|
160
|
-
default=True,
|
|
161
|
-
help="Allow or disallow duplicate characters.",
|
|
162
|
-
)
|
|
163
|
-
@optgroup.option(
|
|
164
|
-
"--no-sequence/--sequence",
|
|
165
|
-
"use_sequence",
|
|
166
|
-
default=True,
|
|
167
|
-
help="Allow or disallow sequential characters.",
|
|
168
|
-
)
|
|
169
|
-
@optgroup.option(
|
|
170
|
-
"--no-similar/--similar",
|
|
171
|
-
"use_similar",
|
|
172
|
-
default=True,
|
|
173
|
-
help="Allow or disallow similar looking characters.",
|
|
174
|
-
)
|
|
175
|
-
def main( # noqa: PLR0913
|
|
176
|
-
length: int,
|
|
177
|
-
count: int,
|
|
178
|
-
*,
|
|
179
|
-
use_digits: bool,
|
|
180
|
-
use_alpha: bool,
|
|
181
|
-
symbols: str,
|
|
182
|
-
no_symbols: bool,
|
|
183
|
-
use_duplicates: bool,
|
|
184
|
-
use_sequence: bool,
|
|
185
|
-
use_similar: bool,
|
|
186
|
-
) -> None:
|
|
187
|
-
"""Simple password generator. Safe to use, generates quite strong
|
|
188
|
-
passwords.
|
|
189
|
-
"""
|
|
190
|
-
alphabet = build_alphabet(
|
|
191
|
-
digits=use_digits,
|
|
192
|
-
lowercase=use_alpha,
|
|
193
|
-
uppercase=use_alpha,
|
|
194
|
-
symbols=False if no_symbols else symbols or True,
|
|
195
|
-
)
|
|
196
|
-
|
|
197
|
-
rules: list[Rule] = []
|
|
198
|
-
|
|
199
|
-
if use_duplicates:
|
|
200
|
-
rules.append(no_duplicates)
|
|
201
|
-
if use_sequence:
|
|
202
|
-
rules.append(no_sequence_characters)
|
|
203
|
-
if use_similar:
|
|
204
|
-
rules.append(no_similar_characters)
|
|
205
|
-
|
|
206
|
-
for _ in range(count):
|
|
207
|
-
print(generate_password(length, alphabet=alphabet, rules=rules)) # noqa: T201
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
if __name__ == "__main__":
|
|
211
|
-
main()
|
|
File without changes
|
|
File without changes
|