Skip to content

Commit f950e99

Browse files
Update legacycrypt usage and fix issues with updated linters. (#603)
* Use legacycrypt where crypt isn't available --------- Co-authored-by: Steve Kowalik <[email protected]>
1 parent 9c7e197 commit f950e99

File tree

11 files changed

+1123
-984
lines changed

11 files changed

+1123
-984
lines changed

.github/workflows/ci.yml

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ env:
99

1010
jobs:
1111
black:
12-
runs-on: "ubuntu-20.04"
12+
runs-on: "ubuntu-24.04"
1313
env:
1414
INVOKE_LOCAL: "True"
1515
steps:
@@ -18,11 +18,11 @@ jobs:
1818
- name: "Setup environment"
1919
uses: "networktocode/gh-action-setup-poetry-environment@v2"
2020
with:
21-
python-version: "3.12"
21+
python-version: "3.13"
2222
- name: "Linting: black"
2323
run: "poetry run invoke black"
2424
bandit:
25-
runs-on: "ubuntu-20.04"
25+
runs-on: "ubuntu-24.04"
2626
env:
2727
INVOKE_LOCAL: "True"
2828
steps:
@@ -31,13 +31,13 @@ jobs:
3131
- name: "Setup environment"
3232
uses: "networktocode/gh-action-setup-poetry-environment@v2"
3333
with:
34-
python-version: "3.12"
34+
python-version: "3.13"
3535
- name: "Linting: bandit"
3636
run: "poetry run invoke bandit"
3737
needs:
3838
- "black"
3939
mypy:
40-
runs-on: "ubuntu-20.04"
40+
runs-on: "ubuntu-24.04"
4141
env:
4242
INVOKE_LOCAL: "True"
4343
steps:
@@ -46,13 +46,13 @@ jobs:
4646
- name: "Setup environment"
4747
uses: "networktocode/gh-action-setup-poetry-environment@v2"
4848
with:
49-
python-version: "3.12"
49+
python-version: "3.13"
5050
- name: "Type-Hints: mypy"
5151
run: "poetry run invoke mypy"
5252
needs:
5353
- "black"
5454
pydocstyle:
55-
runs-on: "ubuntu-20.04"
55+
runs-on: "ubuntu-24.04"
5656
env:
5757
INVOKE_LOCAL: "True"
5858
steps:
@@ -61,13 +61,13 @@ jobs:
6161
- name: "Setup environment"
6262
uses: "networktocode/gh-action-setup-poetry-environment@v2"
6363
with:
64-
python-version: "3.12"
64+
python-version: "3.13"
6565
- name: "Linting: pydocstyle"
6666
run: "poetry run invoke pydocstyle"
6767
needs:
6868
- "black"
6969
flake8:
70-
runs-on: "ubuntu-20.04"
70+
runs-on: "ubuntu-24.04"
7171
env:
7272
INVOKE_LOCAL: "True"
7373
steps:
@@ -76,13 +76,13 @@ jobs:
7676
- name: "Setup environment"
7777
uses: "networktocode/gh-action-setup-poetry-environment@v2"
7878
with:
79-
python-version: "3.12"
79+
python-version: "3.13"
8080
- name: "Linting: flake8"
8181
run: "poetry run invoke flake8"
8282
needs:
8383
- "black"
8484
yamllint:
85-
runs-on: "ubuntu-20.04"
85+
runs-on: "ubuntu-24.04"
8686
env:
8787
INVOKE_LOCAL: "True"
8888
steps:
@@ -91,7 +91,7 @@ jobs:
9191
- name: "Setup environment"
9292
uses: "networktocode/gh-action-setup-poetry-environment@v2"
9393
with:
94-
python-version: "3.12"
94+
python-version: "3.13"
9595
- name: "Linting: yamllint"
9696
run: "poetry run invoke yamllint"
9797
needs:
@@ -100,8 +100,8 @@ jobs:
100100
strategy:
101101
fail-fast: true
102102
matrix:
103-
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
104-
runs-on: "ubuntu-20.04"
103+
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
104+
runs-on: "ubuntu-24.04"
105105
env:
106106
PYTHON_VER: "${{ matrix.python-version }}"
107107
steps:
@@ -133,11 +133,11 @@ jobs:
133133
- "flake8"
134134
- "yamllint"
135135
pylint:
136-
runs-on: "ubuntu-20.04"
136+
runs-on: "ubuntu-24.04"
137137
strategy:
138138
fail-fast: true
139139
matrix:
140-
python-version: ["3.12"]
140+
python-version: ["3.13"]
141141
env:
142142
PYTHON_VER: "${{ matrix.python-version }}"
143143
steps:
@@ -173,8 +173,8 @@ jobs:
173173
strategy:
174174
fail-fast: true
175175
matrix:
176-
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
177-
runs-on: "ubuntu-20.04"
176+
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
177+
runs-on: "ubuntu-24.04"
178178
env:
179179
PYTHON_VER: "${{ matrix.python-version }}"
180180
steps:
@@ -212,15 +212,15 @@ jobs:
212212
- "pylint"
213213
publish_gh:
214214
name: "Publish to GitHub"
215-
runs-on: "ubuntu-20.04"
215+
runs-on: "ubuntu-24.04"
216216
if: "startsWith(github.ref, 'refs/tags/v')"
217217
steps:
218218
- name: "Check out repository code"
219219
uses: "actions/checkout@v2"
220220
- name: "Set up Python"
221221
uses: "actions/setup-python@v2"
222222
with:
223-
python-version: "3.12"
223+
python-version: "3.13"
224224
- name: "Install Python Packages"
225225
run: "pip install poetry"
226226
- name: "Set env"
@@ -241,15 +241,15 @@ jobs:
241241
- "pytest"
242242
publish_pypi:
243243
name: "Push Package to PyPI"
244-
runs-on: "ubuntu-20.04"
244+
runs-on: "ubuntu-24.04"
245245
if: "startsWith(github.ref, 'refs/tags/v')"
246246
steps:
247247
- name: "Check out repository code"
248248
uses: "actions/checkout@v2"
249249
- name: "Set up Python"
250250
uses: "actions/setup-python@v2"
251251
with:
252-
python-version: "3.12"
252+
python-version: "3.13"
253253
- name: "Install Python Packages"
254254
run: "pip install poetry"
255255
- name: "Set env"

docs/dev/contributing.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
Pull requests are welcomed and automatically built and tested against multiple versions of Python through GitHub Actions.
44

5-
Except for unit tests, testing is only supported on Python 3.9.
5+
Except for unit tests, testing is only supported on Python 3.13.
66

77
The project is packaged with a light development environment based on `Docker` to help with the local development of the project and to run tests within GitHub Actions.
88

netutils/config/parser.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,7 @@ def find_children_w_parents(
417417
]
418418
for cfg_line in self.build_config_relationship():
419419
parents = cfg_line.parents[0] if cfg_line.parents else None
420-
if parents in potential_parents and self._match_type_check(parents, parent_pattern, match_type):
420+
if parents in potential_parents and self._match_type_check(parents, parent_pattern, match_type): # type: ignore[arg-type]
421421
config.append(cfg_line.config_line)
422422
return config
423423

netutils/ip.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,7 @@ def _convert_ip(ip: str) -> str:
311311
if is_ip(ip):
312312
if "." in ip:
313313
mask = "32"
314-
if ":" in ip:
314+
else:
315315
mask = "128"
316316
return f"{ip}/{mask}"
317317
return ip

netutils/nist.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,11 +74,13 @@ def _get_nist_urls_juniper_junos(os_platform_data: t.Dict[str, t.Any]) -> t.List
7474
# BASE
7575
_main = os_platform_data.get("main")
7676
_minor = os_platform_data.get("minor")
77+
_type = "" # Check if this is an issue as used below with potentially no definition
7778
if os_platform_data["type"]:
7879
_type = os_platform_data["type"].lower()
7980
_build = os_platform_data.get("build")
8081

8182
# SERVICE
83+
_service = "" # Check if this is an issue as used below with potentially no definition
8284
if os_platform_data["service"]:
8385
_service = os_platform_data["service"].lower()
8486
_service_build = os_platform_data.get("service_build")

netutils/os_version.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,12 @@ def get_upgrade_path(current_version: str, target_version: str, firmware_list: t
5050

5151
def _compare_version(current_version: str, comparison: str, target_version: str, version_type: str) -> bool:
5252
# Convert version strings to Version objects for comparison
53+
if version_type not in ["loose", "strict"]:
54+
raise ValueError(f"Invalid version type: {version_type}, must be 'loose' or 'strict'")
5355
if version_type == "loose":
5456
current_ver_obj = LooseVersion(current_version)
5557
target_ver_obj = LooseVersion(target_version)
56-
elif version_type == "strict":
58+
else:
5759
current_ver_obj = StrictVersion(current_version)
5860
target_ver_obj = StrictVersion(target_version)
5961

netutils/password.py

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
"""Functions for working with Passwords."""
22

3-
# TODO: Swap out crypt prior to py3.13
4-
import crypt # pylint: disable=deprecated-module
53
import random
64
import secrets
75
import string
@@ -19,6 +17,19 @@
1917
HAS_SCRYPT = False
2018

2119

20+
try:
21+
import crypt # pylint: disable=deprecated-module
22+
23+
HAS_CRYPT = True
24+
except ModuleNotFoundError:
25+
try:
26+
import legacycrypt as crypt
27+
28+
HAS_CRYPT = True
29+
except ModuleNotFoundError:
30+
HAS_CRYPT = False
31+
32+
2233
# Code example from Python docs
2334
ALPHABET = string.ascii_letters + string.digits
2435
DEFAULT_PASSWORD_CHARS = "".join((string.ascii_letters + string.digits + ".,:-_"))
@@ -128,9 +139,9 @@ def compare_cisco_type5(
128139
129140
Examples:
130141
>>> from netutils.password import compare_cisco_type5
131-
>>> compare_cisco_type5("cisco","$1$nTc1$Z28sUTcWfXlvVe2x.3XAa.")
142+
>>> compare_cisco_type5("cisco","$1$nTc1$Z28sUTcWfXlvVe2x.3XAa.") # doctest: +SKIP
132143
True
133-
>>> compare_cisco_type5("not_cisco","$1$nTc1$Z28sUTcWfXlvVe2x.3XAa.")
144+
>>> compare_cisco_type5("not_cisco","$1$nTc1$Z28sUTcWfXlvVe2x.3XAa.") # doctest: +SKIP
134145
False
135146
>>>
136147
"""
@@ -247,11 +258,18 @@ def encrypt_cisco_type5(unencrypted_password: str, salt: t.Optional[str] = None,
247258
'$1$MHkb$v2MFmDkQX66TTxLkFF50K/'
248259
>>>
249260
"""
261+
if not HAS_CRYPT:
262+
raise ImportError(
263+
"Your version of Python does not have crypt support built in. "
264+
"Please install legacycrypt, such as `pip install legacycrypt` when "
265+
"adding to your install or `pip install netutils[legacycrypt]` when installing fresh."
266+
)
267+
250268
if not salt:
251269
salt = "".join(secrets.choice(ALPHABET) for _ in range(salt_len))
252270
elif not set(salt) <= set(ALPHABET):
253271
raise ValueError(f"type5_pw salt used improper characters, must be one of {ALPHABET}")
254-
return crypt.crypt(unencrypted_password, f"$1${salt}$")
272+
return str(crypt.crypt(unencrypted_password, f"$1${salt}$"))
255273

256274

257275
def encrypt_cisco_type7(unencrypted_password: str, salt: t.Optional[int] = None) -> str:

0 commit comments

Comments
 (0)