{"id":40498973,"url":"https://github.com/sarugaku/vistir","last_synced_at":"2026-01-20T19:03:29.639Z","repository":{"id":49823772,"uuid":"144255379","full_name":"sarugaku/vistir","owner":"sarugaku","description":"Setup / utilities which most projects eventually need","archived":false,"fork":false,"pushed_at":"2023-03-04T07:57:47.000Z","size":1377,"stargazers_count":36,"open_issues_count":0,"forks_count":13,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-12-08T13:54:51.987Z","etag":null,"topics":["development","miscellaneous-utilities","setup","tools","utilities"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"isc","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sarugaku.png","metadata":{"files":{"readme":"README.rst","changelog":"CHANGELOG.rst","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["oz123","matteius"],"patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"custom":null}},"created_at":"2018-08-10T07:53:35.000Z","updated_at":"2025-10-03T08:14:57.000Z","dependencies_parsed_at":"2024-06-18T19:59:35.686Z","dependency_job_id":null,"html_url":"https://github.com/sarugaku/vistir","commit_stats":{"total_commits":460,"total_committers":18,"mean_commits":"25.555555555555557","dds":"0.31739130434782614","last_synced_commit":"be79c335891e7dc164e28c6739ee9eb7e6ad6b9b"},"previous_names":[],"tags_count":36,"template":false,"template_full_name":null,"purl":"pkg:github/sarugaku/vistir","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sarugaku%2Fvistir","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sarugaku%2Fvistir/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sarugaku%2Fvistir/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sarugaku%2Fvistir/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sarugaku","download_url":"https://codeload.github.com/sarugaku/vistir/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sarugaku%2Fvistir/sbom","scorecard":{"id":800926,"data":{"date":"2025-08-11","repo":{"name":"github.com/sarugaku/vistir","commit":"be79c335891e7dc164e28c6739ee9eb7e6ad6b9b"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.7,"checks":[{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Code-Review","score":2,"reason":"Found 3/12 approved changesets -- score normalized to 2","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/ci.yml:1","Warn: no topLevel permission defined: .github/workflows/pypi_upload.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:42: update your workflow using https://app.stepsecurity.io/secureworkflow/sarugaku/vistir/ci.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:45: update your workflow using https://app.stepsecurity.io/secureworkflow/sarugaku/vistir/ci.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/pypi_upload.yml:18: update your workflow using https://app.stepsecurity.io/secureworkflow/sarugaku/vistir/pypi_upload.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/pypi_upload.yml:22: update your workflow using https://app.stepsecurity.io/secureworkflow/sarugaku/vistir/pypi_upload.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/pypi_upload.yml:32: update your workflow using https://app.stepsecurity.io/secureworkflow/sarugaku/vistir/pypi_upload.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/pypi_upload.yml:58: update your workflow using https://app.stepsecurity.io/secureworkflow/sarugaku/vistir/pypi_upload.yml/master?enable=pin","Warn: pipCommand not pinned by hash: .github/workflows/ci.yml:56","Warn: pipCommand not pinned by hash: .github/workflows/ci.yml:57","Warn: pipCommand not pinned by hash: .github/workflows/ci.yml:74","Warn: pipCommand not pinned by hash: .github/workflows/pypi_upload.yml:38","Info:   0 out of   5 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   1 third-party GitHubAction dependencies pinned","Info:   0 out of   4 pipCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: ISC License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 23 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":0,"reason":"29 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: PYSEC-2024-48 / GHSA-fj7x-q9j7-g6q6","Warn: Project is vulnerable to: PYSEC-2024-230 / GHSA-248v-346w-9cwc","Warn: Project is vulnerable to: PYSEC-2023-135 / GHSA-xqr8-7jwr-rhp7","Warn: Project is vulnerable to: GHSA-3ww4-gg4f-jr7f","Warn: Project is vulnerable to: GHSA-5cpq-8wj7-hf2v","Warn: Project is vulnerable to: PYSEC-2024-225 / GHSA-6vqw-3v5j-54x4","Warn: Project is vulnerable to: GHSA-9v9h-cgj8-h64p","Warn: Project is vulnerable to: GHSA-h4gh-qq45-vh27","Warn: Project is vulnerable to: PYSEC-2023-254 / GHSA-jfhm-5ghh-2f97","Warn: Project is vulnerable to: GHSA-jm77-qphf-c4w8","Warn: Project is vulnerable to: GHSA-v8gr-m533-ghj9","Warn: Project is vulnerable to: PYSEC-2024-60 / GHSA-jjg7-2v4v-x38h","Warn: Project is vulnerable to: GHSA-cpwx-vrp4-4pq7","Warn: Project is vulnerable to: GHSA-gmj6-6f8f-6699","Warn: Project is vulnerable to: GHSA-h5c8-rqwp-cp95","Warn: Project is vulnerable to: GHSA-h75v-3vvj-5mfj","Warn: Project is vulnerable to: GHSA-q2x7-8rv6-6q7h","Warn: Project is vulnerable to: PYSEC-2023-117 / GHSA-mrwq-x4v8-fh7p","Warn: Project is vulnerable to: GHSA-9hjg-9r4m-mvj7","Warn: Project is vulnerable to: GHSA-9wx4-h78v-vm56","Warn: Project is vulnerable to: PYSEC-2023-74 / GHSA-j8r2-6x86-q33q","Warn: Project is vulnerable to: PYSEC-2025-49 / GHSA-5rjg-fvgr-3xxf","Warn: Project is vulnerable to: GHSA-cx63-2mw6-8hw5","Warn: Project is vulnerable to: GHSA-34jh-p97f-mpxf","Warn: Project is vulnerable to: PYSEC-2023-212 / GHSA-g4mx-q9vg-27p4","Warn: Project is vulnerable to: GHSA-pq67-6m6q-mj2v","Warn: Project is vulnerable to: PYSEC-2023-192 / GHSA-v845-jxx5-vc9f","Warn: Project is vulnerable to: PYSEC-2024-187 / GHSA-rqc4-2hc7-8c8v","Warn: Project is vulnerable to: GHSA-jfmj-5v4g-7637"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-23T10:27:49.450Z","repository_id":49823772,"created_at":"2025-08-23T10:27:49.450Z","updated_at":"2025-08-23T10:27:49.450Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28609579,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-20T18:56:40.769Z","status":"ssl_error","status_checked_at":"2026-01-20T18:54:26.653Z","response_time":117,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["development","miscellaneous-utilities","setup","tools","utilities"],"created_at":"2026-01-20T19:02:43.577Z","updated_at":"2026-01-20T19:03:29.566Z","avatar_url":"https://github.com/sarugaku.png","language":"Python","funding_links":["https://github.com/sponsors/oz123","https://github.com/sponsors/matteius"],"categories":[],"sub_categories":[],"readme":"===============================================================================\nvistir: Setup / utilities which most projects eventually need\n===============================================================================\n\n.. image:: https://img.shields.io/pypi/v/vistir.svg\n    :target: https://pypi.python.org/pypi/vistir\n\n.. image:: https://img.shields.io/pypi/l/vistir.svg\n    :target: https://pypi.python.org/pypi/vistir\n\n.. image:: https://travis-ci.com/sarugaku/vistir.svg?branch=master\n    :target: https://travis-ci.com/sarugaku/vistir\n\n.. image:: https://img.shields.io/pypi/pyversions/vistir.svg\n    :target: https://pypi.python.org/pypi/vistir\n\n.. image:: https://img.shields.io/badge/Say%20Thanks-!-1EAEDB.svg\n    :target: https://saythanks.io/to/techalchemy\n\n.. image:: https://readthedocs.org/projects/vistir/badge/?version=latest\n    :target: https://vistir.readthedocs.io/en/latest/?badge=latest\n    :alt: Documentation Status\n\n.. image:: https://dev.azure.com/sarugaku/vistir/_apis/build/status/Vistir%20Build%20Pipeline?branchName=master\n    :target: https://dev.azure.com/sarugaku/vistir/_build/latest?definitionId=2\u0026branchName=master\n\n\n🐉 Installation\n=================\n\nInstall from `PyPI`_:\n\n  ::\n\n    $ pipenv install vistir\n\nInstall from `Github`_:\n\n  ::\n\n    $ pipenv install -e git+https://github.com/sarugaku/vistir.git#egg=vistir\n\n\n.. _PyPI: https://www.pypi.org/project/vistir\n.. _Github: https://github.com/sarugaku/vistir\n\n\n.. _`Summary`:\n\n🐉 Summary\n===========\n\n**vistir** is a library full of utility functions designed to make life easier. Here are\nsome of the places where these functions are used:\n\n  * `pipenv`_\n  * `requirementslib`_\n  * `pip-tools`_\n  * `passa`_\n  * `pythonfinder`_\n\n.. _passa: https://github.com/sarugaku/passa\n.. _pipenv: https://github.com/pypa/pipenv\n.. _pip-tools: https://github.com/jazzband/pip-tools\n.. _requirementslib: https://github.com/sarugaku/requirementslib\n.. _pythonfinder: https://github.com/sarugaku/pythonfinder\n\n\n.. _`Usage`:\n\n🐉 Usage\n==========\n\nImporting a utility\n--------------------\n\nYou can import utilities directly from **vistir**:\n\n.. code:: python\n\n    from vistir import cd\n    with cd('/path/to/somedir'):\n        do_stuff_in('somedir')\n\n\n.. _`Functionality`:\n\n🐉 Functionality\n==================\n\n**vistir** provides several categories of functionality, including:\n\n    * Compatibility Shims\n    * Context Managers\n    * Miscellaneous Utilities\n    * Path Utilities\n\n🐉 Context Managers\n--------------------\n\n**vistir** provides the following context managers as utility contexts:\n\n    * ``vistir.contextmanagers.atomic_open_for_write``\n    * ``vistir.contextmanagers.cd``\n    * ``vistir.contextmanagers.open_file``\n    * ``vistir.contextmanagers.replaced_stream``\n    * ``vistir.contextmanagers.replaced_streams``\n    * ``vistir.contextmanagers.temp_environ``\n    * ``vistir.contextmanagers.temp_path``\n\n\n.. _`atomic_open_for_write`:\n\n**atomic_open_for_write**\n///////////////////////////\n\nThis context manager ensures that a file only gets overwritten if the contents can be\nsuccessfully written in its place.  If you open a file for writing and then fail in the\nmiddle under normal circumstances, your original file is already gone.\n\n.. code:: python\n\n    \u003e\u003e\u003e fn = \"test_file.txt\"\n    \u003e\u003e\u003e with open(fn, \"w\") as fh:\n            fh.write(\"this is some test text\")\n    \u003e\u003e\u003e read_test_file()\n    this is some test text\n    \u003e\u003e\u003e def raise_exception_while_writing(filename):\n            with vistir.contextmanagers.atomic_open_for_write(filename) as fh:\n                fh.write(\"Overwriting all the text from before with even newer text\")\n                raise RuntimeError(\"But did it get overwritten now?\")\n    \u003e\u003e\u003e raise_exception_while_writing(fn)\n        Traceback (most recent call last):\n            ...\n        RuntimeError: But did it get overwritten now?\n    \u003e\u003e\u003e read_test_file()\n    this is some test text\n\n\n.. _`cd`:\n\n**cd**\n///////\n\nA context manager for temporarily changing the working directory.\n\n\n.. code:: python\n\n    \u003e\u003e\u003e os.path.abspath(os.curdir)\n    '/tmp/test'\n    \u003e\u003e\u003e with vistir.contextmanagers.cd('/tmp/vistir_test'):\n            print(os.path.abspath(os.curdir))\n    /tmp/vistir_test\n\n\n.. _`open_file`:\n\n**open_file**\n///////////////\n\nA context manager for streaming file contents, either local or remote. It is recommended\nto pair this with an iterator which employs a sensible chunk size.\n\n\n.. code:: python\n\n    \u003e\u003e\u003e filecontents = b\"\"\n        with vistir.contextmanagers.open_file(\"https://norvig.com/big.txt\") as fp:\n            for chunk in iter(lambda: fp.read(16384), b\"\"):\n                filecontents.append(chunk)\n    \u003e\u003e\u003e import io\n    \u003e\u003e\u003e import shutil\n    \u003e\u003e\u003e filecontents = io.BytesIO(b\"\")\n    \u003e\u003e\u003e with vistir.contextmanagers.open_file(\"https://norvig.com/big.txt\") as fp:\n            shutil.copyfileobj(fp, filecontents)\n\n\n**replaced_stream**\n////////////////////\n\n.. _`replaced_stream`:\n\nA context manager to temporarily swap out *stream_name* with a stream wrapper.  This will\ncapture the stream output and prevent it from being written as normal.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e orig_stdout = sys.stdout\n    \u003e\u003e\u003e with replaced_stream(\"stdout\") as stdout:\n    ...     sys.stdout.write(\"hello\")\n    ...     assert stdout.getvalue() == \"hello\"\n    ...     assert orig_stdout.getvalue() != \"hello\"\n\n    \u003e\u003e\u003e sys.stdout.write(\"hello\")\n    'hello'\n\n\n.. _`replaced_streams`:\n\n**replaced_streams**\n/////////////////////\n\n\nTemporarily replaces both *sys.stdout* and *sys.stderr* and captures anything written\nto these respective targets.\n\n\n.. code-block:: python\n\n    \u003e\u003e\u003e import sys\n    \u003e\u003e\u003e with vistir.contextmanagers.replaced_streams() as streams:\n    \u003e\u003e\u003e     stdout, stderr = streams\n    \u003e\u003e\u003e     sys.stderr.write(\"test\")\n    \u003e\u003e\u003e     sys.stdout.write(\"hello\")\n    \u003e\u003e\u003e     assert stdout.getvalue() == \"hello\"\n    \u003e\u003e\u003e     assert stderr.getvalue() == \"test\"\n\n    \u003e\u003e\u003e stdout.getvalue()\n    'hello'\n\n    \u003e\u003e\u003e stderr.getvalue()\n    'test'\n\n\n.. _`spinner`:\n\n**spinner**\n////////////\n\nA context manager for wrapping some actions with a threaded, interrupt-safe spinner. The\nspinner is fully compatible with all terminals (you can use ``bouncingBar`` on non-utf8\nterminals) and will allow you to update the text of the spinner itself by simply setting\n``spinner.text`` or write lines to the screen above the spinner by using\n``spinner.write(line)``. Success text can be indicated using ``spinner.ok(\"Text\")`` and\nfailure text can be indicated with ``spinner.fail(\"Fail text\")``.\n\n.. code:: python\n\n    \u003e\u003e\u003e lines = [\"a\", \"b\"]\n    \u003e\u003e\u003e with vistir.contextmanagers.spinner(spinner_name=\"dots\", text=\"Running...\", handler_map={}, nospin=False) as sp:\n            for line in lines:\n            sp.write(line + \"\\n\")\n            while some_variable = some_queue.pop():\n                sp.text = \"Consuming item: %s\" % some_variable\n            if success_condition:\n                sp.ok(\"Succeeded!\")\n            else:\n                sp.fail(\"Failed!\")\n\n\n.. _`temp_environ`:\n\n**temp_environ**\n/////////////////\n\nSets a temporary environment context to freely manipulate ``os.environ`` which will\nbe reset upon exiting the context.\n\n\n.. code:: python\n\n    \u003e\u003e\u003e os.environ['MY_KEY'] = \"test\"\n    \u003e\u003e\u003e os.environ['MY_KEY']\n    'test'\n    \u003e\u003e\u003e with vistir.contextmanagers.temp_environ():\n            os.environ['MY_KEY'] = \"another thing\"\n            print(\"New key: %s\" % os.environ['MY_KEY'])\n    New key: another thing\n    \u003e\u003e\u003e os.environ['MY_KEY']\n    'test'\n\n\n.. _`temp_path`:\n\n**temp_path**\n//////////////\n\nSets a temporary environment context to freely manipulate ``sys.path`` which will\nbe reset upon exiting the context.\n\n\n.. code:: python\n\n    \u003e\u003e\u003e path_from_virtualenv = load_path(\"/path/to/venv/bin/python\")\n    \u003e\u003e\u003e print(sys.path)\n    ['/home/user/.pyenv/versions/3.7.0/bin', '/home/user/.pyenv/versions/3.7.0/lib/python37.zip', '/home/user/.pyenv/versions/3.7.0/lib/python3.7', '/home/user/.pyenv/versions/3.7.0/lib/python3.7/lib-dynload', '/home/user/.pyenv/versions/3.7.0/lib/python3.7/site-packages']\n    \u003e\u003e\u003e with temp_path():\n            sys.path = path_from_virtualenv\n            # Running in the context of the path above\n            run([\"pip\", \"install\", \"stuff\"])\n    \u003e\u003e\u003e print(sys.path)\n    ['/home/user/.pyenv/versions/3.7.0/bin', '/home/user/.pyenv/versions/3.7.0/lib/python37.zip', '/home/user/.pyenv/versions/3.7.0/lib/python3.7', '/home/user/.pyenv/versions/3.7.0/lib/python3.7/lib-dynload', '/home/user/.pyenv/versions/3.7.0/lib/python3.7/site-packages']\n\n\n🐉 Cursor Utilities\n--------------------------\n\nThe following Cursor utilities are available to manipulate the console cursor:\n\n    * ``vistir.cursor.hide_cursor``\n    * ``vistir.cursor.show_cursor``\n\n\n.. _`hide_cursor`:\n\n**hide_cursor**\n/////////////////\n\nHide the console cursor in the given stream.\n\n.. code:: python\n\n    \u003e\u003e\u003e vistir.cursor.hide_cursor(stream=sys.stdout)\n\n\n.. _`show_cursor`:\n\n**show_cursor**\n/////////////////\n\nShow the console cursor in the given stream.\n\n.. code:: python\n\n    \u003e\u003e\u003e vistir.cursor.show_cursor(stream=sys.stdout)\n\n\n🐉 Miscellaneous Utilities\n--------------------------\n\nThe following Miscellaneous utilities are available as helper methods:\n\n    * ``vistir.misc.shell_escape``\n    * ``vistir.misc.unnest``\n    * ``vistir.misc.run``\n    * ``vistir.misc.load_path``\n    * ``vistir.misc.partialclass``\n    * ``vistir.misc.to_text``\n    * ``vistir.misc.to_bytes``\n    * ``vistir.misc.take``\n    * ``vistir.misc.decode_for_output``\n    * ``vistir.misc.get_canonical_encoding_name``\n    * ``vistir.misc.get_wrapped_stream``\n    * ``vistir.misc.StreamWrapper``\n    * ``vistir.misc.get_text_stream``\n    * ``vistir.misc.replace_with_text_stream``\n    * ``vistir.misc.get_text_stdin``\n    * ``vistir.misc.get_text_stdout``\n    * ``vistir.misc.get_text_stderr``\n    * ``vistir.misc.echo``\n\n\n.. _`shell_escape`:\n\n**shell_escape**\n/////////////////\n\nEscapes a string for use as shell input when passing *shell=True* to ``os.Popen``.\n\n.. code:: python\n\n    \u003e\u003e\u003e vistir.misc.shell_escape(\"/tmp/test/test script.py hello\")\n    '/tmp/test/test script.py hello'\n\n\n.. _`unnest`:\n\n**unnest**\n///////////\n\nUnnests nested iterables into a flattened one.\n\n.. code:: python\n\n    \u003e\u003e\u003e nested_iterable = (1234, (3456, 4398345, (234234)), (2396, (23895750, 9283798, 29384, (289375983275, 293759, 2347, (2098, 7987, 27599)))))\n    \u003e\u003e\u003e list(vistir.misc.unnest(nested_iterable))\n    [1234, 3456, 4398345, 234234, 2396, 23895750, 9283798, 29384, 289375983275, 293759, 2347, 2098, 7987, 27599]\n\n\n.. _`dedup`:\n\n**dedup**\n//////////\n\nDeduplicates an iterable (like a ``set``, but preserving order).\n\n.. code:: python\n\n    \u003e\u003e\u003e iterable = [\"repeatedval\", \"uniqueval\", \"repeatedval\", \"anotherval\", \"somethingelse\"]\n    \u003e\u003e\u003e list(vistir.misc.dedup(iterable))\n    ['repeatedval', 'uniqueval', 'anotherval', 'somethingelse']\n\n.. _`run`:\n\n**run**\n////////\n\nRuns the given command using ``subprocess.Popen`` and passing sane defaults.\n\n.. code:: python\n\n    \u003e\u003e\u003e out, err = vistir.run([\"cat\", \"/proc/version\"])\n    \u003e\u003e\u003e out\n    'Linux version 4.15.0-27-generic (buildd@lgw01-amd64-044) (gcc version 7.3.0 (Ubuntu 7.3.0-16ubuntu3)) #29-Ubuntu SMP Wed Jul 11 08:21:57 UTC 2018'\n\n\n.. _`load_path`:\n\n**load_path**\n//////////////\n\nLoad the ``sys.path`` from the given python executable's environment as json.\n\n.. code:: python\n\n    \u003e\u003e\u003e load_path(\"/home/user/.virtualenvs/requirementslib-5MhGuG3C/bin/python\")\n    ['', '/home/user/.virtualenvs/requirementslib-5MhGuG3C/lib/python37.zip', '/home/user/.virtualenvs/requirementslib-5MhGuG3C/lib/python3.7', '/home/user/.virtualenvs/requirementslib-5MhGuG3C/lib/python3.7/lib-dynload', '/home/user/.pyenv/versions/3.7.0/lib/python3.7', '/home/user/.virtualenvs/requirementslib-5MhGuG3C/lib/python3.7/site-packages', '/home/user/git/requirementslib/src']\n\n\n.. _`partialclass`:\n\n**partialclass**\n/////////////////\n\nCreate a partially instantiated class.\n\n.. code:: python\n\n    \u003e\u003e\u003e source = partialclass(Source, url=\"https://pypi.org/simple\")\n    \u003e\u003e\u003e new_source = source(name=\"pypi\")\n    \u003e\u003e\u003e new_source\n    \u003c__main__.Source object at 0x7f23af189b38\u003e\n    \u003e\u003e\u003e new_source.__dict__\n    {'url': 'https://pypi.org/simple', 'verify_ssl': True, 'name': 'pypi'}\n\n\n.. _`to_text`:\n\n**to_text**\n////////////\n\nConvert arbitrary text-formattable input to text while handling errors.\n\n.. code:: python\n\n    \u003e\u003e\u003e vistir.misc.to_text(b\"these are bytes\")\n    'these are bytes'\n\n\n.. _`to_bytes`:\n\n**to_bytes**\n/////////////\n\nConverts arbitrary byte-convertable input to bytes while handling errors.\n\n.. code:: python\n\n    \u003e\u003e\u003e vistir.misc.to_bytes(\"this is some text\")\n    b'this is some text'\n    \u003e\u003e\u003e vistir.misc.to_bytes(u\"this is some text\")\n    b'this is some text'\n\n\n.. _`chunked`:\n\n**chunked**\n////////////\n\nSplits an iterable up into groups *of the specified length*, per `more itertools`_.  Returns an iterable.\n\nThis example will create groups of chunk size **5**, which means there will be *6 groups*.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e chunked_iterable = vistir.misc.chunked(5, range(30))\n    \u003e\u003e\u003e for chunk in chunked_iterable:\n    ...     add_to_some_queue(chunk)\n\n.. _more itertools: https://more-itertools.readthedocs.io/en/latest/api.html#grouping\n\n\n.. _`take`:\n\n**take**\n/////////\n\nTake elements from the supplied iterable without consuming it.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e iterable = range(30)\n    \u003e\u003e\u003e first_10 = take(10, iterable)\n    \u003e\u003e\u003e [i for i in first_10]\n    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n\n    \u003e\u003e\u003e [i for i in iterable]\n    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29]\n\n\n.. _`divide`:\n\n**divide**\n////////////\n\nSplits an iterable up into the *specified number of groups*, per `more itertools`_.  Returns an iterable.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e iterable = range(30)\n    \u003e\u003e\u003e groups = []\n    \u003e\u003e\u003e for grp in vistir.misc.divide(3, iterable):\n    ...     groups.append(grp)\n    \u003e\u003e\u003e groups\n    [\u003ctuple_iterator object at 0x7fb7966006a0\u003e, \u003ctuple_iterator object at 0x7fb796652780\u003e, \u003ctuple_iterator object at 0x7fb79650a2b0\u003e]\n\n\n.. _more itertools: https://more-itertools.readthedocs.io/en/latest/api.html#grouping\n\n\n.. _`decode_for_output`:\n\n**decode_for_output**\n//////////////////////\n\nConverts an arbitrary text input to output which is encoded for printing to terminal\noutputs using the system preferred locale using ``locale.getpreferredencoding(False)``\nwith some additional hackery on linux systems.\n\n\n.. _`get_canonical_encoding_name`:\n\n**get_canonical_encoding_name**\n////////////////////////////////\n\nGiven an encoding name, get the canonical name from a codec lookup.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e vistir.misc.get_canonical_encoding_name(\"utf8\")\n    \"utf-8\"\n\n\n.. _`get_wrapped_stream`:\n\n**get_wrapped_stream**\n//////////////////////\n\nGiven a stream, wrap it in a `StreamWrapper` instance and return the wrapped stream.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e stream = sys.stdout\n    \u003e\u003e\u003e wrapped_stream = vistir.misc.get_wrapped_stream(sys.stdout)\n    \u003e\u003e\u003e wrapped_stream.write(\"unicode\\u0141\")\n    \u003e\u003e\u003e wrapped_stream.seek(0)\n    \u003e\u003e\u003e wrapped_stream.read()\n    \"unicode\\u0141\"\n\n\n.. _`StreamWrapper`:\n\n**StreamWrapper**\n//////////////////\n\nA stream wrapper and compatibility class for handling wrapping file-like stream objects\nwhich may be used in place of ``sys.stdout`` and other streams.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e wrapped_stream = vistir.misc.StreamWrapper(sys.stdout, encoding=\"utf-8\", errors=\"replace\", line_buffering=True)\n    \u003e\u003e\u003e wrapped_stream = vistir.misc.StreamWrapper(io.StringIO(), encoding=\"utf-8\", errors=\"replace\", line_buffering=True)\n\n\n.. _`get_text_stream`:\n\n**get_text_stream**\n////////////////////\n\nAn implementation of the **StreamWrapper** for the purpose of wrapping **sys.stdin** or **sys.stdout**.\n\nOn Windows, this returns the appropriate handle to the requested output stream.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e text_stream = vistir.misc.get_text_stream(\"stdout\")\n    \u003e\u003e\u003e sys.stdout = text_stream\n    \u003e\u003e\u003e sys.stdin = vistir.misc.get_text_stream(\"stdin\")\n    \u003e\u003e\u003e vistir.misc.echo(u\"\\0499\", fg=\"green\")\n    ҙ\n\n\n.. _`replace_with_text_stream`:\n\n**replace_with_text_stream**\n/////////////////////////////\n\nGiven a text stream name, replaces the text stream with a **StreamWrapper** instance.\n\n\n.. code-block:: python\n\n    \u003e\u003e\u003e vistir.misc.replace_with_text_stream(\"stdout\")\n\nOnce invoked, the standard stream in question is replaced with the required wrapper,\nturning it into a ``TextIOWrapper`` compatible stream (which ensures that unicode\ncharacters can be written to it).\n\n\n.. _`get_text_stdin`:\n\n**get_text_stdin**\n///////////////////\n\nA helper function for calling **get_text_stream(\"stdin\")**.\n\n\n.. _`get_text_stdout`:\n\n**get_text_stdout**\n////////////////////\n\nA helper function for calling **get_text_stream(\"stdout\")**.\n\n\n.. _`get_text_stderr`:\n\n**get_text_stderr**\n////////////////////\n\nA helper function for calling **get_text_stream(\"stderr\")**.\n\n\n.. _`echo`:\n\n**echo**\n/////////\n\nWrites colored, stream-compatible output to the desired handle (``sys.stdout`` by default).\n\n.. code-block:: python\n\n    \u003e\u003e\u003e vistir.misc.echo(\"some text\", fg=\"green\", bg=\"black\", style=\"bold\", err=True)  # write to stderr\n    some text\n    \u003e\u003e\u003e vistir.misc.echo(\"some other text\", fg=\"cyan\", bg=\"white\", style=\"underline\")  # write to stdout\n    some other text\n\n\n🐉 Path Utilities\n------------------\n\n**vistir** provides utilities for interacting with filesystem paths:\n\n    * ``vistir.path.get_converted_relative_path``\n    * ``vistir.path.normalize_path``\n    * ``vistir.path.is_in_path``\n    * ``vistir.path.handle_remove_readonly``\n    * ``vistir.path.is_file_url``\n    * ``vistir.path.is_readonly_path``\n    * ``vistir.path.is_valid_url``\n    * ``vistir.path.mkdir_p``\n    * ``vistir.path.ensure_mkdir_p``\n    * ``vistir.path.create_tracked_tempdir``\n    * ``vistir.path.create_tracked_tempfile``\n    * ``vistir.path.path_to_url``\n    * ``vistir.path.rmtree``\n    * ``vistir.path.safe_expandvars``\n    * ``vistir.path.set_write_bit``\n    * ``vistir.path.url_to_path``\n    * ``vistir.path.walk_up``\n\n\n.. _`normalize_path`:\n\n**normalize_path**\n//////////////////\n\nReturn a case-normalized absolute variable-expanded path.\n\n\n.. code:: python\n\n    \u003e\u003e\u003e vistir.path.normalize_path(\"~/${USER}\")\n    /home/user/user\n\n\n.. _`is_in_path`:\n\n**is_in_path**\n//////////////\n\nDetermine if the provided full path is in the given parent root.\n\n\n.. code:: python\n\n    \u003e\u003e\u003e vistir.path.is_in_path(\"~/.pyenv/versions/3.7.1/bin/python\", \"${PYENV_ROOT}/versions\")\n    True\n\n\n.. _`get_converted_relative_path`:\n\n**get_converted_relative_path**\n////////////////////////////////\n\nConvert the supplied path to a relative path (relative to ``os.curdir``)\n\n\n.. code:: python\n\n    \u003e\u003e\u003e os.chdir('/home/user/code/myrepo/myfolder')\n    \u003e\u003e\u003e vistir.path.get_converted_relative_path('/home/user/code/file.zip')\n    './../../file.zip'\n    \u003e\u003e\u003e vistir.path.get_converted_relative_path('/home/user/code/myrepo/myfolder/mysubfolder')\n    './mysubfolder'\n    \u003e\u003e\u003e vistir.path.get_converted_relative_path('/home/user/code/myrepo/myfolder')\n    '.'\n\n\n.. _`handle_remove_readonly`:\n\n**handle_remove_readonly**\n///////////////////////////\n\nError handler for shutil.rmtree.\n\nWindows source repo folders are read-only by default, so this error handler attempts to\nset them as writeable and then proceed with deletion.\n\nThis function will call check ``vistir.path.is_readonly_path`` before attempting to\ncall ``vistir.path.set_write_bit`` on the target path and try again.\n\n\n.. _`is_file_url`:\n\n**is_file_url**\n////////////////\n\nChecks whether the given url is a properly formatted ``file://`` uri.\n\n.. code:: python\n\n    \u003e\u003e\u003e vistir.path.is_file_url('file:///home/user/somefile.zip')\n    True\n    \u003e\u003e\u003e vistir.path.is_file_url('/home/user/somefile.zip')\n    False\n\n\n.. _`is_readonly_path`:\n\n**is_readonly_path**\n/////////////////////\n\nCheck if a provided path exists and is readonly by checking for ``bool(path.stat \u0026 stat.S_IREAD) and not os.access(path, os.W_OK)``\n\n.. code:: python\n\n    \u003e\u003e\u003e vistir.path.is_readonly_path('/etc/passwd')\n    True\n    \u003e\u003e\u003e vistir.path.is_readonly_path('/home/user/.bashrc')\n    False\n\n\n.. _`is_valid_url`:\n\n**is_valid_url**\n/////////////////\n\nChecks whether a URL is valid and parseable by checking for the presence of a scheme and\na netloc.\n\n.. code:: python\n\n    \u003e\u003e\u003e vistir.path.is_valid_url(\"https://google.com\")\n    True\n    \u003e\u003e\u003e vistir.path.is_valid_url(\"/home/user/somefile\")\n    False\n\n\n.. _`mkdir_p`:\n\n**mkdir_p**\n/////////////\n\nRecursively creates the target directory and all of its parents if they do not\nalready exist.  Fails silently if they do.\n\n.. code:: python\n\n    \u003e\u003e\u003e os.mkdir('/tmp/test_dir')\n    \u003e\u003e\u003e os.listdir('/tmp/test_dir')\n    []\n    \u003e\u003e\u003e vistir.path.mkdir_p('/tmp/test_dir/child/subchild/subsubchild')\n    \u003e\u003e\u003e os.listdir('/tmp/test_dir/child/subchild')\n    ['subsubchild']\n\n\n.. _`ensure_mkdir_p`:\n\n**ensure_mkdir_p**\n///////////////////\n\nA decorator which ensures that the caller function's return value is created as a\ndirectory on the filesystem.\n\n.. code:: python\n\n    \u003e\u003e\u003e @ensure_mkdir_p\n    def return_fake_value(path):\n        return path\n    \u003e\u003e\u003e return_fake_value('/tmp/test_dir')\n    \u003e\u003e\u003e os.listdir('/tmp/test_dir')\n    []\n    \u003e\u003e\u003e return_fake_value('/tmp/test_dir/child/subchild/subsubchild')\n    \u003e\u003e\u003e os.listdir('/tmp/test_dir/child/subchild')\n    ['subsubchild']\n\n\n.. _`create_tracked_tempdir`:\n\n**create_tracked_tempdir**\n////////////////////////////\n\nCreates a tracked temporary directory using ``vistir.path.TemporaryDirectory``, but does\nnot remove the directory when the return value goes out of scope, instead registers a\nhandler to cleanup on program exit.\n\n.. code:: python\n\n    \u003e\u003e\u003e temp_dir = vistir.path.create_tracked_tempdir(prefix=\"test_dir\")\n    \u003e\u003e\u003e assert temp_dir.startswith(\"test_dir\")\n    True\n    \u003e\u003e\u003e with vistir.path.create_tracked_tempdir(prefix=\"test_dir\") as temp_dir:\n        with io.open(os.path.join(temp_dir, \"test_file.txt\"), \"w\") as fh:\n            fh.write(\"this is a test\")\n    \u003e\u003e\u003e os.listdir(temp_dir)\n\n\n.. _`create_tracked_tempfile`:\n\n**create_tracked_tempfile**\n////////////////////////////\n\nCreates a tracked temporary file using ``vistir.compat.NamedTemporaryFile``, but creates\na ``weakref.finalize`` call which will detach on garbage collection to close and delete\nthe file.\n\n.. code:: python\n\n    \u003e\u003e\u003e temp_file = vistir.path.create_tracked_tempfile(prefix=\"requirements\", suffix=\"txt\")\n    \u003e\u003e\u003e temp_file.write(\"some\\nstuff\")\n    \u003e\u003e\u003e exit()\n\n\n.. _`path_to_url`:\n\n**path_to_url**\n////////////////\n\nConvert the supplied local path to a file uri.\n\n.. code:: python\n\n    \u003e\u003e\u003e path_to_url(\"/home/user/code/myrepo/myfile.zip\")\n    'file:///home/user/code/myrepo/myfile.zip'\n\n\n.. _`rmtree`:\n\n**rmtree**\n///////////\n\nStand-in for ``shutil.rmtree`` with additional error-handling.\n\nThis version of `rmtree` handles read-only paths, especially in the case of index files\nwritten by certain source control systems.\n\n.. code:: python\n\n    \u003e\u003e\u003e vistir.path.rmtree('/tmp/test_dir')\n    \u003e\u003e\u003e [d for d in os.listdir('/tmp') if 'test_dir' in d]\n    []\n\n.. note::\n\n    Setting `ignore_errors=True` may cause this to silently fail to delete the path\n\n\n.. _`safe_expandvars`:\n\n**safe_expandvars**\n////////////////////\n\nCall ``os.path.expandvars`` if value is a string, otherwise do nothing.\n\n.. code:: python\n\n    \u003e\u003e\u003e os.environ['TEST_VAR'] = \"MY_TEST_VALUE\"\n    \u003e\u003e\u003e vistir.path.safe_expandvars(\"https://myuser:${TEST_VAR}@myfakewebsite.com\")\n    'https://myuser:MY_TEST_VALUE@myfakewebsite.com'\n\n\n.. _`set_write_bit`:\n\n**set_write_bit**\n//////////////////\n\nSet read-write permissions for the current user on the target path.  Fail silently\nif the path doesn't exist.\n\n.. code:: python\n\n    \u003e\u003e\u003e vistir.path.set_write_bit('/path/to/some/file')\n    \u003e\u003e\u003e with open('/path/to/some/file', 'w') as fh:\n            fh.write(\"test text!\")\n\n\n.. _`url_to_path`:\n\n**url_to_path**\n////////////////\n\nConvert a valid file url to a local filesystem path. Follows logic taken from pip.\n\n.. code:: python\n\n    \u003e\u003e\u003e vistir.path.url_to_path(\"file:///home/user/somefile.zip\")\n    '/home/user/somefile.zip'\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsarugaku%2Fvistir","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsarugaku%2Fvistir","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsarugaku%2Fvistir/lists"}