diff options
-rw-r--r-- | .circleci/config.yml | 91 | ||||
-rw-r--r-- | .codecov.yml | 3 | ||||
-rw-r--r-- | .coveragerc | 5 | ||||
-rw-r--r-- | .flake8 | 9 | ||||
-rw-r--r-- | .github/FUNDING.yml | 3 | ||||
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | .travis.yml | 88 | ||||
-rw-r--r-- | README.rst | 193 | ||||
-rw-r--r-- | dev-requirements.txt | 12 | ||||
-rw-r--r-- | paramiko/_version.py | 2 | ||||
-rw-r--r-- | paramiko/sftp_client.py | 17 | ||||
-rw-r--r-- | pytest.ini | 10 | ||||
-rw-r--r-- | setup.cfg | 20 | ||||
-rw-r--r-- | setup.py | 28 | ||||
-rw-r--r-- | sites/shared_conf.py | 2 | ||||
-rw-r--r-- | sites/www/changelog.rst | 20 | ||||
-rw-r--r-- | sites/www/contact.rst | 6 | ||||
-rw-r--r-- | sites/www/index.rst | 23 | ||||
-rw-r--r-- | tasks.py | 38 | ||||
-rw-r--r-- | tests/test_client.py | 5 | ||||
-rw-r--r-- | tests/test_gssapi.py | 7 | ||||
-rw-r--r-- | tests/test_sftp.py | 28 |
22 files changed, 293 insertions, 319 deletions
diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 00000000..6b622057 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,91 @@ +version: 2.1 + + +orbs: + orb: invocations/orb@1.0.4 + + +jobs: + sdist-test-suite: + executor: + name: orb/default + version: "3.6" + steps: + - orb/setup + - run: inv release.build --no-wheel --directory . + - run: | + cd dist + tar xzvf *.tar.gz + rm -v *.tar.gz + cd paramiko-* + pip install -e . + inv -e test + - orb/debug + + kerberos: + executor: + name: orb/default + version: "3.6" + steps: + - orb/setup + # Required to actually see all of universe/multiverse :( + - run: sudo apt update + # System reqs to install/build gssapi c-ext & friends (who only + # appear to offer wheels for Windows) + - run: sudo apt install -y libkrb5-dev krb5-admin-server krb5-kdc + # Our gssapi-supporting flavor, eg gssapi, pyasn1 etc + - run: pip install -e '.[gssapi]' + # Test-only deps for Kerberos (if they are importable it triggers + # running the kerberos tests instead of skipping them) + - run: pip install k5test + # Do the thing, win the points! + - run: inv test + - orb/debug + + +workflows: + main: + jobs: + # The basics + - orb/lint: + name: Lint + - orb/format: + name: Style check + # Main test run, w/ coverage, and latest-supported cryptography + - orb/coverage: + name: Test 3.6 (w/ coverage, latest crypto) + # Non-coverage runs w/ other crypto versions. + # (Phrased as 2-dimensional matrix but 3.6 only for now to save credits) + - orb/test: + name: Test << matrix.version >> w/ << matrix.pip-overrides >> + matrix: + parameters: + version: ["3.6"] + # TODO: I don't see a nicer way to do this that doesn't require + # making the orb know too much about its client code... + pip-overrides: ["cryptography==2.5", "cryptography==3.4"] + # Kerberos tests. Currently broken :( + #- kerberos: + # name: Test 3.6 w/ Kerberos support + # # No point testing k5 if base tests already fail + # requires: ["Test 3.6 (w/ coverage, latest crypto)"] + - orb/test-release: + name: Release test + # Ensure test suite is included in sdist & functions appropriately + - sdist-test-suite: + name: Test within sdist + requires: + - "Test 3.6 (w/ coverage, latest crypto)" + - "Release test" + # Test other interpreters if main passed + - orb/test: + name: Test << matrix.version >> + requires: ["Test 3.6 (w/ coverage, latest crypto)"] + matrix: + parameters: + version: ["3.7", "3.8", "3.9"] + # Test doc building if main test suite passed (no real reason to spend + # all those credits if the main tests would also fail...) + - orb/docs: + name: "Docs" + requires: ["Test 3.6 (w/ coverage, latest crypto)"] diff --git a/.codecov.yml b/.codecov.yml new file mode 100644 index 00000000..41ab9cc4 --- /dev/null +++ b/.codecov.yml @@ -0,0 +1,3 @@ +comment: false +coverage: + precision: 0 diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 00000000..47b6f4c3 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,5 @@ +[run] +branch = True +include = + paramiko/* + tests/* diff --git a/.flake8 b/.flake8 new file mode 100644 index 00000000..1317fd52 --- /dev/null +++ b/.flake8 @@ -0,0 +1,9 @@ +[flake8] +exclude = sites,.git,build,dist,demos +# NOTE: W503, E203 are concessions to black 18.0b5 and could be reinstated +# later if fixed on that end. +# NOTE: E722 seems to only have started popping up on move to flake8 3.6.0 from +# 2.4.0. Not sure why, bare excepts have been a (regrettable) thing forever... +ignore = E124,E125,E128,E261,E301,E302,E303,E402,E721,W503,E203,E722 +max-line-length = 79 + diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..30ce38c8 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,3 @@ +# These are supported funding model platforms + +tidelift: "pypi/paramiko" @@ -10,3 +10,5 @@ _build .coverage .cache .idea +coverage.xml +htmlcov diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 58a3ac42..00000000 --- a/.travis.yml +++ /dev/null @@ -1,88 +0,0 @@ -dist: xenial -language: python -sudo: false -cache: - directories: - - $HOME/.cache/pip -python: - - "2.7" - - "3.4" - - "3.5" - - "3.6" - - "3.7" - - "3.8" - - "pypy" - - "pypy3" -matrix: - # Explicitly test against our oldest supported cryptography.io, in addition - # to whatever the latest default is. - include: - - python: 2.7 - env: "OLDEST_CRYPTO=2.5" - - python: 3.7 - env: "OLDEST_CRYPTO=2.5" - - python: 2.7 - env: "USE_K5TEST=yes" - - python: 3.7 - env: "USE_K5TEST=yes" -install: - # Ensure modern pip/etc to avoid some issues w/ older worker environs - - pip install pip==9.0.1 setuptools==36.6.0 - # Grab a specific version of Cryptography if desired. Doing this before other - # installations ensures we don't have to do any downgrading/overriding. - - | - if [[ -n "$OLDEST_CRYPTO" ]]; then - pip install "cryptography==${OLDEST_CRYPTO}" - fi - # Self-install for setup.py-driven deps (plus additional - # safe-enough-for-all-matrix-cells optional deps) - # TODO: additional matrices or test steps to test all the entrypoints - - pip install -e ".[ed25519,invoke]" - # Dev (doc/test running) requirements - # TODO: use poetry + whatever contexty-type stuff it has, should be more than - # just prod/dev split. Also apply to the above re: extras_require. - - pip install codecov # For codecov specifically - - pip install -r dev-requirements.txt - - | - if [[ -n "$USE_K5TEST" ]]; then - # we need a few commands and libraries - # Debian/Ubuntu package: commands used by package k5test - # libkrb5-dev: krb5-config - # krb5-kdc: kdb5_util, krb5kdc - # krb5-admin-server: kadmin.local, kprop, kadmind - # krb5-user: kinit, klist - # - # krb5-multidev: required to build gssapi - sudo apt-get -y install libkrb5-dev krb5-admin-server \ - krb5-kdc krb5-user krb5-multidev && \ - pip install k5test gssapi pyasn1 - fi - # In case of problems uncomment the following to get the krb environment - # - | - # if [[ -n "$USE_K5TEST" ]]; then - # python -c 'from tests.util import k5shell; k5shell()' env | sort - # fi -script: - # Fast syntax check failures for more rapid feedback to submitters - # (Travis-oriented metatask that version checks Python, installs, runs.) - - inv travis.blacken - # I have this in my git pre-push hook, but contributors probably don't - - flake8 - # All (including slow) tests, w/ coverage! - - inv coverage - # Ensure documentation builds, both sites, maxxed nitpicking - - inv sites - # Sanity check ability to build sdist - - python setup.py sdist - # And ability to run tests from within the result - - cd dist && tar xzvf *.tar.gz && rm *.tar.gz && cd paramiko-* && ls -l && pip install -e . && inv test -notifications: - irc: - channels: "irc.freenode.org#paramiko" - template: - - "%{repository}@%{branch}: %{message} (%{build_url})" - on_success: change - on_failure: change - email: false -after_success: - - codecov @@ -1,145 +1,48 @@ -======== -Paramiko -======== - -.. Continuous integration and code coverage badges - -.. image:: https://travis-ci.org/paramiko/paramiko.svg?branch=master - :target: https://travis-ci.org/paramiko/paramiko -.. image:: https://codecov.io/gh/paramiko/paramiko/branch/master/graph/badge.svg - :target: https://codecov.io/gh/paramiko/paramiko - -:Paramiko: Python SSH module -:Copyright: Copyright (c) 2009 Robey Pointer <robeypointer@gmail.com> -:Copyright: Copyright (c) 2020 Jeff Forcier <jeff@bitprophet.org> -:License: `LGPL <https://www.gnu.org/copyleft/lesser.html>`_ -:Homepage: http://www.paramiko.org/ -:API docs: http://docs.paramiko.org -:Development: https://github.com/paramiko/paramiko - - -What ----- - -"Paramiko" is a combination of the Esperanto words for "paranoid" and -"friend". It's a module for Python 2.7/3.4+ that implements the SSH2 protocol -for secure (encrypted and authenticated) connections to remote machines. Unlike -SSL (aka TLS), SSH2 protocol does not require hierarchical certificates signed -by a powerful central authority. You may know SSH2 as the protocol that -replaced Telnet and rsh for secure access to remote shells, but the protocol -also includes the ability to open arbitrary channels to remote services across -the encrypted tunnel (this is how SFTP works, for example). - -It is written entirely in Python (though it depends on third-party C wrappers -for low level crypto; these are often available precompiled) and is released -under the GNU Lesser General Public License (`LGPL -<https://www.gnu.org/copyleft/lesser.html>`_). - -The package and its API is fairly well documented in the ``docs`` folder that -should have come with this repository. - - -Installation ------------- - -For most users, the recommended method to install is via pip:: - - pip install paramiko - -For more detailed instructions, see the `Installing -<http://www.paramiko.org/installing.html>`_ page on the main Paramiko website. - - -Portability Issues ------------------- - -Paramiko primarily supports POSIX platforms with standard OpenSSH -implementations, and is most frequently tested on Linux and OS X. Windows is -supported as well, though it may not be as straightforward. - -Bugs & Support --------------- - -:Bug Reports: `Github <https://github.com/paramiko/paramiko/issues/>`_ -:Mailing List: ``paramiko@librelist.com`` (see the `LibreList website - <http://librelist.com/>`_ for usage details). -:IRC: ``#paramiko`` on Freenode - - -Kerberos Support ----------------- - -Paramiko ships with optional Kerberos/GSSAPI support; for info on the extra -dependencies for this, see the `GSS-API section -<http://www.paramiko.org/installing.html#gssapi>`_ -on the main Paramiko website. - - -Demo ----- - -Several demo scripts come with Paramiko to demonstrate how to use it. -Probably the simplest demo is this:: - - import base64 - import paramiko - key = paramiko.RSAKey(data=base64.b64decode(b'AAA...')) - client = paramiko.SSHClient() - client.get_host_keys().add('ssh.example.com', 'ssh-rsa', key) - client.connect('ssh.example.com', username='strongbad', password='thecheat') - stdin, stdout, stderr = client.exec_command('ls') - for line in stdout: - print('... ' + line.strip('\n')) - client.close() - -This prints out the results of executing ``ls`` on a remote server. The host -key ``b'AAA...'`` should of course be replaced by the actual base64 encoding of the -host key. If you skip host key verification, the connection is not secure! - -The following example scripts (in demos/) get progressively more detailed: - -:demo_simple.py: - Calls invoke_shell() and emulates a terminal/TTY through which you can - execute commands interactively on a remote server. Think of it as a - poor man's SSH command-line client. - -:demo.py: - Same as demo_simple.py, but allows you to authenticate using a private - key, attempts to use an SSH agent if present, and uses the long form of - some of the API calls. - -:forward.py: - Command-line script to set up port-forwarding across an SSH transport. - -:demo_sftp.py: - Opens an SFTP session and does a few simple file operations. - -:demo_server.py: - An SSH server that listens on port 2200 and accepts a login for - 'robey' (password 'foo'), and pretends to be a BBS. Meant to be a - very simple demo of writing an SSH server. - -:demo_keygen.py: - A key generator similar to OpenSSH ``ssh-keygen(1)`` program with - Paramiko keys generation and progress functions. - -Use ---- - -The demo scripts are probably the best example of how to use this package. -Also a lot of documentation is generated by Sphinx autodoc, in the -doc/ folder. - -There are also unit tests here:: - - $ pip install -r dev-requirements.txt - $ pytest - -Which will verify that most of the core components are working correctly. - -To test Kerberos/GSSAPI, you need a Kerberos environment. On UNIX you can -use the package k5test to setup a Kerberos environment on the fly:: - - $ pip install -r dev-requirements.txt - $ pip install k5test gssapi pyasn1 - $ pytest +|version| |python| |license| |ci| |coverage| + +.. |version| image:: https://img.shields.io/pypi/v/paramiko + :target: https://pypi.org/project/paramiko/ + :alt: PyPI - Package Version +.. |python| image:: https://img.shields.io/pypi/pyversions/paramiko + :target: https://pypi.org/project/paramiko/ + :alt: PyPI - Python Version +.. |license| image:: https://img.shields.io/pypi/l/paramiko + :target: https://github.com/paramiko/paramiko/blob/main/LICENSE + :alt: PyPI - License +.. |ci| image:: https://img.shields.io/circleci/build/github/paramiko/paramiko/main + :target: https://app.circleci.com/pipelines/github/paramiko/paramiko + :alt: CircleCI +.. |coverage| image:: https://img.shields.io/codecov/c/gh/paramiko/paramiko + :target: https://app.codecov.io/gh/paramiko/paramiko + :alt: Codecov + +Welcome to Paramiko! +==================== + +Paramiko is a pure-Python [#]_ (2.7, 3.4+) implementation of the SSHv2 protocol +[#]_, providing both client and server functionality. It provides the +foundation for the high-level SSH library `Fabric <https://fabfile.org>`_, +which is what we recommend you use for common client use-cases such as running +remote shell commands or transferring files. + +Direct use of Paramiko itself is only intended for users who need +advanced/low-level primitives or want to run an in-Python sshd. + +For installation information, changelogs, FAQs and similar, please visit `our +main project website <https://paramiko.org>`_; for API details, see `the +versioned docs <https://docs.paramiko.org>`_. Additionally, the project +maintainer keeps a `roadmap <http://bitprophet.org/projects#roadmap>`_ on his +personal site. + +.. [#] + Paramiko relies on `cryptography <https://cryptography.io>`_ for crypto + functionality, which makes use of C and Rust extensions but has many + precompiled options available. See `our installation page + <https://www.paramiko.org/installing.html>`_ for details. + +.. [#] + SSH is defined in :rfc-reference:`4251`, :rfc-reference:`4252`, + :rfc-reference:`4253` and :rfc-reference:`4254`. The primary working + implementation of the protocol is the `OpenSSH project + <http://openssh.org>`_. Paramiko implements a large portion of the SSH + feature set, but there are occasional gaps. diff --git a/dev-requirements.txt b/dev-requirements.txt index 56f53ad9..aa2f8d49 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,6 +1,6 @@ # Invocations for common project tasks -invoke>=1.0,<2.0 -invocations>=1.2.0,<2.0 +invoke==1.6.0 +invocations==2.3.0 pytest==4.4.2 pytest-relaxed==1.1.5 # pytest-xdist for test dir watching and the inv guard task @@ -8,16 +8,16 @@ pytest-xdist==1.28.0 mock==2.0.0 # Linting! flake8==3.8.3 +# Formatting! +black==18.6b4 # Coverage! coverage==4.5.4 -codecov==1.6.3 +codecov==2.1.11 # Documentation tools sphinx>=1.4,<1.7 alabaster==0.7.12 releases>=1.5,<2.0 # Release tools semantic_version>=2.4,<2.5 -wheel==0.24 -twine==1.11.0 # Self --e . +-e ".[ed2559,invoke]" diff --git a/paramiko/_version.py b/paramiko/_version.py index 8b3ae0e7..07aff82b 100644 --- a/paramiko/_version.py +++ b/paramiko/_version.py @@ -1,2 +1,2 @@ -__version_info__ = (2, 7, 2) +__version_info__ = (2, 8, 0) __version__ = ".".join(map(str, __version_info__)) diff --git a/paramiko/sftp_client.py b/paramiko/sftp_client.py index 93190d85..6294fb48 100644 --- a/paramiko/sftp_client.py +++ b/paramiko/sftp_client.py @@ -758,7 +758,7 @@ class SFTPClient(BaseSFTP, ClosingContextManager): with open(localpath, "rb") as fl: return self.putfo(fl, remotepath, file_size, callback, confirm) - def getfo(self, remotepath, fl, callback=None): + def getfo(self, remotepath, fl, callback=None, prefetch=True): """ Copy a remote file (``remotepath``) from the SFTP server and write to an open file or file-like object, ``fl``. Any exception raised by @@ -771,18 +771,23 @@ class SFTPClient(BaseSFTP, ClosingContextManager): :param callable callback: optional callback function (form: ``func(int, int)``) that accepts the bytes transferred so far and the total bytes to be transferred + :param bool prefetch: + controls whether prefetching is performed (default: True) :return: the `number <int>` of bytes written to the opened file object .. versionadded:: 1.10 + .. versionchanged:: 2.8 + Added the ``prefetch`` keyword argument. """ file_size = self.stat(remotepath).st_size with self.open(remotepath, "rb") as fr: - fr.prefetch(file_size) + if prefetch: + fr.prefetch(file_size) return self._transfer_with_callback( reader=fr, writer=fl, file_size=file_size, callback=callback ) - def get(self, remotepath, localpath, callback=None): + def get(self, remotepath, localpath, callback=None, prefetch=True): """ Copy a remote file (``remotepath``) from the SFTP server to the local host as ``localpath``. Any exception raised by operations will be @@ -793,13 +798,17 @@ class SFTPClient(BaseSFTP, ClosingContextManager): :param callable callback: optional callback function (form: ``func(int, int)``) that accepts the bytes transferred so far and the total bytes to be transferred + :param bool prefetch: + controls whether prefetching is performed (default: True) .. versionadded:: 1.4 .. versionchanged:: 1.7.4 Added the ``callback`` param + .. versionchanged:: 2.8 + Added the ``prefetch`` keyword argument. """ with open(localpath, "wb") as fl: - size = self.getfo(remotepath, fl, callback) + size = self.getfo(remotepath, fl, callback, prefetch) s = os.stat(localpath) if s.st_size != size: raise IOError( diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 00000000..be207cd8 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,10 @@ +[pytest] +# We use pytest-relaxed just for its utils at the moment, so disable it at the +# plugin level until we adapt test organization to really use it. +addopts = -p no:relaxed +# Loop on failure +looponfailroots = tests paramiko +# Ignore some warnings we cannot easily handle. +filterwarnings = + ignore::DeprecationWarning:pkg_resources + ignore::cryptography.utils.CryptographyDeprecationWarning @@ -6,23 +6,3 @@ license_file = LICENSE [coverage:run] omit = paramiko/_winapi.py - -[flake8] -exclude = sites,.git,build,dist,demos -# NOTE: W503, E203 are concessions to black 18.0b5 and could be reinstated -# later if fixed on that end. -# NOTE: E722 seems to only have started popping up on move to flake8 3.6.0 from -# 2.4.0. Not sure why, bare excepts have been a (regrettable) thing forever... -ignore = E124,E125,E128,E261,E301,E302,E303,E402,E721,W503,E203,E722 -max-line-length = 79 - -[tool:pytest] -# We use pytest-relaxed just for its utils at the moment, so disable it at the -# plugin level until we adapt test organization to really use it. -addopts = -p no:relaxed -# Loop on failure -looponfailroots = tests paramiko -# Ignore some warnings we cannot easily handle. -filterwarnings = - ignore::DeprecationWarning:pkg_resources - ignore::cryptography.utils.CryptographyDeprecationWarning @@ -24,16 +24,7 @@ if sys.platform == "darwin": setup_helper.install_custom_make_tarball() -longdesc = """ -This is a library for making SSH2 connections (client or server). -Emphasis is on using SSH2 as an alternative to SSL for making secure -connections between python scripts. All major ciphers and hash methods -are supported. SFTP client and server mode are both supported too. - -To install the development version, ``pip install -e -git+https://github.com/paramiko/paramiko/#egg=paramiko``. -""" - +long_description = open("README.rst").read() # Version info -- read without importing _locals = {} @@ -61,10 +52,17 @@ setup( name="paramiko", version=version, description="SSH2 protocol library", - long_description=longdesc, + long_description=long_description, author="Jeff Forcier", author_email="jeff@bitprophet.org", - url="https://github.com/paramiko/paramiko/", + url="https://paramiko.org", + project_urls={ + "Docs": "https://docs.paramiko.org", + "Source": "https://github.com/paramiko/paramiko", + "Issues": "https://github.com/paramiko/paramiko/issues", + "Changelog": "https://www.paramiko.org/changelog.html", + "CI": "https://app.circleci.com/pipelines/github/paramiko/paramiko", + }, packages=["paramiko"], license="LGPL", platforms="Posix; MacOS X; Windows", @@ -85,9 +83,13 @@ setup( "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", ], # TODO 3.0: remove bcrypt, pynacl and update installation docs noting that - # use of the extras_require(s) is now required for those + # use of the extras_require ("paramiko[ed2559]") is now required for those + # TODO 3.0: alternately, given how prevalent ed25519 is now, and how we use + # invoke for (increasing) subproc stuff, consider making the default flavor + # "full" and adding a "minimal" or similar that is just-crypto? install_requires=["bcrypt>=3.1.3", "cryptography>=2.5", "pynacl>=1.0.1"], extras_require=extras_require, ) diff --git a/sites/shared_conf.py b/sites/shared_conf.py index 7bb503ce..c406054c 100644 --- a/sites/shared_conf.py +++ b/sites/shared_conf.py @@ -13,7 +13,7 @@ html_theme_options = { "github_user": "paramiko", "github_repo": "paramiko", "analytics_id": "UA-18486793-2", - "travis_button": True, + "travis_button": False, "tidelift_url": "https://tidelift.com/subscription/pkg/pypi-paramiko?utm_source=pypi-paramiko&utm_medium=referral&utm_campaign=docs", } html_sidebars = { diff --git a/sites/www/changelog.rst b/sites/www/changelog.rst index 7c20e434..49b41f41 100644 --- a/sites/www/changelog.rst +++ b/sites/www/changelog.rst @@ -2,12 +2,28 @@ Changelog ========= -- :bug:`1462` (via :issue:`1882`) Newer server-side key exchange algorithms not - intended to use SHA1 (``diffie-hellman-group14-sha256``, +- :release:`2.8.0 <2021-10-09>` +- :support:`-` Administrivia overhaul, including but not limited to: + + - Migrate CI to CircleCI + - Primary dev branch is now ``main`` (renamed) + - Many README edits for clarity, modernization etc; including a bunch more + (and consistent) status badges & unification with main project site index + - PyPI page much more fleshed out (long_description is now filled in with the + README; sidebar links expanded; etc) + - flake8, pytest configs split out of setup.cfg into their own files + - Invoke/invocations (used by maintainers/contributors) upgraded to modern + versions + +- :bug:`1462 major` (via :issue:`1882`) Newer server-side key exchange + algorithms not intended to use SHA1 (``diffie-hellman-group14-sha256``, ``diffie-hellman-group16-sha512``) were incorrectly using SHA1 after all, due to a bug causing them to ignore the ``hash_algo`` class attribute. This has been corrected. Big thanks to ``@miverson`` for the report and to Benno Rice for the patch. +- :feature:`1846` Add a ``prefetch`` keyword argument to `SFTPClient.get <paramiko.sftp_client.SFTPClient.get>`/`SFTPClient.getfo <paramiko.sftp_client.SFTPClient.getfo>` + so users who need to skip SFTP prefetching are able to conditionally turn it + off. Thanks to Github user ``@h3ll0r`` for the PR. - :release:`2.7.2 <2020-08-30>` - :support:`- backported` Update our CI to catch issues with sdist generation, installation and testing. diff --git a/sites/www/contact.rst b/sites/www/contact.rst index dafc1bd4..202815f8 100644 --- a/sites/www/contact.rst +++ b/sites/www/contact.rst @@ -5,6 +5,8 @@ Contact You can get in touch with the developer & user community in any of the following ways: -* IRC: ``#paramiko`` on Freenode -* This website - a blog section is forthcoming. * Submit contributions on Github - see the :doc:`contributing` page. +* Follow ``@bitprophet`` on Twitter, though it's not a dedicated account and + mostly just retweets funny pictures. +* Subscribe to the ``paramiko`` category on the developer's blog: + http://bitprophet.org/categories/paramiko/ diff --git a/sites/www/index.rst b/sites/www/index.rst index 26961f24..9c3bb3aa 100644 --- a/sites/www/index.rst +++ b/sites/www/index.rst @@ -1,18 +1,4 @@ -Welcome to Paramiko! -==================== - -Paramiko is a Python (2.7, 3.4+) implementation of the SSHv2 protocol [#]_, -providing both client and server functionality. While it leverages a Python C -extension for low level cryptography (`Cryptography -<https://cryptography.io>`_), Paramiko itself is a pure Python interface around -SSH networking concepts. - -This website covers project information for Paramiko such as the changelog, -contribution guidelines, development roadmap, news/blog, and so forth. Detailed -usage and API documentation can be found at our code documentation site, -`docs.paramiko.org <http://docs.paramiko.org>`_. - -Please see the sidebar to the left to begin. +.. include:: ../../README.rst .. toctree:: :hidden: @@ -25,10 +11,3 @@ Please see the sidebar to the left to begin. contact -.. rubric:: Footnotes - -.. [#] - SSH is defined in :rfc:`4251`, :rfc:`4252`, :rfc:`4253` and :rfc:`4254`. The - primary working implementation of the protocol is the `OpenSSH project - <http://openssh.org>`_. Paramiko implements a large portion of the SSH - feature set, but there are occasional gaps. @@ -3,7 +3,6 @@ from os.path import join from shutil import rmtree, copytree from invoke import Collection, task -from invocations import travis from invocations.checks import blacken from invocations.docs import docs, www, sites from invocations.packaging.release import ns as release_coll, publish @@ -60,7 +59,7 @@ def test( # Leverage how pytest can be run as 'python -m pytest', and then how # coverage can be told to run things in that manner instead of # expecting a literal .py file. - runner = "coverage run --source=paramiko -m pytest" + runner = "coverage run -m pytest" # Strip SSH_AUTH_SOCK from parent env to avoid pollution by interactive # users. # TODO: once pytest coverage plugin works, see if there's a pytest-native @@ -75,11 +74,15 @@ def test( @task -def coverage(ctx, opts=""): +def coverage(ctx, opts="", codecov=False): """ Execute all tests (normal and slow) with coverage enabled. """ - return test(ctx, coverage=True, include_slow=True, opts=opts) + test(ctx, coverage=True, include_slow=True, opts=opts) + # Cribbed from invocations.pytest.coverage for now + if codecov: + ctx.run("coverage xml") + ctx.run("codecov") @task @@ -94,8 +97,12 @@ def guard(ctx, opts=""): # Until we stop bundling docs w/ releases. Need to discover use cases first. # TODO: would be nice to tie this into our own version of build() too, but # still have publish() use that build()...really need to try out classes! +# TODO 3.0: I'd like to just axe the 'built docs in sdist', none of my other +# projects do it. @task -def release(ctx, sdist=True, wheel=True, sign=True, dry_run=False, index=None): +def publish_( + ctx, sdist=True, wheel=True, sign=True, dry_run=False, index=None +): """ Wraps invocations.packaging.publish to add baked-in docs folder. """ @@ -110,17 +117,22 @@ def release(ctx, sdist=True, wheel=True, sign=True, dry_run=False, index=None): publish( ctx, sdist=sdist, wheel=wheel, sign=sign, dry_run=dry_run, index=index ) - # Remind - print( - "\n\nDon't forget to update RTD's versions page for new minor " - "releases!" - ) + + +# Also have to hack up the newly enhanced all_() so it uses our publish +@task(name="all", default=True) +def all_(c, dry_run=False): + release_coll["prepare"](c, dry_run=dry_run) + publish_(c, dry_run=dry_run) + release_coll["push"](c, dry_run=dry_run) + release_coll["tidelift"](c, dry_run=dry_run) # TODO: "replace one task with another" needs a better public API, this is # using unpublished internals & skips all the stuff add_task() does re: # aliasing, defaults etc. -release_coll.tasks["publish"] = release +release_coll.tasks["publish"] = publish_ +release_coll.tasks["all"] = all_ ns = Collection( test, @@ -131,7 +143,6 @@ ns = Collection( www, sites, count_errors, - travis, blacken, ) ns.configure( @@ -145,7 +156,6 @@ ns.configure( "changelog_file": join( www.configuration()["sphinx"]["source"], "changelog.rst" ), - }, - "travis": {"black": {"version": "18.6b4"}}, + } } ) diff --git a/tests/test_client.py b/tests/test_client.py index 60ad310c..f14aac23 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -421,7 +421,10 @@ class SSHClientTest(ClientTest): # force a collection to see whether the SSHClient object is deallocated # 2 GCs are needed on PyPy, time is needed for Python 3 - time.sleep(0.3) + # TODO: this still fails randomly under CircleCI under Python 3.7, 3.8 + # at the very least. bumped sleep 0.3->1.0s but the underlying + # functionality should get reevaluated once we drop Python 2. + time.sleep(1) gc.collect() gc.collect() diff --git a/tests/test_gssapi.py b/tests/test_gssapi.py index 308caa93..acdc7c82 100644 --- a/tests/test_gssapi.py +++ b/tests/test_gssapi.py @@ -26,6 +26,13 @@ import socket from .util import needs_gssapi, KerberosTestCase, update_env +# +# NOTE: KerberosTestCase skips all tests if it was unable to import k5test +# third-party library. That's the primary trigger for whether this module +# effectively gets run or not. See tests/util.py for other triggers (a set of +# env vars a human might have defined). +# + @needs_gssapi class GSSAPITest(KerberosTestCase): diff --git a/tests/test_sftp.py b/tests/test_sftp.py index e4e18e5a..a98a46c6 100644 --- a/tests/test_sftp.py +++ b/tests/test_sftp.py @@ -554,6 +554,34 @@ class TestSFTP(object): os.unlink(localname) sftp.unlink(sftp.FOLDER + "/bunny.txt") + def test_get_without_prefetch(self, sftp): + """ + Create a 4MB file. Verify that pull works without prefetching + using a lager file. + """ + + sftp_filename = sftp.FOLDER + "/dummy_file" + num_chars = 1024 * 1024 * 4 + + fd, localname = mkstemp() + os.close(fd) + + with open(localname, "wb") as f: + f.write(b"0" * num_chars) + + sftp.put(localname, sftp_filename) + + os.unlink(localname) + fd, localname = mkstemp() + os.close(fd) + + sftp.get(sftp_filename, localname, prefetch=False) + + assert os.stat(localname).st_size == num_chars + + os.unlink(localname) + sftp.unlink(sftp_filename) + def test_check(self, sftp): """ verify that file.check() works against our own server. |