From 68ab293d8a111b9f8bea6f878b37a64ef07babd7 Mon Sep 17 00:00:00 2001 From: jackbrett Date: Fri, 24 Jan 2025 09:49:54 -0600 Subject: [PATCH 01/15] add tox.ini, add python versions to test --- .github/workflows/build.yml | 58 +++++++++++++++++++++++-------------- tox.ini | 10 +++++++ 2 files changed, 47 insertions(+), 21 deletions(-) create mode 100644 tox.ini diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 59aaa6b..e24ac45 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,27 +7,43 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [2.7, 3.6, 3.7, 3.8, 3.9] + python-version: + [ + "3.0.1", + "3.1.4", + "3.2.5", + "3.4.10", + "3.5.10", + "3.6.15", + "3.7.17", + "3.8.18", + "3.9.21", + "3.10.16", + "3.11.11", + "3.12.8", + "3.13.0", + ] steps: - - uses: actions/checkout@master - - name: set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -U setuptools - pip install -r requirements.txt - pip install . - - name: Run mypy - if: "matrix.python-version != '2.7' && matrix.python-version != 'pypy2'" - run: | + - uses: actions/checkout@master + - name: set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U setuptools + pip install -r requirements.txt + pip install . + pip install tox + - name: Run mypy + if: "matrix.python-version != '2.7' && matrix.python-version != 'pypy2'" + run: | pip install -U mypy mypy -p zxcvbn --ignore-missing-imports - - name: Run tests - run: | - pytest -v - - name: Test Compatibility - run: | - python tests/test_compatibility.py tests/password_expected_value.json + - name: Run tests + run: | + pytest -v + - name: Test Compatibility + run: | + tox diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..3392a84 --- /dev/null +++ b/tox.ini @@ -0,0 +1,10 @@ +[tox] +envlist = py37, py38, py39, py310, py311 +isolated_build = True + +[testenv] +deps = + pytest +commands = + pytest + python tests/test_compatibility.py tests/password_expected_value.json From 699eeda97d81ade3a6c36d1163a950365e327a9d Mon Sep 17 00:00:00 2001 From: jackbrett Date: Fri, 24 Jan 2025 09:58:15 -0600 Subject: [PATCH 02/15] try v5 of setup-python gha --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e24ac45..5e86f41 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,7 +26,7 @@ jobs: steps: - uses: actions/checkout@master - name: set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Install dependencies From c505a517fd1549ca0eb0f53449a9442068e3d476 Mon Sep 17 00:00:00 2001 From: jackbrett Date: Fri, 24 Jan 2025 10:06:07 -0600 Subject: [PATCH 03/15] try dropping python versions older than 3.6 --- .github/workflows/build.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5e86f41..b4296d6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -9,11 +9,6 @@ jobs: matrix: python-version: [ - "3.0.1", - "3.1.4", - "3.2.5", - "3.4.10", - "3.5.10", "3.6.15", "3.7.17", "3.8.18", From c88af4444861694324047911a279213dca16314a Mon Sep 17 00:00:00 2001 From: jackbrett Date: Fri, 24 Jan 2025 10:18:59 -0600 Subject: [PATCH 04/15] update build.yml with python versions supported by Ubuntu 24.04 --- .github/workflows/build.yml | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b4296d6..2c7c39d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,17 +7,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: - [ - "3.6.15", - "3.7.17", - "3.8.18", - "3.9.21", - "3.10.16", - "3.11.11", - "3.12.8", - "3.13.0", - ] + python-version: ["3.8.18", "3.9.21", "3.10.16", "3.11.11"] steps: - uses: actions/checkout@master - name: set up Python ${{ matrix.python-version }} From 6734d06686f9c3f53e3c9693979e06f06567a4aa Mon Sep 17 00:00:00 2001 From: jackbrett Date: Fri, 24 Jan 2025 10:28:24 -0600 Subject: [PATCH 05/15] try python version 3.8.18 by itself --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2c7c39d..8e2b2a4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.8.18", "3.9.21", "3.10.16", "3.11.11"] + python-version: ["3.8.18"] steps: - uses: actions/checkout@master - name: set up Python ${{ matrix.python-version }} From 8b8e53ae0bff68a3d1ac3369ad5ee2d892973425 Mon Sep 17 00:00:00 2001 From: jackbrett Date: Fri, 24 Jan 2025 10:57:51 -0600 Subject: [PATCH 06/15] let tox control pytest version --- requirements.txt | 1 - tox.ini | 6 ++++-- 2 files changed, 4 insertions(+), 3 deletions(-) delete mode 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index e0fa86f..0000000 --- a/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -pytest==3.5.0 diff --git a/tox.ini b/tox.ini index 3392a84..f2094e8 100644 --- a/tox.ini +++ b/tox.ini @@ -1,10 +1,12 @@ [tox] -envlist = py37, py38, py39, py310, py311 +envlist = py38 isolated_build = True [testenv] deps = - pytest + # Vary Pytest by Python version: + pytest==3.5.0: python_version < '3.8' + pytest==7.4.2: python_version >= '3.8' commands = pytest python tests/test_compatibility.py tests/password_expected_value.json From bd63736695b06bbaf663bc8119099d3b8468643e Mon Sep 17 00:00:00 2001 From: jackbrett Date: Fri, 24 Jan 2025 10:59:05 -0600 Subject: [PATCH 07/15] rm reference to requirements.txt --- .github/workflows/build.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8e2b2a4..3f46639 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,7 +18,6 @@ jobs: run: | python -m pip install --upgrade pip pip install -U setuptools - pip install -r requirements.txt pip install . pip install tox - name: Run mypy From 909084c1f3dcf9ac9241cd47853a7780f3d5932e Mon Sep 17 00:00:00 2001 From: jackbrett Date: Fri, 24 Jan 2025 11:05:34 -0600 Subject: [PATCH 08/15] tweak tox config --- .github/workflows/build.yml | 3 --- requirements.txt | 5 +++++ tox.ini | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) create mode 100644 requirements.txt diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3f46639..787458c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,8 +26,5 @@ jobs: pip install -U mypy mypy -p zxcvbn --ignore-missing-imports - name: Run tests - run: | - pytest -v - - name: Test Compatibility run: | tox diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..dfbc44f --- /dev/null +++ b/requirements.txt @@ -0,0 +1,5 @@ +# For older Python versions < 3.6 (example threshold), install an older Pytest: +pytest==3.5.0; python_version < "3.6" + +# For Python 3.6+ or 3.8+, install a more modern Pytest: +pytest==7.4.2; python_version >= "3.6" diff --git a/tox.ini b/tox.ini index f2094e8..42ce9db 100644 --- a/tox.ini +++ b/tox.ini @@ -4,7 +4,7 @@ isolated_build = True [testenv] deps = - # Vary Pytest by Python version: + # Then vary Pytest by Python version: pytest==3.5.0: python_version < '3.8' pytest==7.4.2: python_version >= '3.8' commands = From b27c7ab1a7a0e7625acfa93dea2724881e2b65cc Mon Sep 17 00:00:00 2001 From: jackbrett Date: Fri, 24 Jan 2025 11:08:11 -0600 Subject: [PATCH 09/15] trying another tox config --- .github/workflows/build.yml | 1 + tox.ini | 4 +--- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 787458c..3c0c01d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,6 +18,7 @@ jobs: run: | python -m pip install --upgrade pip pip install -U setuptools + pip install -r requirements.txt pip install . pip install tox - name: Run mypy diff --git a/tox.ini b/tox.ini index 42ce9db..8f5c205 100644 --- a/tox.ini +++ b/tox.ini @@ -4,9 +4,7 @@ isolated_build = True [testenv] deps = - # Then vary Pytest by Python version: - pytest==3.5.0: python_version < '3.8' - pytest==7.4.2: python_version >= '3.8' + pytest commands = pytest python tests/test_compatibility.py tests/password_expected_value.json From dffa53bd5763364b0fa121b7c0034fa5d50494ed Mon Sep 17 00:00:00 2001 From: jackbrett Date: Fri, 24 Jan 2025 11:21:58 -0600 Subject: [PATCH 10/15] add py versions 3.9, 3.10, 3.11 --- .github/workflows/build.yml | 2 +- requirements.txt | 4 ++-- tox.ini | 3 +-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3c0c01d..e7adf97 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.8.18"] + python-version: ["3.8.18", "3.9.21", "3.10.16", "3.11.11"] steps: - uses: actions/checkout@master - name: set up Python ${{ matrix.python-version }} diff --git a/requirements.txt b/requirements.txt index dfbc44f..5ac1407 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ -# For older Python versions < 3.6 (example threshold), install an older Pytest: +# For older Python versions < 3.6 install Pytest 3.5.0 pytest==3.5.0; python_version < "3.6" -# For Python 3.6+ or 3.8+, install a more modern Pytest: +# For Python 3.6+, install a more modern Pytest: pytest==7.4.2; python_version >= "3.6" diff --git a/tox.ini b/tox.ini index 8f5c205..2ce0781 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py38 +envlist = py38, py39, py310, py311 isolated_build = True [testenv] @@ -7,4 +7,3 @@ deps = pytest commands = pytest - python tests/test_compatibility.py tests/password_expected_value.json From 3c4e3ae2e24965daffb3acc30afa0852825a0461 Mon Sep 17 00:00:00 2001 From: jackbrett Date: Fri, 24 Jan 2025 15:02:48 -0600 Subject: [PATCH 11/15] add py versions 3.12 and 3.13 --- .github/workflows/build.yml | 3 ++- tox.ini | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e7adf97..b2673ba 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,7 +7,8 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.8.18", "3.9.21", "3.10.16", "3.11.11"] + python-version: + ["3.8.18", "3.9.21", "3.10.16", "3.11.11", "3.12.8", "3.13.1"] steps: - uses: actions/checkout@master - name: set up Python ${{ matrix.python-version }} diff --git a/tox.ini b/tox.ini index 2ce0781..f73fe62 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py38, py39, py310, py311 +envlist = py38, py39, py310, py311, py312, py313 isolated_build = True [testenv] @@ -7,3 +7,4 @@ deps = pytest commands = pytest + python tests/test_compatibility.py tests/password_expected_value.json From ba2525d6479fd99a95eb19bc147c1acaacb4a5d9 Mon Sep 17 00:00:00 2001 From: jackbrett Date: Tue, 28 Jan 2025 12:45:59 -0600 Subject: [PATCH 12/15] remove python 2 condition from mypy job --- .github/workflows/build.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b2673ba..123eaf8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -23,7 +23,6 @@ jobs: pip install . pip install tox - name: Run mypy - if: "matrix.python-version != '2.7' && matrix.python-version != 'pypy2'" run: | pip install -U mypy mypy -p zxcvbn --ignore-missing-imports From 276f719c8e6de754d5501fffb0f2d54d908147a7 Mon Sep 17 00:00:00 2001 From: jackbrett Date: Wed, 5 Feb 2025 13:19:19 -0600 Subject: [PATCH 13/15] decorator solution for lazy loading frequency_lists library --- tests/matching_test.py | 18 ++------- tests/test_compatibility.py | 7 ++-- zxcvbn/__init__.py | 5 +-- zxcvbn/matching.py | 76 ++++++++++++++++++++++++++----------- 4 files changed, 61 insertions(+), 45 deletions(-) diff --git a/tests/matching_test.py b/tests/matching_test.py index 6ca0ff3..6db0279 100644 --- a/tests/matching_test.py +++ b/tests/matching_test.py @@ -67,18 +67,6 @@ def test_build_ranked_dict(): } -def test_add_frequency_lists(): - matching.add_frequency_lists({ - 'test_words': ['qidkviflkdoejjfkd', 'sjdshfidssdkdjdhfkl'] - }) - - assert 'test_words' in matching.RANKED_DICTIONARIES - assert matching.RANKED_DICTIONARIES['test_words'] == { - 'qidkviflkdoejjfkd': 1, - 'sjdshfidssdkdjdhfkl': 2, - } - - def test_matching_utils(): chr_map = { 'a': 'A', @@ -102,7 +90,7 @@ def test_matching_utils(): def test_dictionary_matching(): def dm(pw): - return matching.dictionary_match(pw, test_dicts) + return matching.dictionary_match(pw, _ranked_dictionaries=test_dicts) test_dicts = { 'd1': { @@ -196,7 +184,7 @@ def test_reverse_dictionary_matching(): } } password = '0123456789' - matches = matching.reverse_dictionary_match(password, test_dicts) + matches = matching.reverse_dictionary_match(password, _ranked_dictionaries=test_dicts) msg = 'matches against reversed words' check_matches(msg, matches, 'dictionary', ['123', '456'], [[1, 3], [4, 6]], { @@ -236,7 +224,7 @@ def test_l33t_matching(): assert matching.enumerate_l33t_subs(table) == subs, msg def lm(pw): - return matching.l33t_match(pw, dicts, test_table) + return matching.l33t_match(pw, _ranked_dictionaries=dicts, _l33t_table=test_table) dicts = { 'words': { diff --git a/tests/test_compatibility.py b/tests/test_compatibility.py index 81dc202..5e15358 100644 --- a/tests/test_compatibility.py +++ b/tests/test_compatibility.py @@ -42,14 +42,13 @@ def main(argv): number_of_passwords = len(d) scores_collision = 0 guesses_collision = 0 - refresh_rate = number_of_passwords/100 + refresh_rate = number_of_passwords // 100 i = 0 for js_zxcvbn_score in d: if i%refresh_rate== 0: update_console_status(i*100/number_of_passwords) i += 1 - py_zxcvbn_scroe = dict() py_zxcvbn_scroe_full = zxcvbn(js_zxcvbn_score['password']) py_zxcvbn_scroe["password"] = py_zxcvbn_scroe_full["password"] @@ -64,7 +63,7 @@ def main(argv): expected: %s results: -%s\033[00m""")%(js_zxcvbn_score, py_zxcvbn_scroe) +%s\033[00m""" % (js_zxcvbn_score, py_zxcvbn_scroe)) if py_zxcvbn_scroe["score"] != js_zxcvbn_score["score"]: scores_collision += 1 @@ -72,7 +71,7 @@ def main(argv): if (guesses_collision or scores_collision): print ("""\033[91mFailed! guesses_collision:%d -guesses_score:%d""")%(guesses_collision, scores_collision) +guesses_score:%d""" % (guesses_collision, scores_collision)) else: print ("\033[92mPassed!") diff --git a/zxcvbn/__init__.py b/zxcvbn/__init__.py index d820e6b..204ac4a 100644 --- a/zxcvbn/__init__.py +++ b/zxcvbn/__init__.py @@ -21,10 +21,7 @@ def zxcvbn(password, user_inputs=None): arg = str(arg) sanitized_inputs.append(arg.lower()) - ranked_dictionaries = matching.RANKED_DICTIONARIES - ranked_dictionaries['user_inputs'] = matching.build_ranked_dict(sanitized_inputs) - - matches = matching.omnimatch(password, ranked_dictionaries) + matches = matching.omnimatch(password, user_inputs=sanitized_inputs) result = scoring.most_guessable_match_sequence(password, matches) result['calc_time'] = datetime.now() - start diff --git a/zxcvbn/matching.py b/zxcvbn/matching.py index a92d6ea..e211ab3 100644 --- a/zxcvbn/matching.py +++ b/zxcvbn/matching.py @@ -1,7 +1,7 @@ from zxcvbn import scoring from . import adjacency_graphs -from zxcvbn.frequency_lists import FREQUENCY_LISTS import re +import functools from zxcvbn.scoring import most_guessable_match_sequence @@ -9,15 +9,36 @@ def build_ranked_dict(ordered_list): return {word: idx for idx, word in enumerate(ordered_list, 1)} -RANKED_DICTIONARIES = {} - - -def add_frequency_lists(frequency_lists_): - for name, lst in frequency_lists_.items(): - RANKED_DICTIONARIES[name] = build_ranked_dict(lst) - - -add_frequency_lists(FREQUENCY_LISTS) +RANKED_DICTIONARIES = None + +def get_ranked_dictionaries(): + """ + Lazy-load large dictionary data set. + Return global _RANKED_DICTIONARIES, ensuring it is built only once. + """ + global RANKED_DICTIONARIES + + if RANKED_DICTIONARIES is None: + # Do the expensive import here only + from zxcvbn.frequency_lists import FREQUENCY_LISTS + + # Build the dictionary once + RANKED_DICTIONARIES = {} + for name, lst in FREQUENCY_LISTS.items(): + RANKED_DICTIONARIES[name] = build_ranked_dict(lst) + return RANKED_DICTIONARIES + + +def ensure_ranked_dictionaries(func): + """Decorator to ensure _ranked_dictionaries argument is always populated.""" + @functools.wraps(func) + def wrapper(*args, **kwargs): + # If an explicit _ranked_dictionaries arg was passed, use it. + # Otherwise fetch from the global cache. + if '_ranked_dictionaries' not in kwargs or kwargs['_ranked_dictionaries'] is None: + kwargs['_ranked_dictionaries'] = get_ranked_dictionaries() + return func(*args, **kwargs) + return wrapper GRAPHS = { 'qwerty': adjacency_graphs.ADJACENCY_GRAPHS['qwerty'], @@ -75,7 +96,11 @@ def add_frequency_lists(frequency_lists_): # omnimatch -- perform all matches -def omnimatch(password, _ranked_dictionaries=RANKED_DICTIONARIES): +@ensure_ranked_dictionaries +def omnimatch(password, _ranked_dictionaries=None, user_inputs=[]): + if len(user_inputs): + _ranked_dictionaries['user_inputs'] = build_ranked_dict(user_inputs) + matches = [] for matcher in [ dictionary_match, @@ -93,7 +118,8 @@ def omnimatch(password, _ranked_dictionaries=RANKED_DICTIONARIES): # dictionary match (common passwords, english, last names, etc) -def dictionary_match(password, _ranked_dictionaries=RANKED_DICTIONARIES): +@ensure_ranked_dictionaries +def dictionary_match(password, _ranked_dictionaries=None): matches = [] length = len(password) password_lower = password.lower() @@ -117,11 +143,11 @@ def dictionary_match(password, _ranked_dictionaries=RANKED_DICTIONARIES): return sorted(matches, key=lambda x: (x['i'], x['j'])) - +@ensure_ranked_dictionaries def reverse_dictionary_match(password, - _ranked_dictionaries=RANKED_DICTIONARIES): + _ranked_dictionaries=None): reversed_password = ''.join(reversed(password)) - matches = dictionary_match(reversed_password, _ranked_dictionaries) + matches = dictionary_match(reversed_password, _ranked_dictionaries=_ranked_dictionaries) for match in matches: match['token'] = ''.join(reversed(match['token'])) match['reversed'] = True @@ -212,7 +238,8 @@ def translate(string, chr_map): return ''.join(chars) -def l33t_match(password, _ranked_dictionaries=RANKED_DICTIONARIES, +@ensure_ranked_dictionaries +def l33t_match(password, _ranked_dictionaries=None, _l33t_table=L33T_TABLE): matches = [] @@ -222,7 +249,7 @@ def l33t_match(password, _ranked_dictionaries=RANKED_DICTIONARIES, break subbed_password = translate(password, sub) - for match in dictionary_match(subbed_password, _ranked_dictionaries): + for match in dictionary_match(subbed_password, _ranked_dictionaries=_ranked_dictionaries): token = password[match['i']:match['j'] + 1] if token.lower() == match['matched_word']: # only return the matches that contain an actual substitution @@ -247,7 +274,8 @@ def l33t_match(password, _ranked_dictionaries=RANKED_DICTIONARIES, # repeats (aaa, abcabcabc) and sequences (abcdef) -def repeat_match(password, _ranked_dictionaries=RANKED_DICTIONARIES): +@ensure_ranked_dictionaries +def repeat_match(password, _ranked_dictionaries=None): matches = [] greedy = re.compile(r'(.+)\1+') lazy = re.compile(r'(.+?)\1+') @@ -298,7 +326,8 @@ def repeat_match(password, _ranked_dictionaries=RANKED_DICTIONARIES): return matches -def spatial_match(password, _graphs=GRAPHS, _ranked_dictionaries=RANKED_DICTIONARIES): +@ensure_ranked_dictionaries +def spatial_match(password, _graphs=GRAPHS, _ranked_dictionaries=None): matches = [] for graph_name, graph in _graphs.items(): matches.extend(spatial_match_helper(password, graph, graph_name)) @@ -379,7 +408,8 @@ def spatial_match_helper(password, graph, graph_name): MAX_DELTA = 5 -def sequence_match(password, _ranked_dictionaries=RANKED_DICTIONARIES): +@ensure_ranked_dictionaries +def sequence_match(password, _ranked_dictionaries=None): # Identifies sequences by looking for repeated differences in unicode codepoint. # this allows skipping, such as 9753, and also matches some extended unicode sequences # such as Greek and Cyrillic alphabets. @@ -440,7 +470,8 @@ def update(i, j, delta): return result -def regex_match(password, _regexen=REGEXEN, _ranked_dictionaries=RANKED_DICTIONARIES): +@ensure_ranked_dictionaries +def regex_match(password, _regexen=REGEXEN, _ranked_dictionaries=None): matches = [] for name, regex in _regexen.items(): for rx_match in regex.finditer(password): @@ -456,7 +487,8 @@ def regex_match(password, _regexen=REGEXEN, _ranked_dictionaries=RANKED_DICTIONA return sorted(matches, key=lambda x: (x['i'], x['j'])) -def date_match(password, _ranked_dictionaries=RANKED_DICTIONARIES): +@ensure_ranked_dictionaries +def date_match(password, _ranked_dictionaries=None): # a "date" is recognized as: # any 3-tuple that starts or ends with a 2- or 4-digit year, # with 2 or 0 separator chars (1.1.91 or 1191), From d9a05ad1fd96dbf0077cc0e7d119208700bf3d2f Mon Sep 17 00:00:00 2001 From: jackbrett Date: Tue, 18 Feb 2025 10:22:30 -0600 Subject: [PATCH 14/15] update README w/ tested py versions, try 3.8.* as test version --- .github/workflows/build.yml | 2 +- README.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 123eaf8..5758e1e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,7 +8,7 @@ jobs: strategy: matrix: python-version: - ["3.8.18", "3.9.21", "3.10.16", "3.11.11", "3.12.8", "3.13.1"] + ["3.8.*", "3.9.21", "3.10.16", "3.11.11", "3.12.8", "3.13.1"] steps: - uses: actions/checkout@master - name: set up Python ${{ matrix.python-version }} diff --git a/README.rst b/README.rst index fb3d411..b41a3af 100644 --- a/README.rst +++ b/README.rst @@ -16,7 +16,7 @@ time. Features -------- -- **Tested in Python versions 2.7, 3.6-3.9** +- **Tested in Python versions 3.8-3.13** - Accepts user data to be added to the dictionaries that are tested against (name, birthdate, etc) - Gives a score to the password, from 0 (terrible) to 4 (great) - Provides feedback on the password and ways to improve it From 9750eadcbc04929bc336d4142514d96eb6cd84d6 Mon Sep 17 00:00:00 2001 From: jackbrett Date: Tue, 18 Feb 2025 10:24:29 -0600 Subject: [PATCH 15/15] fuzzy match all py test versions --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5758e1e..f9ec440 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,7 +8,7 @@ jobs: strategy: matrix: python-version: - ["3.8.*", "3.9.21", "3.10.16", "3.11.11", "3.12.8", "3.13.1"] + ["3.8.*", "3.9.*", "3.10.*", "3.11.*", "3.12.*", "3.13.*"] steps: - uses: actions/checkout@master - name: set up Python ${{ matrix.python-version }}