Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
7d0af51
require python-3.5 or later
ploxiln Aug 22, 2021
4e6ddf7
require python-3.6 or later, update CI OS/images/etc
ploxiln Mar 14, 2025
4892635
minor docs updates now that python-3.6+ required
ploxiln Mar 14, 2025
2a19ee0
fixup nose and fudge testing deps
ploxiln Mar 14, 2025
2216871
fixup fudge archive url
ploxiln Mar 14, 2025
a6dd8e3
remove unused six import
ploxiln Mar 14, 2025
7fde8f5
remove one last use of 'six' from contrib/project
ploxiln Mar 14, 2025
aff7723
remove "rudolf" and thus nose "--with-color" arg
ploxiln Mar 14, 2025
8b3e15f
ci ssh setup: mkdir -p
ploxiln Mar 14, 2025
4d4f819
fix check for exception in utils.error()
ploxiln Mar 14, 2025
23e4a72
tests: don't fudge-patch output, use settings(show())
ploxiln Mar 14, 2025
bf5d936
ci: build docs with python-3.9 container
ploxiln Mar 14, 2025
3be169b
try to fixup some test mocks
ploxiln Mar 14, 2025
8348c07
ci: always use ecdsa ssh key for integration tests
ploxiln Mar 14, 2025
2eb0428
switch to inspect.getfullargspec() for python-3.11+
ploxiln Mar 14, 2025
6c543a2
switch to fudge fork with use_2to3 removed from setup
ploxiln Mar 14, 2025
38937a7
setup: update python req, simplify paramiko req
ploxiln Mar 14, 2025
6520c00
remove last traces of raw_input()
ploxiln Mar 14, 2025
4893855
ci: update flake8 to 5.x
ploxiln Mar 14, 2025
53485a8
fix silly test_connect_does_not_prompt_password_when...
ploxiln Mar 14, 2025
a9de15e
bump to version 1.20.0beta1
ploxiln Mar 14, 2025
2e20fb2
tests: remove obsolete __cmp__ from FakeFile
ploxiln Mar 14, 2025
fd8c797
cleanup some noqa comments
ploxiln Mar 14, 2025
a719d4a
install patched fudge from my own fork just in case
ploxiln Mar 14, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 24 additions & 31 deletions .github/workflows/linux.yml
Original file line number Diff line number Diff line change
@@ -1,46 +1,39 @@
name: Linux tests

on:
"on":
push: {branches: [master]}
pull_request: {branches: [master]}

jobs:
test:
runs-on: ubuntu-20.04
runs-on: ubuntu-24.04
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
imgtag:
- 2.7-buster
- 3.4-stretch
- 3.5-buster
- 3.6-buster
- 3.7-buster
- 3.8-bullseye
- 3.9-bullseye
- 3.10-bullseye
- 3.6-bullseye
- 3.7-bullseye
- 3.8-bookworm
- 3.9-bookworm
- 3.10-bookworm
- 3.11-bookworm
- 3.12-bookworm
- 3.13-bookworm
paramiko_ver:
- 2.6.4
- 2.7.10
- 2.8.8
- 2.6.6
- 2.7.12
- 2.8.10
container: "python:${{matrix.imgtag}}"
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: System dependencies
run: |
apt-get -q -y update
apt-get -q -y install openssh-client openssh-server rsync sudo
if [ 3.4-stretch = "${{matrix.imgtag}}" ]; then
sed -i -e '/^mesg n/d' /root/.profile
fi
- name: Python dependencies
run: |
# nose and Fudge need use_2to3 which setuptools-58 removed
pip install 'setuptools<58'
# nose binary wheel is not compatible with python-3.10
# see https://github.com/nose-devs/nose/issues/1099#issuecomment-577412313
pip install --no-binary nose -r dev-requirements.txt
pip install -r dev-requirements.txt
pip install paramiko-ng==${{matrix.paramiko_ver}}
pip install -e .
- name: Lint
Expand All @@ -50,10 +43,10 @@ jobs:
- name: Setup SSH
run: |
export USER=root HOME=/root
mkdir -v /run/sshd
mkdir -v -p /run/sshd
/usr/sbin/sshd -D & sleep 1
mkdir -v ~/.ssh
ssh-keygen -N "" -f ~/.ssh/testkey
mkdir -v -p ~/.ssh
ssh-keygen -t ecdsa -N "" -f ~/.ssh/testkey
cp -v ~/.ssh/testkey.pub ~/.ssh/authorized_keys
ssh-keyscan -t ecdsa localhost > ~/.ssh/known_hosts
- name: Test
Expand All @@ -65,19 +58,19 @@ jobs:
script -e -q -c "fab -H localhost test:integration" /dev/null

