Skip to content

Commit 816b8c5

Browse files
authored
require python-3.6 or later, update CI (#56)
* remove dependency on "six" * remove last couple of "from __future__ import with_statement" * switch input.getargspec() to getfullargspec() * fix check for exception in utils.error() * setup: simplify paramiko req, update python version req * bump to version 1.20.0beta1 * ci: update test matrix: python 3.6 through 3.13 * ci: runners use ubuntu-24.04, containers use debian 11.x or 12.x * ci: update paramiko to latest patch releases: 2.8.10 / 2.7.12 / 2.6.6 * ci: update flake8 to 5.x * ci: ssh setup updates (mkdir -p, ecdsa key type) * ci: build docs with python-3.9 container * switch to nose-py3, fudge fork with use_2to3 removed * remove "rudolf" and thus nose "--with-color" arg * tests: don't fudge-patch output, use settings(show()) * tests: fix silly test_connect_does_not_prompt_password_when... * tests: remove obsolete __cmp__ from FakeFile
1 parent 3019a1a commit 816b8c5

32 files changed

+152
-229
lines changed

.github/workflows/linux.yml

Lines changed: 24 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,39 @@
11
name: Linux tests
22

3-
on:
3+
"on":
44
push: {branches: [master]}
55
pull_request: {branches: [master]}
66

77
jobs:
88
test:
9-
runs-on: ubuntu-20.04
9+
runs-on: ubuntu-24.04
1010
timeout-minutes: 30
1111
strategy:
1212
fail-fast: false
1313
matrix:
1414
imgtag:
15-
- 2.7-buster
16-
- 3.4-stretch
17-
- 3.5-buster
18-
- 3.6-buster
19-
- 3.7-buster
20-
- 3.8-bullseye
21-
- 3.9-bullseye
22-
- 3.10-bullseye
15+
- 3.6-bullseye
16+
- 3.7-bullseye
17+
- 3.8-bookworm
18+
- 3.9-bookworm
19+
- 3.10-bookworm
20+
- 3.11-bookworm
21+
- 3.12-bookworm
22+
- 3.13-bookworm
2323
paramiko_ver:
24-
- 2.6.4
25-
- 2.7.10
26-
- 2.8.8
24+
- 2.6.6
25+
- 2.7.12
26+
- 2.8.10
2727
container: "python:${{matrix.imgtag}}"
2828
steps:
29-
- uses: actions/checkout@v2
29+
- uses: actions/checkout@v4
3030
- name: System dependencies
3131
run: |
3232
apt-get -q -y update
3333
apt-get -q -y install openssh-client openssh-server rsync sudo
34-
if [ 3.4-stretch = "${{matrix.imgtag}}" ]; then
35-
sed -i -e '/^mesg n/d' /root/.profile
36-
fi
3734
- name: Python dependencies
3835
run: |
39-
# nose and Fudge need use_2to3 which setuptools-58 removed
40-
pip install 'setuptools<58'
41-
# nose binary wheel is not compatible with python-3.10
42-
# see https://github.com/nose-devs/nose/issues/1099#issuecomment-577412313
43-
pip install --no-binary nose -r dev-requirements.txt
36+
pip install -r dev-requirements.txt
4437
pip install paramiko-ng==${{matrix.paramiko_ver}}
4538
pip install -e .
4639
- name: Lint
@@ -50,10 +43,10 @@ jobs:
5043
- name: Setup SSH
5144
run: |
5245
export USER=root HOME=/root
53-
mkdir -v /run/sshd
46+
mkdir -v -p /run/sshd
5447
/usr/sbin/sshd -D & sleep 1
55-
mkdir -v ~/.ssh
56-
ssh-keygen -N "" -f ~/.ssh/testkey
48+
mkdir -v -p ~/.ssh
49+
ssh-keygen -t ecdsa -N "" -f ~/.ssh/testkey
5750
cp -v ~/.ssh/testkey.pub ~/.ssh/authorized_keys
5851
ssh-keyscan -t ecdsa localhost > ~/.ssh/known_hosts
5952
- name: Test
@@ -65,19 +58,19 @@ jobs:
6558
script -e -q -c "fab -H localhost test:integration" /dev/null
6659
6760
docs:
68-
runs-on: ubuntu-20.04
61+
runs-on: ubuntu-24.04
6962
timeout-minutes: 30
63+
container: "python:3.9-bookworm"
64+
7065
steps:
71-
- uses: actions/checkout@v2
72-
- uses: actions/setup-python@v1
73-
with:
74-
python-version: 3.8
66+
- uses: actions/checkout@v4
7567

7668
- name: Install dependencies
7769
run: |
7870
pip install -r doc-requirements.txt
79-
pip install paramiko-ng==2.8.4
71+
pip install paramiko-ng==2.8.10
8072
pip install -e .
73+
8174
- name: Build docs
8275
run: |
8376
sphinx-build -W sites/docs tmpbuild

README.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
*fab-classic* is a Python (2.7 or 3.4+) library and command-line tool
1+
*fab-classic* is a Python (3.6+) library and command-line tool
22
for streamlining the use of SSH for application deployment or systems
33
administration tasks.
44

dev-requirements.txt

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,9 @@
11
# You should already have Paramiko and your local Fabric checkout installed
22
# Test runner/testing utils
3-
nose<2.0
4-
# Rudolf adds color to the output of 'fab test'. This is a custom fork
5-
# addressing Python 2.7 and Nose's 'skip' plugin compatibility issues.
6-
https://github.com/iknite/rudolf/archive/4a33a26a3aff.tar.gz#egg=rudolf==0.4
7-
# Mocking library
8-
Fudge<1.0
3+
nose-py3
4+
# Fudge mocking library, small fork with use_2to3 removed from setup
5+
https://github.com/ploxiln/fudge/archive/a252954d6e34.tar.gz#egg=fudge
96
# used in some tests
10-
jinja2<4.0
7+
jinja2 <4.0
118
# flake8
12-
flake8==3.7.9
13-
mccabe==0.6.1
14-
pycodestyle==2.5.0
15-
pyflakes==2.1.1
9+
flake8 <6

fabfile.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,6 @@ def test(args=None):
1717
# Default to explicitly targeting the 'tests' folder, but only if nothing
1818
# is being overridden.
1919
tests = "" if args else " tests"
20-
default_args = "-sv --with-doctest --nologcapture --with-color %s" % tests
20+
default_args = "-sv --with-doctest --nologcapture %s" % tests
2121
default_args += (" " + args) if args else ""
2222
nose.core.run_exit(argv=[''] + default_args.split())

fabric/context_managers.py

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,7 @@
1818
1919
"""
2020

21-
from contextlib import contextmanager
22-
import six
21+
from contextlib import contextmanager, ExitStack
2322
import socket
2423
import select
2524

@@ -28,16 +27,12 @@
2827
from fabric import state
2928
from fabric.utils import isatty
3029

31-
if six.PY2 is True:
32-
from contextlib import nested
33-
else:
34-
from contextlib import ExitStack
3530

36-
class nested(ExitStack):
37-
def __init__(self, *managers):
38-
super(nested, self).__init__()
39-
for manager in managers:
40-
self.enter_context(manager)
31+
class nested(ExitStack):
32+
def __init__(self, *managers):
33+
super(nested, self).__init__()
34+
for manager in managers:
35+
self.enter_context(manager)
4136

4237

4338
if not win32:
@@ -130,7 +125,7 @@ def _setenv(variables):
130125
clean_revert = variables.pop('clean_revert', False)
131126
previous = {}
132127
new = []
133-
for key, value in six.iteritems(variables):
128+
for key, value in variables.items():
134129
if key in state.env:
135130
previous[key] = state.env[key]
136131
else:
@@ -140,7 +135,7 @@ def _setenv(variables):
140135
yield
141136
finally:
142137
if clean_revert:
143-
for key, value in six.iteritems(variables):
138+
for key, value in variables.items():
144139
# If the current env value for this key still matches the
145140
# value we set it to beforehand, we are OK to revert it to the
146141
# pre-block value.

fabric/contrib/files.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@
44

55
import hashlib
66
import os
7-
import six
8-
7+
from io import StringIO
98
from functools import partial
109

1110
from fabric.api import run, sudo, hide, settings, env, put, abort
@@ -163,12 +162,12 @@ def upload_template(filename, destination, context=None, use_jinja=False,
163162
target = destination.replace(' ', r'\ ')
164163
func("cp %s %s.bak" % (target, target))
165164

166-
if six.PY3 is True and isinstance(text, bytes):
165+
if isinstance(text, bytes):
167166
text = text.decode('utf-8')
168167

169168
# Upload the file.
170169
return put(
171-
local_path=six.StringIO(text),
170+
local_path=StringIO(text),
172171
remote_path=destination,
173172
use_sudo=use_sudo,
174173
mirror_local_mode=mirror_local_mode,
@@ -413,7 +412,7 @@ def append(filename, text, use_sudo=False, partial=False, escape=True,
413412
"""
414413
func = use_sudo and sudo or run
415414
# Normalize non-list input to be a list
416-
if isinstance(text, six.string_types):
415+
if isinstance(text, str):
417416
text = [text]
418417
for line in text:
419418
regex = '^' + _escape_for_regex(line) + ('' if partial else '$')

fabric/contrib/project.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@
55
import os.path
66
from tempfile import mkdtemp
77

8-
import six
9-
108
from fabric.network import needs_host, key_filenames, normalize
119
from fabric.operations import local, run, sudo, put
1210
from fabric.state import env, output
@@ -106,7 +104,7 @@ def rsync_project(
106104
The ``default_opts`` keyword argument.
107105
"""
108106
# Turn single-string exclude into a one-item list for consistency
109-
if isinstance(exclude, six.string_types):
107+
if isinstance(exclude, str):
110108
exclude = (exclude,)
111109
# Create --exclude options from exclude list
112110
exclude_opts = ' --exclude "%s"' * len(exclude)

fabric/decorators.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
"""
22
Convenience decorators for use in fabfiles.
33
"""
4-
import six
54
import types
65
from functools import wraps
76

@@ -55,7 +54,7 @@ def inner_decorator(*args, **kwargs):
5554
return func(*args, **kwargs)
5655
_values = values
5756
# Allow for single iterable argument as well as *args
58-
if len(_values) == 1 and not isinstance(_values[0], six.string_types):
57+
if len(_values) == 1 and not isinstance(_values[0], str):
5958
_values = _values[0]
6059
setattr(inner_decorator, attribute, list(_values))
6160
# Don't replace @task new-style task objects with inner_decorator by

fabric/io.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@
55
from select import select
66
from collections import deque
77

8-
import six
9-
108
from fabric.state import env, output, win32
119
from fabric.auth import get_password, set_password
1210
import fabric.network
@@ -92,7 +90,7 @@ def loop(self):
9290
raise CommandTimeout(timeout=self.timeout)
9391
continue
9492

95-
if six.PY3 is True and isinstance(bytelist, six.binary_type):
93+
if isinstance(bytelist, bytes):
9694
# Note that we have to decode this right away, even if an error
9795
# is thrown only later in the code, because e.g. '' != b'' (see
9896
# first if below).
@@ -243,7 +241,7 @@ def _get_prompt_response(self):
243241
Iterate through the request prompts dict and return the response and
244242
original request if we find a match
245243
"""
246-
for tup in six.iteritems(env.prompts):
244+
for tup in env.prompts.items():
247245
if _endswith(self.capture, tup[0]):
248246
return tup
249247
return None, None

fabric/main.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@
2020
except ImportError:
2121
from collections import Mapping
2222

23-
import six
24-
2523
# For checking callables against the API, & easy mocking
2624
from fabric import api, state, colors
2725
from fabric.contrib import console, files, project
@@ -379,7 +377,7 @@ def _is_task(name, value):
379377

380378
def _sift_tasks(mapping):
381379
tasks, collections = [], []
382-
for name, value in six.iteritems(mapping):
380+
for name, value in mapping.items():
383381
if _is_task(name, value):
384382
tasks.append(name)
385383
elif isinstance(value, Mapping):
@@ -410,7 +408,7 @@ def _print_docstring(docstrings, name):
410408
if not docstrings:
411409
return False
412410
docstring = crawl(name, state.commands).__doc__
413-
if isinstance(docstring, six.string_types):
411+
if isinstance(docstring, str):
414412
return docstring
415413

416414

@@ -644,7 +642,7 @@ def main(fabfile_locations=None):
644642
# Handle --hosts, --roles, --exclude-hosts (comma separated string =>
645643
# list)
646644
for key in ['hosts', 'roles', 'exclude_hosts']:
647-
if key in state.env and isinstance(state.env[key], six.string_types):
645+
if key in state.env and isinstance(state.env[key], str):
648646
state.env[key] = state.env[key].split(',')
649647

650648
# Feed the env.tasks : tasks that are asked to be executed.

0 commit comments

Comments
 (0)