{ "info": { "author": "Marko Ristin", "author_email": "marko.ristin@gmail.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6" ], "description": "Spur+\n=====\n\n.. image:: https://api.travis-ci.com/Parquery/spurplus.svg?branch=master\n :target: https://api.travis-ci.com/Parquery/spurplus.svg?branch=master\n :alt: Build Status\n\n.. image:: https://coveralls.io/repos/github/Parquery/spurplus/badge.svg?branch=master\n :target: https://coveralls.io/github/Parquery/spurplus?branch=master\n :alt: Coverage\n\n.. image:: https://readthedocs.org/projects/spurplus/badge/?version=latest\n :target: https://spurplus.readthedocs.io/en/latest/?badge=latest\n :alt: Documentation Status\n \n.. image:: https://badge.fury.io/py/spurplus.svg\n :target: https://pypi.org/project/spurplus/\n :alt: PyPi\n\n.. image:: https://img.shields.io/pypi/pyversions/spurplus.svg\n :alt: PyPI - Python Version\n\nSpur+ is a library to manage remote machines and perform file operations over SSH.\n\nIt builds on top of Spur_ and Paramiko_ libraries. While we already find that Spur_ and Paramiko_ provide most of the\nfunctionality out-of-the-box, we missed certain features:\n\n- typing. Since spur supports both Python 2 and 3, it does not provide any type annotations which makes it harder to use\n with type checkers such as mypy.\n\n- pathlib.Path support. We find it easier to manipulate paths using pathlib.Path instead of plain strings. spur+\n provides support for both.\n\n- a function for creating directories. spur relies on sftp client. While it is fairly straightforward to get an sftp\n client from ``spur.SshShell`` and create a directory, we think that it merits a wrapper function akin to\n ``pathlib.Path.mkdir()`` provided how often this functionality is needed.\n\n- reading/writing text and binary data in one go. Similarly to creating directories, ``spur.SshShell.open()`` already\n provides all the functionality you need to read/write files. However, we found the usage code to be more readable when\n written in one line and no extra variables for file descriptors are introduced.\n\n- a function for putting and getting files to/from the remote host, respectively.\n\n- a function to sync a local directory to a remote directory (similar to ``rsync``).\n\n- a function for computing MD5 checksums.\n\n- a function to check if a file exists.\n\n- a more elaborate context manager for a temporary directory which allows for specifying prefix, suffix and\n base directory and gives you a pathlib.Path. In contrast, ``spur.temporary_directory()`` gives you only a string with\n no knobs.\n\n- an initializer function to repeatedly re-connect on connection failure. We found this function particularly important\n when you spin a virtual instance in the cloud and need to wait for it to initialize.\n\n- a wrapper around paramiko's SFTP client (``spurplus.sftp.ReconnectingSFTP``) to automatically reconnect if the SFTP\n client experienced a connection failure. While original ``spur.SshShell.open()`` creates a new SFTP client on every\n call in order to prevent issues with time-outs, `spurplus.SshShell` is able to re-use the SFTP client over multiple\n calls via ``spurplus.sftp.ReconnectingSFTP``.\n\n This can lead up to 10x speed-up (see the benchmark in ``tests/live_test.py``).\n\n.. _Spur: https://github.com/mwilliamson/spur.py\n.. _Paramiko: https://github.com/paramiko/paramiko\n\nUsage\n=====\n.. code-block:: python\n\n import pathlib\n\n import spurplus\n\n # Re-try on connection failure; sftp client and the underlying spur SshShell\n # are automatically closed when the shell is closed.\n with spurplus.connect_with_retries(\n hostname='some-machine.example.com', username='devop') as shell:\n p = pathlib.Path('/some/directory')\n\n # Create a directory\n shell.mkdir(remote_path=p, parents=True, exist_ok=True)\n\n # Write a file\n shell.write_text(remote_path=p/'some-file', text='hello world!')\n\n # Read from a file\n text = shell.read_text(remote_path=p/'some-file')\n\n # Change the permissions\n shell.chmod(remote_path=p/'some-file', mode=0o444)\n\n # Sync a local directory to a remote.\n # Only differing files are uploaded,\n # files missing locally are deleted before the transfer and\n # the permissions are mirrored from the local.\n sync_to_remote(\n local_path=\"/some/local/directory\",\n remote_path=\"/some/remote/directory\",\n delete=spurplus.Delete.BEFORE,\n preserve_permissions = True)\n\n # Stat the file\n print(\"The stat of {}: {}\".format(p/'some-file', shell.stat(p/'some-file')))\n\n # Use a wrapped SFTP client\n sftp = shell.as_sftp()\n # Do something with the SFTP\n for attr in sftp.listdir_attr(path=p.as_posix()):\n do_something(attr.filename, attr.st_size)\n\nDocumentation\n=============\nThe documentation is available on `readthedocs `_.\n\nInstallation\n============\n\n* Create a virtual environment:\n\n.. code-block:: bash\n\n python3 -m venv venv3\n\n* Activate it:\n\n.. code-block:: bash\n\n source venv3/bin/activate\n\n* Install spur+ with pip:\n\n.. code-block:: bash\n\n pip3 install spurplus\n\nDevelopment\n===========\n\n* Check out the repository.\n\n* In the repository root, create the virtual environment:\n\n.. code-block:: bash\n\n python3 -m venv venv3\n\n* Activate the virtual environment:\n\n.. code-block:: bash\n\n source venv3/bin/activate\n\n* Install the development dependencies:\n\n.. code-block:: bash\n\n pip3 install -e .[dev]\n\n* There are live tests for which you need to have a running SSH server. The parameters of the tests\n are passed via environment variables:\n\n - ``TEST_SSH_HOSTNAME`` (host name of the SSH server, defaults to \"127.0.0.1\"),\n - ``TEST_SSH_PORT`` (optional, defaults to 22),\n - ``TEST_SSH_USERNAME`` (optional, uses paramiko's default),\n - ``TEST_SSH_PASSWORD`` (optional, uses private key file if not specified) and\n - ``TEST_SSH_PRIVATE_KEY_FILE`` (optional, looks for private key in expected places if not specified).\n\nWe use tox for testing and packaging the distribution. Assuming that the above-mentioned environment variables has\nbeen set, the virutal environment has been activated and the development dependencies have been installed, run:\n\n.. code-block:: bash\n\n tox\n\nPre-commit Checks\n-----------------\nWe provide a set of pre-commit checks that lint and check code for formatting.\n\nNamely, we use:\n\n* `yapf `_ to check the formatting.\n* The style of the docstrings is checked with `pydocstyle `_.\n* Static type analysis is performed with `mypy `_.\n* Various linter checks are done with `pylint `_.\n* Doctests are executed using the Python `doctest module `_.\n\nRun the pre-commit checks locally from an activated virtual environment with development dependencies:\n\n.. code-block:: bash\n\n ./precommit.py\n\n* The pre-commit script can also automatically format the code:\n\n.. code-block:: bash\n\n ./precommit.py --overwrite\n\n\nVersioning\n==========\nWe follow `Semantic Versioning `_. The version X.Y.Z indicates:\n\n* X is the major version (backward-incompatible),\n* Y is the minor version (backward-compatible), and\n* Z is the patch version (backward-compatible bug fix).", "description_content_type": "", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "http://github.com/Parquery/spurplus", "keywords": "ssh sftp spur paramiko execute remote commands modify files", "license": "License :: OSI Approved :: MIT License", "maintainer": "", "maintainer_email": "", "name": "spurplus", "package_url": "https://pypi.org/project/spurplus/", "platform": "", "project_url": "https://pypi.org/project/spurplus/", "project_urls": { "Homepage": "http://github.com/Parquery/spurplus" }, "release_url": "https://pypi.org/project/spurplus/2.3.3/", "requires_dist": null, "requires_python": "", "summary": "Manage remote machines and file operations over SSH.", "version": "2.3.3" }, "last_serial": 5958847, "releases": { "1.0.0": [ { "comment_text": "", "digests": { "md5": "3db283405a9df37a6fffc0bb8a4322a0", "sha256": "3d2a9b73dc8f4429388bbce1b849d0d60dcf29ba1fb91ded16f361cb3b673392" }, "downloads": -1, "filename": "spurplus-1.0.0.tar.gz", "has_sig": false, "md5_digest": "3db283405a9df37a6fffc0bb8a4322a0", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 13424, "upload_time": "2018-04-30T07:36:35", "url": "https://files.pythonhosted.org/packages/28/f7/6d1b9577bc781c7fc2f2b7b9091cb0e3e34c139262293b778e12167b7336/spurplus-1.0.0.tar.gz" } ], "1.0.1": [ { "comment_text": "", "digests": { "md5": "4f4b25de5ffe33bb0216bc78b69b0fea", "sha256": "9882cc120db31b4d998d4caf5d4aa93421789e54877e1420c7206e725cf103ae" }, "downloads": -1, "filename": "spurplus-1.0.1.tar.gz", "has_sig": false, "md5_digest": "4f4b25de5ffe33bb0216bc78b69b0fea", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 14197, "upload_time": "2018-05-03T05:59:12", "url": "https://files.pythonhosted.org/packages/cc/72/e52179b8547505c435ae1659fce7416b5ec70ced01fc70af616c56bf45e7/spurplus-1.0.1.tar.gz" } ], "1.1.0": [ { "comment_text": "", "digests": { "md5": "e897de395b6ebdbcdd0082190278dad0", "sha256": "952cb18782aa26ba88fc1d5ad15ac71b5b7bf38ff531ca1914882740107c770f" }, "downloads": -1, "filename": "spurplus-1.1.0.tar.gz", "has_sig": false, "md5_digest": "e897de395b6ebdbcdd0082190278dad0", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 15056, "upload_time": "2018-06-08T08:06:39", "url": "https://files.pythonhosted.org/packages/66/b4/6abdd8338e904eb192d1d8719b2f65f5d7f740eec7fc595ff83967b48624/spurplus-1.1.0.tar.gz" } ], "1.1.1": [ { "comment_text": "", "digests": { "md5": "f368243f331662b40ec9b2102706e228", "sha256": "0b7a18b637218f808ba818d5332b731106b8f1137436bd8b2ec59c64b8df4610" }, "downloads": -1, "filename": "spurplus-1.1.1.tar.gz", "has_sig": false, "md5_digest": "f368243f331662b40ec9b2102706e228", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 15009, "upload_time": "2018-08-03T14:30:27", "url": "https://files.pythonhosted.org/packages/aa/a8/fe6c5d6041600345e70671d7bc1c6288adfd4d8ef66380ac3c7176e12600/spurplus-1.1.1.tar.gz" } ], "1.1.2": [ { "comment_text": "", "digests": { "md5": "b590bb89b5a0551bb384f4a8da5181f0", "sha256": "1e57dfcc5a2dfd55f3c9ff8f3476f9904ed4ed5f383cce4e321b719ba4fb2434" }, "downloads": -1, "filename": "spurplus-1.1.2.tar.gz", "has_sig": false, "md5_digest": "b590bb89b5a0551bb384f4a8da5181f0", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 15078, "upload_time": "2018-08-03T15:08:44", "url": "https://files.pythonhosted.org/packages/1c/61/3eeebc5598e6680f6f973ce6c888d58178a93d5120e1ad6ad760bcbbaf06/spurplus-1.1.2.tar.gz" } ], "1.2.0": [ { "comment_text": "", "digests": { "md5": "8e09ce81e6e0cc33d6e1591ef04801a0", "sha256": "0b43d10be4bee926ac7b03a8716a86d1a933a56312ac049ff715b5eb79db410b" }, "downloads": -1, "filename": "spurplus-1.2.0.tar.gz", "has_sig": false, "md5_digest": "8e09ce81e6e0cc33d6e1591ef04801a0", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 18458, "upload_time": "2018-08-21T15:02:55", "url": "https://files.pythonhosted.org/packages/8e/e2/fe6aa4f845ff8714061992616455c8a31a9d86f397523936a684f4452fde/spurplus-1.2.0.tar.gz" } ], "1.2.1": [ { "comment_text": "", "digests": { "md5": "774a782150b9870d9afe8901879c7ba3", "sha256": "2c6083703d5c851bda0210f5f70ac38ab0f412b698278220c5b54a055d16c8bb" }, "downloads": -1, "filename": "spurplus-1.2.1.tar.gz", "has_sig": false, "md5_digest": "774a782150b9870d9afe8901879c7ba3", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 18978, "upload_time": "2018-08-22T00:29:04", "url": "https://files.pythonhosted.org/packages/69/37/880cbe8355744dd04c60e14ab344809f1cf6a2560c9cf076d919592c95b9/spurplus-1.2.1.tar.gz" } ], "1.2.2": [ { "comment_text": "", "digests": { "md5": "d8802e9a96fa0d5ff7426ad98fa63968", "sha256": "f64b94bd22497fdfbc4188cd66ea74cc6b704b27371736306693806bf25e7cb2" }, "downloads": -1, "filename": "spurplus-1.2.2.tar.gz", "has_sig": false, "md5_digest": "d8802e9a96fa0d5ff7426ad98fa63968", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 19461, "upload_time": "2018-08-24T11:53:33", "url": "https://files.pythonhosted.org/packages/3b/1d/253c79b659315c9265f8cef5bff8e5de5a7f76facb41a263d48d92341f21/spurplus-1.2.2.tar.gz" } ], "1.2.3": [ { "comment_text": "", "digests": { "md5": "6329249ac0d4fca1e185bbb473d1b9a6", "sha256": "220c51754b45a7568816235ba6c57c51d91c98911c330d6bbe3a5d592ef17b74" }, "downloads": -1, "filename": "spurplus-1.2.3.tar.gz", "has_sig": false, "md5_digest": "6329249ac0d4fca1e185bbb473d1b9a6", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 20272, "upload_time": "2018-08-24T12:15:52", "url": "https://files.pythonhosted.org/packages/dc/4a/3557e2288fd773978f63cfb1c08f39226bc7fbd6c53a3c6d6c7cd5a3ab61/spurplus-1.2.3.tar.gz" } ], "1.2.4": [ { "comment_text": "", "digests": { "md5": "72358990ec58ca0350f1bf8d9f3af4f9", "sha256": "18b9b7b81fa521c95563667644f50534522ee1e89de4e368a027ce67c5c2f7d7" }, "downloads": -1, "filename": "spurplus-1.2.4.tar.gz", "has_sig": false, "md5_digest": "72358990ec58ca0350f1bf8d9f3af4f9", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 20271, "upload_time": "2018-08-24T12:50:02", "url": "https://files.pythonhosted.org/packages/a7/c2/8c33831a004537470ca0cf21e54eb46bf950f300a3bd8ab16e7e64dd5c1a/spurplus-1.2.4.tar.gz" } ], "1.2.5": [ { "comment_text": "", "digests": { "md5": "2038ffcf7907ae8826318bde7277a53d", "sha256": "e670f307f750cfa7e9fa18c14bc4e3fab27d15cee6d656cebcd0dd3cbb537538" }, "downloads": -1, "filename": "spurplus-1.2.5.tar.gz", "has_sig": false, "md5_digest": "2038ffcf7907ae8826318bde7277a53d", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 20417, "upload_time": "2018-08-28T10:20:49", "url": "https://files.pythonhosted.org/packages/12/32/059b15b45e3ad8f7fc0e5958d68e53b4c1e57ab922a739d62eff1aed3111/spurplus-1.2.5.tar.gz" } ], "2.0.0": [ { "comment_text": "", "digests": { "md5": "d5456cbe685d970acb544014e49bbff2", "sha256": "e0233874d14d00ab41a4822e6ccbbfe472528469ce7d1477633e4624bdc5d4d0" }, "downloads": -1, "filename": "spurplus-2.0.0.tar.gz", "has_sig": false, "md5_digest": "d5456cbe685d970acb544014e49bbff2", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 20000, "upload_time": "2018-10-02T15:20:46", "url": "https://files.pythonhosted.org/packages/bd/68/0bcea1cd065db2511302569c4e16411dc85c43227773612bf974718bc210/spurplus-2.0.0.tar.gz" } ], "2.1.0": [ { "comment_text": "", "digests": { "md5": "a017295825c57f3fc5a52e349f728e39", "sha256": "9c90ff2a087a5930a5fe28a6dad6f1aab5b2ed02b3fe71c84089ee1b39ec7ad6" }, "downloads": -1, "filename": "spurplus-2.1.0.tar.gz", "has_sig": false, "md5_digest": "a017295825c57f3fc5a52e349f728e39", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 20084, "upload_time": "2018-10-05T08:40:20", "url": "https://files.pythonhosted.org/packages/b9/1a/a69efe92c59889885dfb5411d749d20162c1056397a6f67726178fc7855a/spurplus-2.1.0.tar.gz" } ], "2.1.1": [ { "comment_text": "", "digests": { "md5": "094e56e671fe7579550275f87bae75d2", "sha256": "c5a5cd219e649834ec7e3193dcbfaf80b9f0c47d07ed54a175a20c29c29d6302" }, "downloads": -1, "filename": "spurplus-2.1.1.tar.gz", "has_sig": false, "md5_digest": "094e56e671fe7579550275f87bae75d2", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 20208, "upload_time": "2018-10-19T10:09:46", "url": "https://files.pythonhosted.org/packages/2b/44/179ee3da95c6b29fb2a09a66a20d717aa7bf5137720134cdd1b9aa22aa0e/spurplus-2.1.1.tar.gz" } ], "2.1.2": [ { "comment_text": "", "digests": { "md5": "9157280453c39e88b81070da815ab828", "sha256": "bfb376dea8ef3dc84318633af838cc8c631c6a01e1990d9f88f5acaadfb6b9ca" }, "downloads": -1, "filename": "spurplus-2.1.2.tar.gz", "has_sig": false, "md5_digest": "9157280453c39e88b81070da815ab828", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 20222, "upload_time": "2018-10-23T12:20:28", "url": "https://files.pythonhosted.org/packages/9a/cc/4ff38b4ab92c75896fe0949fd649f8379212832d32a2867cfffd6309535d/spurplus-2.1.2.tar.gz" } ], "2.2.0": [ { "comment_text": "", "digests": { "md5": "789186323a537856d1bf02f5ca10ac1b", "sha256": "f02f631aeb13d20982a9135784f99e76092dae780b0f703a432fb2c4bcbbec1e" }, "downloads": -1, "filename": "spurplus-2.2.0.tar.gz", "has_sig": false, "md5_digest": "789186323a537856d1bf02f5ca10ac1b", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 20251, "upload_time": "2018-10-30T11:02:19", "url": "https://files.pythonhosted.org/packages/d5/f1/049a08facd290777b12bf96f9dec8c1210a98bd854c68abf599536033a83/spurplus-2.2.0.tar.gz" } ], "2.2.1": [ { "comment_text": "", "digests": { "md5": "b534fe27bef893683a477482f672594e", "sha256": "5635cab365bd51936d6491364e27f095e6376acca58b4f86d3d797055c7c65bf" }, "downloads": -1, "filename": "spurplus-2.2.1.tar.gz", "has_sig": false, "md5_digest": "b534fe27bef893683a477482f672594e", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 20252, "upload_time": "2019-03-01T10:17:53", "url": "https://files.pythonhosted.org/packages/0a/20/c917c027f7cfad96e2967e8edc2bbba56b805292e73f0da4b41906db51d1/spurplus-2.2.1.tar.gz" } ], "2.3.0": [ { "comment_text": "", "digests": { "md5": "cba3a374d0d4bbca72a014e6dd5b86d4", "sha256": "94547d40eebcbbd3eea771b92dc34fc9caccaeb596d3d4df6392bdb4481ba3d1" }, "downloads": -1, "filename": "spurplus-2.3.0.tar.gz", "has_sig": false, "md5_digest": "cba3a374d0d4bbca72a014e6dd5b86d4", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 20619, "upload_time": "2019-03-01T13:25:00", "url": "https://files.pythonhosted.org/packages/40/97/82678772a988074b84ea64b99fa0ced80e0bd9bc67cd8f5a5c4541518fe1/spurplus-2.3.0.tar.gz" } ], "2.3.1": [ { "comment_text": "", "digests": { "md5": "8ed660a68365e98b89f2401187deae49", "sha256": "dd6423f6189976c30f82ed4493d21c19dc1fdbb01977b748ff76456a354c583c" }, "downloads": -1, "filename": "spurplus-2.3.1.tar.gz", "has_sig": false, "md5_digest": "8ed660a68365e98b89f2401187deae49", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 20693, "upload_time": "2019-04-09T16:04:28", "url": "https://files.pythonhosted.org/packages/aa/34/1e7f8011553d10aa3351571288617eb2923da02d7a7c68c4c6fba9584d12/spurplus-2.3.1.tar.gz" } ], "2.3.2": [ { "comment_text": "", "digests": { "md5": "d4b3947b3bcc78eeaf3035d1906001ea", "sha256": "2ede4daae1339da5c296ec2f81316f87a6de52e231471499eee4bc18a6b3bfe2" }, "downloads": -1, "filename": "spurplus-2.3.2.tar.gz", "has_sig": false, "md5_digest": "d4b3947b3bcc78eeaf3035d1906001ea", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 19460, "upload_time": "2019-09-11T15:01:16", "url": "https://files.pythonhosted.org/packages/3d/17/72bd715c6fdcc53ba7c693cd400aecf745c2836cca41a9340270a984cc37/spurplus-2.3.2.tar.gz" } ], "2.3.3": [ { "comment_text": "", "digests": { "md5": "209475ede404089f77d8b2cc0a57070e", "sha256": "71c734a0827a68235d5b610c3570c3abc0c6be56708a340ba7bebc0a6eb77e92" }, "downloads": -1, "filename": "spurplus-2.3.3.tar.gz", "has_sig": false, "md5_digest": "209475ede404089f77d8b2cc0a57070e", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 19646, "upload_time": "2019-10-11T07:42:07", "url": "https://files.pythonhosted.org/packages/84/88/073f7f5b573bb2eae397ba94f4f40116bdb5c3f9ae4d65bb1b645e6dc26f/spurplus-2.3.3.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "209475ede404089f77d8b2cc0a57070e", "sha256": "71c734a0827a68235d5b610c3570c3abc0c6be56708a340ba7bebc0a6eb77e92" }, "downloads": -1, "filename": "spurplus-2.3.3.tar.gz", "has_sig": false, "md5_digest": "209475ede404089f77d8b2cc0a57070e", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 19646, "upload_time": "2019-10-11T07:42:07", "url": "https://files.pythonhosted.org/packages/84/88/073f7f5b573bb2eae397ba94f4f40116bdb5c3f9ae4d65bb1b645e6dc26f/spurplus-2.3.3.tar.gz" } ] }