docs:
runs-on: ubuntu-20.04
runs-on: ubuntu-24.04
timeout-minutes: 30
container: "python:3.9-bookworm"

steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v1
with:
python-version: 3.8
- uses: actions/checkout@v4

- name: Install dependencies
run: |
pip install -r doc-requirements.txt
pip install paramiko-ng==2.8.4
pip install paramiko-ng==2.8.10
pip install -e .

- name: Build docs
run: |
sphinx-build -W sites/docs tmpbuild
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
*fab-classic* is a Python (2.7 or 3.4+) library and command-line tool
*fab-classic* is a Python (3.6+) library and command-line tool
for streamlining the use of SSH for application deployment or systems
administration tasks.

Expand Down
16 changes: 5 additions & 11 deletions dev-requirements.txt
Original file line number Diff line number Diff line change
@@ -1,15 +1,9 @@
# You should already have Paramiko and your local Fabric checkout installed
# Test runner/testing utils
nose<2.0
# Rudolf adds color to the output of 'fab test'. This is a custom fork
# addressing Python 2.7 and Nose's 'skip' plugin compatibility issues.
https://github.com/iknite/rudolf/archive/4a33a26a3aff.tar.gz#egg=rudolf==0.4
# Mocking library
Fudge<1.0
nose-py3
# Fudge mocking library, small fork with use_2to3 removed from setup
https://github.com/ploxiln/fudge/archive/a252954d6e34.tar.gz#egg=fudge
# used in some tests
jinja2<4.0
jinja2 <4.0
# flake8
flake8==3.7.9
mccabe==0.6.1
pycodestyle==2.5.0
pyflakes==2.1.1
flake8 <6
2 changes: 1 addition & 1 deletion fabfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@ def test(args=None):
# Default to explicitly targeting the 'tests' folder, but only if nothing
# is being overridden.
tests = "" if args else " tests"
default_args = "-sv --with-doctest --nologcapture --with-color %s" % tests
default_args = "-sv --with-doctest --nologcapture %s" % tests
default_args += (" " + args) if args else ""
nose.core.run_exit(argv=[''] + default_args.split())
21 changes: 8 additions & 13 deletions fabric/context_managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@

"""

from contextlib import contextmanager
import six
from contextlib import contextmanager, ExitStack
import socket
import select

Expand All @@ -28,16 +27,12 @@
from fabric import state
from fabric.utils import isatty

if six.PY2 is True:
from contextlib import nested
else:
from contextlib import ExitStack

class nested(ExitStack):
def __init__(self, *managers):
super(nested, self).__init__()
for manager in managers:
self.enter_context(manager)
class nested(ExitStack):
def __init__(self, *managers):
super(nested, self).__init__()
for manager in managers:
self.enter_context(manager)


if not win32:
Expand Down Expand Up @@ -130,7 +125,7 @@ def _setenv(variables):
clean_revert = variables.pop('clean_revert', False)
previous = {}
new = []
for key, value in six.iteritems(variables):
for key, value in variables.items():
if key in state.env:
previous[key] = state.env[key]
else:
Expand All @@ -140,7 +135,7 @@ def _setenv(variables):
yield
finally:
if clean_revert:
for key, value in six.iteritems(variables):
for key, value in variables.items():
# If the current env value for this key still matches the
# value we set it to beforehand, we are OK to revert it to the
# pre-block value.
Expand Down
9 changes: 4 additions & 5 deletions fabric/contrib/files.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@

import hashlib
import os
import six

from io import StringIO
from functools import partial

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

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

# Upload the file.
return put(
local_path=six.StringIO(text),
local_path=StringIO(text),
remote_path=destination,
use_sudo=use_sudo,
mirror_local_mode=mirror_local_mode,
Expand Down Expand Up @@ -413,7 +412,7 @@ def append(filename, text, use_sudo=False, partial=False, escape=True,
"""
func = use_sudo and sudo or run
# Normalize non-list input to be a list
if isinstance(text, six.string_types):
if isinstance(text, str):
text = [text]
for line in text:
regex = '^' + _escape_for_regex(line) + ('' if partial else '$')
Expand Down
4 changes: 1 addition & 3 deletions fabric/contrib/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
import os.path
from tempfile import mkdtemp

import six

from fabric.network import needs_host, key_filenames, normalize
from fabric.operations import local, run, sudo, put
from fabric.state import env, output
Expand Down Expand Up @@ -106,7 +104,7 @@ def rsync_project(
The ``default_opts`` keyword argument.
"""
# Turn single-string exclude into a one-item list for consistency
if isinstance(exclude, six.string_types):
if isinstance(exclude, str):
exclude = (exclude,)
# Create --exclude options from exclude list
exclude_opts = ' --exclude "%s"' * len(exclude)
Expand Down
3 changes: 1 addition & 2 deletions fabric/decorators.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
"""
Convenience decorators for use in fabfiles.
"""
import six
import types
from functools import wraps

Expand Down Expand Up @@ -55,7 +54,7 @@ def inner_decorator(*args, **kwargs):
return func(*args, **kwargs)
_values = values
# Allow for single iterable argument as well as *args
if len(_values) == 1 and not isinstance(_values[0], six.string_types):
if len(_values) == 1 and not isinstance(_values[0], str):
_values = _values[0]
setattr(inner_decorator, attribute, list(_values))
# Don't replace @task new-style task objects with inner_decorator by
Expand Down
6 changes: 2 additions & 4 deletions fabric/io.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
from select import select
from collections import deque

import six

from fabric.state import env, output, win32
from fabric.auth import get_password, set_password
import fabric.network
Expand Down Expand Up @@ -92,7 +90,7 @@ def loop(self):
raise CommandTimeout(timeout=self.timeout)
continue

if six.PY3 is True and isinstance(bytelist, six.binary_type):
if isinstance(bytelist, bytes):
# Note that we have to decode this right away, even if an error
# is thrown only later in the code, because e.g. '' != b'' (see
# first if below).
Expand Down Expand Up @@ -243,7 +241,7 @@ def _get_prompt_response(self):
Iterate through the request prompts dict and return the response and
original request if we find a match
"""
for tup in six.iteritems(env.prompts):
for tup in env.prompts.items():
if _endswith(self.capture, tup[0]):
return tup
return None, None
Expand Down
8 changes: 3 additions & 5 deletions fabric/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@
except ImportError:
from collections import Mapping

import six

# For checking callables against the API, & easy mocking
from fabric import api, state, colors
from fabric.contrib import console, files, project
Expand Down Expand Up @@ -379,7 +377,7 @@ def _is_task(name, value):

def _sift_tasks(mapping):
tasks, collections = [], []
for name, value in six.iteritems(mapping):
for name, value in mapping.items():
if _is_task(name, value):
tasks.append(name)
elif isinstance(value, Mapping):
Expand Down Expand Up @@ -410,7 +408,7 @@ def _print_docstring(docstrings, name):
if not docstrings:
return False
docstring = crawl(name, state.commands).__doc__
if isinstance(docstring, six.string_types):
if isinstance(docstring, str):
return docstring


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

# Feed the env.tasks : tasks that are asked to be executed.
Expand Down
17 changes: 4 additions & 13 deletions fabric/network.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
import os
import re
import time
import six
import socket
import sys
from io import StringIO

import paramiko as ssh

Expand Down Expand Up @@ -204,7 +204,7 @@ def key_filenames():
from fabric.state import env
keys = env.key_filename
# For ease of use, coerce stringish key filename into list
if isinstance(env.key_filename, six.string_types) or env.key_filename is None:
if isinstance(env.key_filename, str) or env.key_filename is None:
keys = [keys]
# Strip out any empty strings (such as the default value...meh)
keys = list(filter(bool, keys))
Expand Down Expand Up @@ -232,7 +232,7 @@ def key_from_env(passphrase=None):
if output.debug:
sys.stderr.write("Trying to load it as %s\n" % pkey_class)
try:
return pkey_class.from_private_key(six.StringIO(env.key), passphrase)
return pkey_class.from_private_key(StringIO(env.key), passphrase)
except Exception as e:
# File is valid key, but is encrypted: raise it, this will
# cause cxn loop to prompt for passphrase & retry
Expand Down Expand Up @@ -612,10 +612,6 @@ def connect(user, host, port, cache, seek_gateway=True):


def _password_prompt(prompt, stream):
# NOTE: Using encode-to-ascii to prevent (Windows, at least) getpass from
# choking if given Unicode.
if six.PY3 is False:
prompt = prompt.encode('ascii', 'ignore')
return getpass.getpass(prompt, stream)

def prompt_for_password(prompt=None, no_colon=False, stream=None):
Expand Down Expand Up @@ -678,12 +674,7 @@ def host_prompting_wrapper(*args, **kwargs):
handle_prompt_abort("the target host connection string")
prompt = "No hosts found. Please specify (single) " \
"host string for connection: "
# WARNING: do not use six.moves.input, because test cases to not
# overwrite that method with a faked method from Fudge
if six.PY3 is True:
host_string = input(prompt)
else:
host_string = raw_input(prompt) # noqa: F821
host_string = input(prompt)
env.update(to_dict(host_string))
return func(*args, **kwargs)
host_prompting_wrapper.undecorated = func
Expand Down
Loading