[
  {
    "path": ".coveragerc",
    "content": "[report]\nshow_missing = True\nexclude_lines =\n    nocover\n"
  },
  {
    "path": ".github/workflows/build.yml",
    "content": "name: Build\non: [push, pull_request]\njobs:\n  test:\n    runs-on: ${{ matrix.os }}\n    strategy:\n      matrix:\n        os: [ubuntu-latest, macOS-latest, windows-latest]\n        python-version: [3.6, 3.7, 3.8, 3.9]\n    steps:\n      - uses: actions/checkout@v1\n      - uses: actions/setup-python@v1\n        with:\n          python-version: ${{ matrix.python-version }}\n      - run: python -m pip install -U pip setuptools wheel\n      - run: python -m pip install -r requirements-test.txt\n      - run: python -m pip install -e .\n      - run: python -m pytest\n"
  },
  {
    "path": ".gitignore",
    "content": "*.egg-info\n*.pyc\n.cache\n.coverage\n.DS_Store\n.python-version\n.tox\n_build\nbuild\ndist\ndata.json\nvenv*\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2016-2021 Chang-Hung Liang\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "MANIFEST.in",
    "content": "include README.rst LICENSE requirements.txt requirements-test.txt\n"
  },
  {
    "path": "Makefile",
    "content": ".PHONY: build\n\ninstall:\n\tpython -m pip install -e .\n\tpython -m pip install -r requirements-test.txt\n\nclean:\n\trm -rf dist/ build/\n\ntest:\n\tpython -m pytest\n\nbuild:\n\tpython setup.py sdist bdist_wheel\n\ncheck:\n\ttwine check dist/*\n\nupload:\n\ttwine upload --repository=http-prompt dist/*\n\nrelease: test clean build check upload\n"
  },
  {
    "path": "README.rst",
    "content": "HTTP Prompt\n===========\n\n|PyPI| |Docs| |Build| |Coverage| |Discord|\n\nHTTP Prompt is an interactive command-line HTTP client featuring autocomplete\nand syntax highlighting, built on HTTPie_ and prompt_toolkit_.\n\n|Asciinema|\n\n\nLinks\n-----\n\n* Home: https://http-prompt.com\n* Documentation: https://docs.http-prompt.com\n* Code: https://github.com/httpie/http-prompt\n* Chat: https://httpie.io/chat\n\n\n.. |PyPI| image:: https://img.shields.io/pypi/v/http-prompt.svg\n    :target: https://pypi.python.org/pypi/http-prompt\n\n.. |Docs| image:: https://img.shields.io/badge/docs-latest-brightgreen.svg?style=flat\n    :target: http://docs.http-prompt.com/en/latest/?badge=latest\n\n.. |Build| image:: https://github.com/httpie/http-prompt/workflows/Build/badge.svg\n    :target: https://github.com/httpie/http-prompt/actions\n\n.. |Coverage| image:: https://coveralls.io/repos/github/eliangcs/http-prompt/badge.svg?branch=master\n    :target: https://coveralls.io/github/eliangcs/http-prompt?branch=master\n\n.. |Discord| image:: https://img.shields.io/badge/chat-on%20Discord-brightgreen?style=flat-square\n    :target: https://httpie.io/chat\n\n.. |Asciinema| image:: https://asciinema.org/a/96613.png\n    :target: https://asciinema.org/a/96613?theme=monokai&size=medium&autoplay=1&speed=1.5\n\n.. _HTTPie: https://httpie.io\n.. _prompt_toolkit: https://github.com/jonathanslenders/python-prompt-toolkit\n"
  },
  {
    "path": "docs/LICENSE",
    "content": "Refer to LICENSE in the main repo:\n\nhttps://github.com/httpie/http-prompt\n"
  },
  {
    "path": "docs/Makefile",
    "content": "# Minimal makefile for Sphinx documentation\n#\n\n# You can set these variables from the command line.\nSPHINXOPTS    =\nSPHINXBUILD   = sphinx-build\nSPHINXPROJ    = HTTPPrompt\nSOURCEDIR     = .\nBUILDDIR      = _build\n\n# Put it first so that \"make\" without argument is like \"make help\".\nhelp:\n\t@$(SPHINXBUILD) -M help \"$(SOURCEDIR)\" \"$(BUILDDIR)\" $(SPHINXOPTS) $(O)\n\n.PHONY: help Makefile\n\n# Catch-all target: route all unknown targets to Sphinx using the new\n# \"make mode\" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).\n%: Makefile\n\t@$(SPHINXBUILD) -M $@ \"$(SOURCEDIR)\" \"$(BUILDDIR)\" $(SPHINXOPTS) $(O)"
  },
  {
    "path": "docs/README.md",
    "content": "# HTTP Prompt Documentation\n\nThis repo contains the documentation for HTTP Prompt, published on\nhttp://docs.http-prompt.com. The source code of HTTP Prompt can be found in the\nmain repo: https://github.com/httpie/http-prompt\n\n## How to Build\n\n```\npip install sphinx\nmake html\nopen _build/html/index.html\n```\n"
  },
  {
    "path": "docs/_templates/layout.html",
    "content": "{%- extends \"!layout.html\" %}\n"
  },
  {
    "path": "docs/conf.py",
    "content": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n#\n# HTTP Prompt documentation build configuration file, created by\n# sphinx-quickstart on Wed Dec 21 20:28:44 2016.\n#\n# This file is execfile()d with the current directory set to its\n# containing dir.\n#\n# Note that not all possible configuration values are present in this\n# autogenerated file.\n#\n# All configuration values have a default; values that are commented out\n# serve to show the default.\n\n# If extensions (or modules to document with autodoc) are in another directory,\n# add these directories to sys.path here. If the directory is relative to the\n# documentation root, use os.path.abspath to make it absolute, like shown here.\n#\n# import os\n# import sys\n# sys.path.insert(0, os.path.abspath('.'))\nfrom collections import OrderedDict\n\nfrom http_prompt import __version__\n\n# -- General configuration ------------------------------------------------\n\n# If your documentation needs a minimal Sphinx version, state it here.\n#\n# needs_sphinx = '1.0'\n\n# Add any Sphinx extension module names here, as strings. They can be\n# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom\n# ones.\nextensions = ['sphinx.ext.autodoc']\n\n# Add any paths that contain templates here, relative to this directory.\ntemplates_path = ['_templates']\n\n# The suffix(es) of source filenames.\n# You can specify multiple suffix as a list of string:\n#\n# source_suffix = ['.rst', '.md']\nsource_suffix = '.rst'\n\n# The master toctree document.\nmaster_doc = 'index'\n\n# General information about the project.\nproject = 'HTTP Prompt'\ncopyright = '2016-17, Chang-Hung Liang'\nauthor = 'Chang-Hung Liang'\n\n# The version info for the project you're documenting, acts as replacement for\n# |version| and |release|, also used in various other places throughout the\n# built documents.\n#\n# The short X.Y version.\nversion = __version__\n# The full version, including alpha/beta/rc tags.\nrelease = version\n\n# The language for content autogenerated by Sphinx. Refer to documentation\n# for a list of supported languages.\n#\n# This is also used if you do content translation via gettext catalogs.\n# Usually you set \"language\" from the command line for these cases.\nlanguage = None\n\n# List of patterns, relative to source directory, that match files and\n# directories to ignore when looking for source files.\n# This patterns also effect to html_static_path and html_extra_path\nexclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']\n\n# The name of the Pygments (syntax highlighting) style to use.\npygments_style = 'sphinx'\n\n# If true, `todo` and `todoList` produce output, else they produce nothing.\ntodo_include_todos = False\n\n\n# -- Options for HTML output ----------------------------------------------\n\n# The theme to use for HTML and HTML Help pages.  See the documentation for\n# a list of builtin themes.\n#\nhtml_theme = 'alabaster'\n\n# Theme options are theme-specific and customize the look and feel of a theme\n# further.  For a list of options available for each theme, see the\n# documentation.\n#\n# html_theme_options = {}\n\n# Add any paths that contain custom static files (such as style sheets) here,\n# relative to this directory. They are copied after the builtin static files,\n# so a file named \"default.css\" will overwrite the builtin \"default.css\".\nhtml_static_path = ['_static']\n\n\n# -- Options for HTMLHelp output ------------------------------------------\n\n# Output file base name for HTML help builder.\nhtmlhelp_basename = 'HTTPPromptdoc'\n\n\n# -- Options for LaTeX output ---------------------------------------------\n\nlatex_elements = {\n    # The paper size ('letterpaper' or 'a4paper').\n    #\n    # 'papersize': 'letterpaper',\n\n    # The font size ('10pt', '11pt' or '12pt').\n    #\n    # 'pointsize': '10pt',\n\n    # Additional stuff for the LaTeX preamble.\n    #\n    # 'preamble': '',\n\n    # Latex figure (float) alignment\n    #\n    # 'figure_align': 'htbp',\n}\n\n# Grouping the document tree into LaTeX files. List of tuples\n# (source start file, target name, title,\n#  author, documentclass [howto, manual, or own class]).\nlatex_documents = [\n    (master_doc, 'HTTPPrompt.tex', 'HTTP Prompt Documentation',\n     'Chang-Hung Liang', 'manual'),\n]\n\n\n# -- Options for manual page output ---------------------------------------\n\n# One entry per manual page. List of tuples\n# (source start file, name, description, authors, manual section).\nman_pages = [\n    (master_doc, 'httpprompt', 'HTTP Prompt Documentation',\n     [author], 1)\n]\n\n\n# -- Options for Texinfo output -------------------------------------------\n\n# Grouping the document tree into Texinfo files. List of tuples\n# (source start file, target name, title, author,\n#  dir menu entry, description, category)\ntexinfo_documents = [\n    (master_doc, 'HTTPPrompt', 'HTTP Prompt Documentation',\n     author, 'HTTPPrompt', 'One line description of project.',\n     'Miscellaneous'),\n]\n\n\nhtml_sidebars = {\n    '**': [\n        'localtoc.html',\n        'navigation.html'\n    ]\n}\n\n\nhtml_theme_options = {\n    'extra_nav_links': OrderedDict([\n        ('Home', 'http://http-prompt.com'),\n        ('Discord', 'https://httpie.io/chat'),\n        ('Code on GitHub', 'https://github.com/httpie/http-prompt'),\n    ])\n}\n"
  },
  {
    "path": "docs/contributor-guide.rst",
    "content": ".. _contributor-guide:\n\nContributor Guide\n=================\n\nThis document is for developers who want to contribute code to this project.\nAny contributions are welcome and greatly appreciated!\n\nThis project follows the common conventions of a Python/GitHub project. So if\nyou're already an experienced Python/GitHub user, it should be straightforward\nfor you to set up your development environment and send patches. Generally, the\nsteps include:\n\n1. Fork and clone the repo\n2. Create a virtualenv for this project\n3. Install dependent packages with ``pip install -e .``\n4. Install test dependent packages with ``pip install -r requirements-test.txt``\n5. Make your changes to the code\n6. Run tests with ``pytest`` and ``tox``\n7. Commit and push your changes\n8. Send a pull request\n9. Wait to be reviewed and get merged!\n\nIf you're not familiar with any of the above steps, read the following\ninstructions.\n\n\nForking\n-------\n\nFork_ is like copying someone else's project to your account, so you can start\nyour own independent development without interfering with the original one.\n\nTo fork HTTP Prompt, just click the **Fork** button on HTTP Prompt's GitHub\nproject page. Then you clone your fork to your local computer::\n\n    $ cd ~/Projects\n    $ git clone git@github.com:{YOUR_USERNAME}/http-prompt.git\n\nRead `Forking Projects`_ on GitHub to learn more.\n\n\nWorking with virtualenv\n-----------------------\n\n*virtualenv* is the de facto standard tool when developing a Python project.\nInstead of polluting your system-wide Python installation with different Python\nprojects, virtualenv creates an isolated Python environment exclusively for a\nPython project.\n\nThere are several tools you can use for managing virtualenvs. In this guide,\nwe'll show you how to use pyenv_ and pyenv-virtualenv_, which is one of the\nmost popular virtualenv management tools.\n\nMake sure you have installed pyenv_ and pyenv-virtualenv_ first.\n\nHTTP Prompt should work on Python 3.6 and newer. You can use any\nof these Python versions as your development environment, but using the latest\nversion (3.6.x) is probably the best. You can install the latest Python with\npyenv::\n\n    $ pyenv install 3.6.0\n\nThis will install Python 3.6.0 in ``~/.pyenv/versions/3.6.0`` directory. To\ncreate a virtualenv for HTTP Prompt, do::\n\n    $ pyenv virtualenv 3.6.0 http-prompt\n\nThe command means: create a virtualenv named \"http-prompt\" based on Python\n3.6.0. The virtualenv can be found at ``~/.pyenv/versions/3.6.0/envs/http-prompt``.\n\nTo activate the virtualenv, do::\n\n    $ pyenv activate http-prompt\n\nThis will switch your Python environment from the system-wide Python to the\nvirtualenv's (named \"http-prompt\") Python.\n\nTo go back to the system-wide Python, you have to deactivate the virtualenv::\n\n    $ pyenv deactivate\n\nRefer to pyenv_ and pyenv-virtualenv_ if anything else is unclear.\n\n\nInstalling Dependent Packages\n-----------------------------\n\nThe dependent packages should be installed on a virtualenv, so make sure you\nactivate your virtualenv first. If not, do::\n\n    $ pyenv activate http-prompt\n\nIt is also recommended to use the latest version of pip. You can upgrade it\nwith::\n\n    $ pip install -U pip\n\nInstall HTTP Prompt with its dependent packages::\n\n    $ cd ~/Projects/http-prompt\n    $ pip install -e .\n\n``pip install -e .`` means install the ``http-prompt`` package in editable mode\n(or developer mode). This allows you to edit code directly in\n``~/Projects/http-prompt`` without reinstalling the package. Without the ``-e``\noption, the package will be installed to Python's ``site-packages`` directory,\nwhich is not convenient for developing.\n\n\nInstalling Test Dependent Packages\n----------------------------------\n\nTest requirements are placed in a separate file named ``requirements-test.txt``.\nTo install them, do::\n\n    $ cd ~/Projects/http-prompt\n    $ pip install -r requirements-test.txt\n\n\nMaking Your Changes\n-------------------\n\nCode Style\n~~~~~~~~~~\n\nAlways lint your code with Flake8_. You can set it up in your code editor or\nsimply use ``flake8`` in the command line.\n\n`The Hitchhiker’s Guide to Python`_ provides the best Python coding practices.\nWe recommend anyone who wants to write good Python code to read it.\n\nAdding Features\n~~~~~~~~~~~~~~~\n\nBefore you add a new feature, make sure you create an issue making a proposal\nfirst, because you don't want to waste your time on something that the\ncommunity don't agree upon.\n\nPython Compatibility\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nHTTP Prompt is compatible with Python 3.6+.\n\nDocumentation\n~~~~~~~~~~~~~\n\nDocumentation is written in Sphinx_. To build documentation, you need to\ninstall Sphinx_ first::\n\n    $ pip install sphinx\n\nTo build and view documentation in HTML, do::\n\n    $ cd ~/Projects/http-prompt/docs\n    $ make html\n    $ open _build/html/index.html\n\n\nRunning Tests\n-------------\n\nSingle Python Version\n~~~~~~~~~~~~~~~~~~~~~\n\nMake sure your virtualenv is activated. To run tests, do::\n\n    $ cd ~/Projects/http-prompt\n    $ pytest\n\n``pytest`` runs the tests with your virtualenv's Python version. This is good\nfor fast testing. To test the code against multiple Python versions, you use\nTox_.\n\nMultiple Python Versions\n~~~~~~~~~~~~~~~~~~~~~~~~\n\nAll the commands in this section should **NOT** be run in a virtualenv.\nDeactivate it first if you're in a virtualenv::\n\n    $ pyenv deactivate\n\nMake sure you have installed all the Python versions we're targeting. If not,\ndo::\n\n    $ pyenv install 3.6.0\n    $ pyenv install 3.7.0\n    $ pyenv install 3.8.0\n\nTo use Tox_ with pyenv_, you have to instruct pyenv to use multiple Python\nversions for the project::\n\n    $ cd ~/Projects/http-prompt\n    $ pyenv local 3.6.0 3.7.0 3.8.0\n\nThis will generate a ``.python-version`` in the project directory::\n\n    $ cat ~/Projects/http-prompt/.python-version\n    3.6.0\n    3.7.0\n    3.8.0\n\nThis tells pyenv_ to choose a Python version based on the above order. In this\ncase, 3.6.0 is the first choice, so any Python executables (such as ``python``\nand ``pip``) will be automatically mapped to the ones in\n``~/.pyenv/versions/3.8.0/bin``.\n\nWe want to run ``tox`` using on Python 3.8.0. Make sure you have installed\nTox_::\n\n    $ pip install tox\n\nTo run tests, execute ``tox``::\n\n    $ cd ~/Projects/http-prompt\n    $ tox\n\nTox_ will install the test Python environments in the ``.tox/`` directory in\nthe project directory, and run the test code against all the Python versions\nlisted above.\n\n\nCode Review\n-----------\n\nOnce you made changes and all the tests pass, push your modified code to your\nGitHub account. Submit a pull request (PR) on GitHub for the maintainers to\nreview. If the patch is good, The maintainers will merge it to the master\nbranch and ship the new code in the next release. If the patch needs\nimprovements, we'll give you feedback so you can modify accordingly and\nresubmit it to the PR.\n\n\n.. _Flake8: http://flake8.pycqa.org/en/latest/index.html\n.. _Fork: https://en.wikipedia.org/wiki/Fork_(software_development)\n.. _Forking Projects: https://guides.github.com/activities/forking/\n.. _pyenv-virtualenv: https://github.com/yyuu/pyenv-virtualenv\n.. _pyenv: https://github.com/yyuu/pyenv\n.. _Sphinx: http://www.sphinx-doc.org/\n.. _The Hitchhiker’s Guide to Python: http://docs.python-guide.org/en/latest/\n.. _Tox: https://tox.readthedocs.io/en/latest/\n"
  },
  {
    "path": "docs/index.rst",
    "content": "HTTP Prompt Documentation\n=========================\n\nHTTP Prompt is an interactive command-line HTTP client featuring autocomplete\nand syntax highlighting, built on HTTPie_ and prompt_toolkit_.\n\nSee it in action:\n\n|Asciinema|\n\n\nContents\n--------\n\n.. toctree::\n   :maxdepth: 3\n\n   user-guide\n   contributor-guide\n\n\nRoadmap\n-------\n\n* Support for advanced HTTPie syntax, e.g, ``field=@file.json``\n* Support for cURL command and raw format preview\n* Improve autocomplete\n* Python syntax evaluation\n* HTTP/2 support\n\n\nUser Support\n------------\n\nWe'd love to hear more from our users! Please use the following channels for\nbug reports, feature requests, and questions:\n\n* `GitHub issues`_\n* `Gitter chat room`_\n\n\nContributing\n------------\n\nAre you a developer and interested in contributing to HTTP Prompt? See\n:ref:`Contributor Guide <contributor-guide>`.\n\n\nThanks\n------\n\n* HTTPie_: for designing such a user-friendly HTTP CLI\n* prompt_toolkit_: for simplifying the work of building an interactive CLI\n* Parsimonious_: for the PEG parser used by this project\n* pgcli_: for the inspiration of this project\n* Contributors_: for improving this project\n\n\n.. |Asciinema| image:: https://asciinema.org/a/96613.png\n    :target: https://asciinema.org/a/96613?theme=monokai&size=medium&autoplay=1&speed=1.5\n\n.. _Contributors: https://github.com/eliangcs/http-prompt/graphs/contributors\n.. _GitHub issues: https://github.com/httpie/http-prompt/issues\n.. _Discord: https://htpie.io/chat\n.. _HTTPie: https://httpie.io\n.. _Parsimonious: https://github.com/erikrose/parsimonious\n.. _pgcli: http://pgcli.com\n.. _prompt_toolkit: https://github.com/jonathanslenders/python-prompt-toolkit\n"
  },
  {
    "path": "docs/make.bat",
    "content": "@ECHO OFF\r\n\r\npushd %~dp0\r\n\r\nREM Command file for Sphinx documentation\r\n\r\nif \"%SPHINXBUILD%\" == \"\" (\r\n\tset SPHINXBUILD=sphinx-build\r\n)\r\nset SOURCEDIR=.\r\nset BUILDDIR=_build\r\nset SPHINXPROJ=HTTPPrompt\r\n\r\nif \"%1\" == \"\" goto help\r\n\r\n%SPHINXBUILD% >NUL 2>NUL\r\nif errorlevel 9009 (\r\n\techo.\r\n\techo.The 'sphinx-build' command was not found. Make sure you have Sphinx\r\n\techo.installed, then set the SPHINXBUILD environment variable to point\r\n\techo.to the full path of the 'sphinx-build' executable. Alternatively you\r\n\techo.may add the Sphinx directory to PATH.\r\n\techo.\r\n\techo.If you don't have Sphinx installed, grab it from\r\n\techo.http://sphinx-doc.org/\r\n\texit /b 1\r\n)\r\n\r\n%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%\r\ngoto end\r\n\r\n:help\r\n%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%\r\n\r\n:end\r\npopd\r\n"
  },
  {
    "path": "docs/user-guide.rst",
    "content": ".. _user-guide:\n\nUser Guide\n==========\n\nInstallation\n------------\n\nJust install it like a regular Python package::\n\n    $ pip install http-prompt\n\nYou'll probably see some permission errors if you're trying to install it on\nthe system-wide Python. It isn't recommended. But if that's what you want to\ndo, you need to ``sudo``::\n\n    $ sudo pip install http-prompt\n\nAnother alternative is to use ``--user`` option to install the package into\nyour user directory::\n\n    $ pip install --user http-prompt\n\nTo upgrade HTTP Prompt, do::\n\n    $ pip install -U http-prompt\n\nIt's also possible to install it using Homebrew::\n\n    $ brew install http-prompt\n\n\nQuickstart\n----------\n\nTo start a session, you use the ``http-prompt`` executable:\n\n.. code-block:: bash\n\n    # Start with the last session or http://localhost:8000\n    $ http-prompt\n\n    # Start with the given URL\n    $ http-prompt http://httpbin.org\n\n    # Start with some initial options\n    $ http-prompt localhost:8000/api --auth user:pass username=somebody\n\nOnce you're in a session, you can use the following commands.\n\nTo change URL address, use ``cd``:\n\n.. code-block:: bash\n\n    # Relative URL path\n    > cd api/v1\n\n    # Absolute URL\n    > cd http://localhost/api\n\nTo add headers, querystring, or body parameters, use the syntax as in HTTPie_.\nThe following are all valid:\n\n.. code-block:: bash\n\n    # Header\n    > Content-Type:application/json\n\n    # Querystring parameter\n    > page==2\n\n    # Body parameters\n    > username=foo\n    > full_name='foo bar'\n\n    # Body parameters in raw JSON (new in v0.9.0)\n    > number:=1234\n    > is_ok:=true\n    > names:=[\"foo\",\"bar\"]\n    > user:='{\"username\": \"foo\", \"password\": \"bar\"}'\n\n    # Write them in one line\n    > Content-Type:application/json page==2 username=foo\n\nYou can also add HTTPie_ options like this:\n\n.. code-block:: bash\n\n    > --form --auth user:pass\n    > --verify=no\n\n    # HTTPie options and request parameters in one line\n    > --form --auth user:pass username=foo Content-Type:application/json\n\nTo preview how HTTP Prompt is going to call HTTPie_, do:\n\n.. code-block:: bash\n\n    > httpie post\n    http --auth user:pass --form POST http://localhost/api apikey==abc username=john\n\nYou can temporarily override the request parameters by supplying options and\nparameters in ``httpie`` command. The overrides won't affect the later\nrequests.\n\n.. code-block:: bash\n\n    # No parameters initially\n    > httpie\n    http http://localhost\n\n    # Override parameters temporarily\n    > httpie /api/something page==2 --json\n    http --json http://localhost/api/something page==2\n\n    # Current state is not affected by the above overrides\n    > httpie\n    http http://localhost\n\nSince v0.6.0, apart from ``httpie`` command, you can also use ``env`` to print\nthe current session:\n\n.. code-block:: bash\n\n    > env\n    --verify=no\n    cd http://localhost\n    page==10\n    limit==20\n\nTo actually send an HTTP request, enter one of the HTTP methods:\n\n.. code-block:: bash\n\n    > get\n    > post\n    > put\n    > patch\n    > delete\n    > head\n    > options (new in v0.8.0)\n\nThe above HTTP methods also support temporary overriding:\n\n.. code-block:: bash\n\n    # No parameters initially\n    > httpie\n    http http://localhost\n\n    # Send a request with some overrided parameters\n    > post /api/v1 --form name=jane\n\n    # Current state remains intact\n    > httpie\n    http http://localhost\n\nTo remove an existing header, a querystring parameter, a body parameter, or an\nHTTPie_ option:\n\n.. code-block:: bash\n\n    # Remove a header\n    > rm -h Content-Type\n\n    # Remove a querystring parameter\n    > rm -q apikey\n\n    # Remove a body parameter\n    > rm -b username\n\n    # Remove an HTTPie option\n    > rm -o --auth\n\nTo reset the session, i.e., clear all parameters and options:\n\n.. code-block:: bash\n\n    > rm *\n\nTo exit a session, simply enter:\n\n.. code-block:: bash\n\n    > exit\n\n\nOutput Redirection\n------------------\n\n*New in v0.6.0.*\n\nYou can redirect the output of a command to a file by using the syntax:\n\n.. code-block:: bash\n\n    # Write output to a file\n    > COMMAND > /path/to/file\n\n    # Append output to a file\n    > COMMAND >> /path/to/file\n\nwhere ``COMMAND`` can be one of the following:\n\n* ``env``\n* ``httpie``\n* HTTP actions: ``get``, ``post``, ``put``, ``patch``, ``delete``, ``head``,\n  ``options``\n\n\nSaving and Loading Sessions\n~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nOne of the use cases of output redirection is to save and load sessions, which\nis especially useful for team collaboration, where you want to share your\nsessions with your team members.\n\nTo save your current session, you redirect the output of ``env`` to a file:\n\n.. code-block:: bash\n\n    > env > /path/to/file\n\nTo load a saved session, you can use ``source`` or ``exec``. Their only\ndifference is that ``exec`` wipes out the current session before loading.\nUsage:\n\n.. code-block:: bash\n\n    # Update the current session\n    > source /path/to/file\n\n    # Wipe out the current session and load from a file\n    > exec /path/to/file\n\n*New in v0.11.0.*\n\nLoad a saved session from the command line directly with the ``--env`` option.\nThis allows you for example to define aliases and easily start HTTP Prompt with\na full configuration already loaded for each of your projects.\n\n.. code-block:: bash\n\n    # Define alias for project1\n    $ alias http_project1='http-prompt --env /path/to/project1/env/file'\n\n    # Launch HTTP Prompt for project1\n    $ http_project1\n\nAny extra argument in the command line is still used and overwrites the value\nfrom the session file if already present\n\n.. code-block:: bash\n\n    # Use saved session but overwrite the URL and add a parameter\n    $ http-prompt --env /path/to/file localhost:8080 page==2\n\n\nSaving HTTP Responses\n~~~~~~~~~~~~~~~~~~~~~\n\nPrinting HTTP responses to the console is good for small text responses. For\nlarger text or binary data, you may want to save the response to a file. Usage:\n\n.. code-block:: bash\n\n    # Save http://httpbin.org/image/png to a file\n    > cd http://httpbin.org/image/png\n    > get > pig.png\n\n    # Or use this one-liner\n    > get http://httpbin.org/image/png > pig.png\n\n\nPipeline\n--------\n\n*New in v0.7.0.*\n\nHTTP Prompt supports simplified pipeline syntax, where you can pipe the output\nto a shell command:\n\n.. code-block:: bash\n\n    # Replace 'localhost' to '127.0.0.1'\n    > httpie POST http://localhost | sed 's/localhost/127.0.0.1/'\n    http http://127.0.0.1\n\n    # Only print the line that contains 'User-Agent' using grep\n    > get http://httpbin.org/get | grep 'User-Agent'\n        \"User-Agent\": \"HTTPie/0.9.6\"\n\nOn macOS, you can even copy the result to the clipboard using ``pbcopy``:\n\n.. code-block:: bash\n\n    # Copy the HTTPie command to the clipboard (macOS only)\n    > httpie | pbcopy\n\nAnother cool trick is to use jq_ to parse JSON data:\n\n.. code-block:: bash\n\n    > get http://httpbin.org/get | jq '.headers.\"User-Agent\"'\n    \"HTTPie/0.9.6\"\n\n**Note**: Syntax with multiple pipes is not supported currently.\n\n\nShell Substitution\n------------------\n\n*New in v0.7.0.*\n\nShell substitution happens when you put a shell command between two backticks\nlike ```...```. This syntax allows you compute a value from the shell\nenvironment and assign the value to a parameter::\n\n    # Set date to current time\n    > date==`date -u +\"%Y-%m-%d %H:%M:%S\"`\n    > httpie\n    http http://localhost:8000 'date==2016-10-08 09:45:00'\n\n    # Get password from a file. Suppose the file has a content of\n    # \"secret_api_key\".\n    > password==`cat ./apikey.txt`\n    > httpie\n    http http://localhost:8000 password==secret_api_key\n\n\nConfiguration\n-------------\n\n*New in v0.4.0.*\n\nWhen launched for the first time, HTTP Prompt creates a user config file at\n``$XDG_CONFIG_HOME/http-prompt/config.py`` (or ``%LOCALAPPDATA%/http-prompt/config.py``\non Windows). By default, it's ``~/.config/http-prompt/config.py`` (or\n``~/AppData/Local/http-prompt/config.py``).\n\n``config.py`` is a Python module with all the available options you can\ncustomize. Don't worry. You don't need to know Python to edit it. Just open it\nup with a text editor and follow the guidance inside.\n\n\nPersistent Context\n------------------\n\n*New in v0.4.0.*\n\nHTTP Prompt keeps a data structure called *context* to represent your current\nsession. Every time you enter a command modifying your context, HTTP Prompt\nsaves the context to your filesystem, enabling you to resume your previous\nsession when you restart ``http-prompt``.\n\nThe last saved context is located at ``$XDG_DATA_HOME/http-prompt/context.hp``\n(or ``%LOCALAPPDATA%/http-prompt/context.hp`` on Windows). By default, it's\n``~/.local/share/http-prompt/context.hp`` (or ``~/AppData/Local/http-prompt/context.hp``).\n\nAs context data may contain sensitive data like API keys, you should keep the\nuser data directory private. By default, HTTP Prompt sets the modes of\n``$XDG_DATA_HOME/http-prompt`` to ``rwx------`` (i.e., ``700``) so that the\nonly person who can read it is the owner (you).\n\n**Note for users of older versions**: Since 0.6.0, HTTP Prompt only stores the\nlast context instead of grouping multiple contexts by hostnames and ports like\nit did previously. We changed the behavior because the feature can be simply\nreplaced by ``env``, ``exec`` and ``source`` commands. See the discussion in\n`issue #70 <https://github.com/httpie/http-prompt/issues/70>`_ for detail.\n\n\n``ls``, ``cd``, and OpenAPI/Swagger Specification\n-------------------------------------------------\n\n*New in v0.10.0.*\n\nOpenAPI_ (formerly known as Swagger_) is a specification that describes an\nHTTP/REST API. The ``http-prompt`` has a ``--spec`` option for you to provide\nan OpenAPI specification in JSON format. The specification enables HTTP Prompt\nto do some cool things like autocomplete API endpoint paths and parameters\nfor you.\n\nSee it in action:\n\n|ls-demo|\n\nTo use this feature, specify an OpenAPI/Swagger specification file with\n``--spec`` command line option::\n\n    # Specify a spec on local filesystem\n    $ http-prompt http://localhost:8000 --spec /path/to/spec.json\n\n    # Specify a spec on the internet (https://apis.guru has lots of them)\n    $ http-prompt https://api.github.com --spec https://api.apis.guru/v2/specs/github.com/v3/swagger.json\n\nThen you can use ``ls`` and ``cd`` commands to navigate API endpoints with\nautocomplete!\n\n\n.. |ls-demo| image:: https://asciinema.org/a/107732.png\n    :target: https://asciinema.org/a/107732\n\n.. _HTTPie: https://httpie.org\n.. _jq: https://stedolan.github.io/jq/\n.. _OpenAPI: https://openapis.org\n.. _Swagger: http://swagger.io/\n"
  },
  {
    "path": "http_prompt/__init__.py",
    "content": "__version__ = '2.1.0'\n"
  },
  {
    "path": "http_prompt/cli.py",
    "content": "import json\nfrom http.cookies import SimpleCookie\nfrom urllib.request import pathname2url, urlopen\n\nimport yaml\nimport os\nimport re\nimport sys\n\nimport click\n\nfrom httpie.plugins import FormatterPlugin  # noqa, avoid cyclic import\nfrom httpie.output.formatters.colors import Solarized256Style\nfrom prompt_toolkit import prompt\nfrom prompt_toolkit.auto_suggest import AutoSuggestFromHistory\nfrom prompt_toolkit.history import FileHistory\nfrom prompt_toolkit.lexers import PygmentsLexer\nfrom prompt_toolkit.styles.pygments import style_from_pygments_cls\nfrom pygments.styles import get_style_by_name\nfrom pygments.util import ClassNotFound\n\nfrom . import __version__\nfrom . import config\nfrom .completer import HttpPromptCompleter\nfrom .context import Context\nfrom .contextio import load_context, save_context\nfrom .execution import execute\nfrom .lexer import HttpPromptLexer\nfrom .utils import smart_quote\nfrom .xdg import get_data_dir\n\n\ndef fix_incomplete_url(url):\n    if url.startswith(('s://', '://')):\n        url = 'http' + url\n    elif url.startswith('//'):\n        url = 'http:' + url\n    elif not url.startswith(('http://', 'https://')):\n        url = 'http://' + url\n    return url\n\n\ndef update_cookies(base_value, cookies):\n    cookie = SimpleCookie(base_value)\n    for k, v in cookies.items():\n        cookie[k] = v\n    return str(cookie.output(header='', sep=';').lstrip())\n\n\nclass ExecutionListener(object):\n\n    def __init__(self, cfg):\n        self.cfg = cfg\n\n    def context_changed(self, context):\n        # Dump the current context to HTTP Prompt format\n        save_context(context)\n\n    def response_returned(self, context, response):\n        if not response.cookies:\n            return\n\n        cookie_pref = self.cfg.get('set_cookies') or 'auto'\n        if cookie_pref == 'auto' or (\n                cookie_pref == 'ask' and\n                click.confirm('Cookies incoming! Do you want to set them?')):\n            existing_cookie = context.headers.get('Cookie')\n            new_cookie = update_cookies(existing_cookie, response.cookies)\n            context.headers['Cookie'] = new_cookie\n            click.secho('Cookies set: %s' % new_cookie)\n\n\ndef normalize_url(ctx, param, value):\n    if value:\n        if not re.search(r'^\\w+://', value):\n            value = 'file:' + pathname2url(os.path.abspath(value))\n        return value\n    return None\n\n\n@click.command(context_settings={'ignore_unknown_options': True})\n@click.option('--spec', help='OpenAPI/Swagger specification file.',\n              callback=normalize_url)\n@click.option('--env', help='Environment file to preload.',\n              type=click.Path(exists=True))\n@click.argument('url', default='')\n@click.argument('http_options', nargs=-1, type=click.UNPROCESSED)\n@click.version_option(message='%(version)s')\ndef cli(spec, env, url, http_options):\n    click.echo('Version: %s' % __version__)\n\n    copied, config_path = config.initialize()\n    if copied:\n        click.echo('Config file not found. Initialized a new one: %s' %\n                   config_path)\n\n    cfg = config.load()\n\n    # Override pager/less options\n    os.environ['PAGER'] = cfg['pager']\n    os.environ['LESS'] = '-RXF'\n\n    if spec:\n        f = urlopen(spec)\n        try:\n            content = f.read().decode()\n            try:\n                spec = json.loads(content)\n            except json.JSONDecodeError:\n                try:\n                    spec = yaml.safe_load(content)\n                except yaml.YAMLError:\n                    click.secho(\"Warning: Specification file '%s' is neither valid JSON nor YAML\" %\n                                spec, err=True, fg='red')\n                    spec = None\n        finally:\n            f.close()\n\n    if url:\n        url = fix_incomplete_url(url)\n    context = Context(url, spec=spec)\n\n    output_style = cfg.get('output_style')\n    if output_style:\n        context.options['--style'] = output_style\n\n    # For prompt-toolkit\n    history = FileHistory(os.path.join(get_data_dir(), 'history'))\n    lexer = PygmentsLexer(HttpPromptLexer)\n    completer = HttpPromptCompleter(context)\n    try:\n        style_class = get_style_by_name(cfg['command_style'])\n    except ClassNotFound:\n        style_class = Solarized256Style\n    style = style_from_pygments_cls(style_class)\n\n    listener = ExecutionListener(cfg)\n\n    if len(sys.argv) == 1:\n        # load previous context if nothing defined\n        load_context(context)\n    else:\n        if env:\n            load_context(context, env)\n            if url:\n                # Overwrite the env url if not default\n                context.url = url\n\n        if http_options:\n            # Execute HTTPie options from CLI (can overwrite env file values)\n            http_options = [smart_quote(a) for a in http_options]\n            execute(' '.join(http_options), context, listener=listener)\n\n    while True:\n        try:\n            text = prompt('%s> ' % context.url, completer=completer,\n                          lexer=lexer, style=style, history=history,\n                          auto_suggest=AutoSuggestFromHistory(),\n                          vi_mode=cfg['vi'])\n        except KeyboardInterrupt:\n            continue  # Control-C pressed\n        except EOFError:\n            break  # Control-D pressed\n        else:\n            execute(text, context, listener=listener, style=style_class)\n            if context.should_exit:\n                break\n\n    click.echo('Goodbye!')\n"
  },
  {
    "path": "http_prompt/completer.py",
    "content": "# -*- coding: utf-8 -*-\nimport re\n\nfrom collections import OrderedDict\n\nfrom itertools import chain\nfrom urllib.parse import urlparse\n\nfrom prompt_toolkit.completion import Completer, Completion\n\nfrom .completion import (ROOT_COMMANDS, ACTIONS, OPTION_NAMES, HEADER_NAMES,\n                         HEADER_VALUES)\n\n\nRULES = [\n    # (regex pattern, a method name in CompletionGenerator)\n    (r'((?:[^\\s\\'\"\\\\=:]|(?:\\\\.))+):((?:[^\\s\\'\"\\\\]|(?:\\\\.))*)$',\n     'header_values'),\n\n    (r'(get|head|post|put|patch|delete|connect)\\s+', 'concat_mutations'),\n    (r'(httpie|curl)\\s+', 'preview'),\n    (r'rm\\s+\\-b\\s+', 'existing_body_params'),\n    (r'rm\\s+\\-h\\s+', 'existing_header_names'),\n    (r'rm\\s+\\-o\\s+', 'existing_option_names'),\n    (r'rm\\s+\\-q\\s+', 'existing_querystring_params'),\n\n    # The last two captures are full URL path and the last part of the URL\n    # path. For example:\n    # '/foo/bar' => ('/foo/bar', 'bar')\n    # '/foo/bar/' => ('/foo/bar/', '')\n    # 'foo/bar' => ('foo/bar', 'bar')\n    (r'(ls|cd)\\s+(/?(?:[^/]+/)*([^/]*)/?)$', 'urlpaths'),\n    (r'^\\s*[^\\s]*$', 'root_commands')\n]\n\n\ndef compile_rules(rules):\n    compiled_rules = []\n    for pattern, meta_dict in rules:\n        regex = re.compile(pattern)\n        compiled_rules.append((regex, meta_dict))\n    return compiled_rules\n\n\nRULES = compile_rules(RULES)\n\n\ndef fuzzyfinder(text, collection):\n    \"\"\"https://github.com/amjith/fuzzyfinder\"\"\"\n    suggestions = []\n    if not isinstance(text, str):\n        text = str(text)\n    pat = '.*?'.join(map(re.escape, text))\n    regex = re.compile(pat, flags=re.IGNORECASE)\n    for item in collection:\n        r = regex.search(item)\n        if r:\n            suggestions.append((len(r.group()), r.start(), item))\n\n    return (z for _, _, z in sorted(suggestions))\n\n\ndef match_completions(cur_word, word_dict):\n    words = word_dict.keys()\n    suggestions = fuzzyfinder(cur_word, words)\n    for word in suggestions:\n        desc = word_dict.get(word, '')\n        yield Completion(word, -len(cur_word), display_meta=desc)\n\n\nclass CompletionGenerator(object):\n\n    def root_commands(self, context, match):\n        return chain(\n            self._generic_generate(ROOT_COMMANDS.keys(), {}, ROOT_COMMANDS),\n            self.actions(context, match),\n            self.concat_mutations(context, match)\n        )\n\n    def header_values(self, context, match):\n        header_name = match.group(1)\n        header_values = HEADER_VALUES.get(header_name)\n        if header_values:\n            for value in header_values:\n                yield value, header_name\n\n    def preview(self, context, match):\n        return chain(\n            self.actions(context, match),\n            self.concat_mutations(context, match)\n        )\n\n    def actions(self, context, match):\n        return self._generic_generate(ACTIONS.keys(), {}, ACTIONS)\n\n    def concat_mutations(self, context, match):\n        return chain(\n            self._generic_generate(context.body_params.keys(),\n                                   context.body_params, 'Body parameter'),\n            self._generic_generate(context.querystring_params.keys(),\n                                   context.querystring_params,\n                                   'Querystring parameter'),\n            self._generic_generate(HEADER_NAMES.keys(),\n                                   context.headers, HEADER_NAMES),\n            self._generic_generate(OPTION_NAMES.keys(),\n                                   context.options, OPTION_NAMES)\n        )\n\n    def existing_body_params(self, context, match):\n        params = context.body_params.copy()\n        params.update(context.body_json_params)\n        return self._generic_generate(params.keys(), params, 'Body parameter')\n\n    def existing_querystring_params(self, context, match):\n        return self._generic_generate(\n            context.querystring_params.keys(),\n            context.querystring_params, 'Querystring parameter')\n\n    def existing_header_names(self, context, match):\n        return self._generic_generate(context.headers.keys(),\n                                      context.headers, HEADER_NAMES)\n\n    def existing_option_names(self, context, match):\n        return self._generic_generate(context.options.keys(),\n                                      context.options, OPTION_NAMES)\n\n    def urlpaths(self, context, match):\n        path = urlparse(context.url).path.split('/')\n        overrided_path = match.group(2)\n        if overrided_path:\n            if overrided_path.startswith('/'):\n                # Absolute path\n                path = []\n            path += overrided_path.split('/')[:-1]\n        names = [\n            node.name for node in context.root.ls(*path)\n            if node.data.get('type') == 'dir'\n        ]\n        return self._generic_generate(names, {}, 'Endpoint')\n\n    def _generic_generate(self, names, values, descs):\n        for name in sorted(names):\n            if isinstance(descs, str):\n                desc = descs\n            else:\n                desc = descs.get(name, '')\n            if name in values:\n                value = values[name]\n                if value is None:\n                    desc += ' (on)'\n                else:\n                    value = str(value)\n                    if len(value) > 16:\n                        value = value[:13] + '...'\n                    desc += ' (=%s)' % value\n            yield name, desc\n\n\nclass HttpPromptCompleter(Completer):\n\n    def __init__(self, context):\n        self.context = context\n        self.comp_gen = CompletionGenerator()\n\n    def get_completions(self, document, complete_event):\n        cur_text = document.text_before_cursor\n        cur_word = None\n        word_dict = None\n\n        for regex, method_name in RULES:\n            match = regex.search(cur_text)\n            if match:\n                gen_completions = getattr(self.comp_gen, method_name)\n                completions = gen_completions(self.context, match)\n                word_dict = OrderedDict(completions)\n\n                groups = match.groups()\n                if len(groups) > 1:\n                    cur_word = groups[-1]\n                else:\n                    cur_word = document.get_word_before_cursor(WORD=True)\n\n                break\n\n        if word_dict:\n            for comp in match_completions(cur_word, word_dict):\n                yield comp\n"
  },
  {
    "path": "http_prompt/completion.py",
    "content": "\"\"\"Meta data for autocomplete.\"\"\"\n\nfrom collections import OrderedDict\n\nfrom . import options as opt\n\n\nROOT_COMMANDS = OrderedDict([\n    ('cd', 'Change URL/path'),\n    ('clear', 'Clear console screen'),\n    ('curl', 'Preview curl command'),\n    ('env', 'Print environment'),\n    ('exec', 'Clear and load environment from a file'),\n    ('exit', 'Exit HTTP Prompt'),\n    ('help', 'List commands, actions, and HTTPie options'),\n    ('httpie', 'Preview HTTPie command'),\n    ('rm *', 'Remove all options and parameters'),\n    ('rm -b', 'Remove body parameter'),\n    ('rm -b *', 'Remove all body parameters'),\n    ('rm -h', 'Remove header'),\n    ('rm -h *', 'Remove all headers'),\n    ('rm -o', 'Remove HTTPie option'),\n    ('rm -o *', 'Remove all HTTPie options'),\n    ('rm -q', 'Remove querystring parameter'),\n    ('rm -q *', 'Remove all querystring parameters'),\n    ('source', 'Load environment from a file'),\n])\n\nACTIONS = OrderedDict([\n    ('connect', 'CONNECT request'),\n    ('delete', 'DELETE request'),\n    ('get', 'GET request'),\n    ('head', 'HEAD request'),\n    ('options', 'OPTIONS request'),\n    ('patch', 'GET request'),\n    ('post', 'POST request'),\n    ('put', 'PUT request'),\n])\n\n# http://www.iana.org/assignments/message-headers/message-headers.xhtml\n# https://en.wikipedia.org/wiki/List_of_HTTP_header_fields\nHEADER_NAMES = OrderedDict([\n    ('Accept', 'Acceptable response media type'),\n    ('Accept-Charset', 'Acceptable response charsets'),\n    ('Accept-Encoding', 'Acceptable response content codings'),\n    ('Accept-Language', 'Preferred natural languages in response'),\n    ('ALPN', 'Application-layer protocol negotiation to use'),\n    ('Alt-Used', 'Alternative host in use'),\n    ('Authorization', 'Authentication information'),\n    ('Cache-Control', 'Directives for caches'),\n    ('Connection', 'Connection options'),\n    ('Content-Encoding', 'Content codings'),\n    ('Content-Language', 'Natural languages for content'),\n    ('Content-Length', 'Anticipated size for payload body'),\n    ('Content-Location', 'Where content was obtained'),\n    ('Content-MD5', 'Base64-encoded MD5 sum of content'),\n    ('Content-Type', 'Content media type'),\n    ('Cookie', 'Stored cookies'),\n    ('Date', 'Datetime when message was originated'),\n    ('Depth', 'Applied only to resource or its members'),\n    ('DNT', 'Do not track user'),\n    ('Expect', 'Expected behaviors supported by server'),\n    ('Forwarded', 'Proxies involved'),\n    ('From', 'Sender email address'),\n    ('Host', 'Target URI'),\n    ('HTTP2-Settings', 'HTTP/2 connection parameters'),\n    ('If', 'Request condition on state tokens and ETags'),\n    ('If-Match', 'Request condition on target resource'),\n    ('If-Modified-Since', 'Request condition on modification date'),\n    ('If-None-Match', 'Request condition on target resource'),\n    ('If-Range', 'Request condition on Range'),\n    ('If-Schedule-Tag-Match', 'Request condition on Schedule-Tag'),\n    ('If-Unmodified-Since', 'Request condition on modification date'),\n    ('Max-Forwards', 'Max number of times forwarded by proxies'),\n    ('MIME-Version', 'Version of MIME protocol'),\n    ('Origin', 'Origin(s) issuing the request'),\n    ('Pragma', 'Implementation-specific directives'),\n    ('Prefer', 'Preferred server behaviors'),\n    ('Proxy-Authorization', 'Proxy authorization credentials'),\n    ('Proxy-Connection', 'Proxy connection options'),\n    ('Range', 'Request transfer of only part of data'),\n    ('Referer', 'Previous web page'),\n    ('TE', 'Transfer codings willing to accept'),\n    ('Transfer-Encoding', 'Transfer codings applied to payload body'),\n    ('Upgrade', 'Invite server to upgrade to another protocol'),\n    ('User-Agent', 'User agent string'),\n    ('Via', 'Intermediate proxies'),\n    ('Warning', 'Possible incorrectness with payload body'),\n    ('WWW-Authenticate', 'Authentication scheme'),\n    ('X-Csrf-Token', 'Prevent cross-site request forgery'),\n    ('X-CSRFToken', 'Prevent cross-site request forgery'),\n    ('X-Forwarded-For', 'Originating client IP address'),\n    ('X-Forwarded-Host', 'Original host requested by client'),\n    ('X-Forwarded-Proto', 'Originating protocol'),\n    ('X-Http-Method-Override', 'Request method override'),\n    ('X-Requested-With', 'Used to identify Ajax requests'),\n    ('X-XSRF-TOKEN', 'Prevent cross-site request forgery'),\n])\n\nCONTENT_TYPES = [\n    'application/json',\n    'application/x-www-form-urlencoded',\n    'multipart/form-data',\n    'text/html',\n]\n\n# TODO: Include more common header values\nHEADER_VALUES = {\n    'Accept': CONTENT_TYPES,\n    'Content-Type': CONTENT_TYPES,\n}\n\nOPTION_NAMES = sorted(opt.FLAG_OPTIONS + opt.VALUE_OPTIONS)\nOPTION_NAMES = OrderedDict(OPTION_NAMES)\n"
  },
  {
    "path": "http_prompt/config.py",
    "content": "\"\"\"Functions that deal with the user configuration.\"\"\"\n\nimport os\nimport shutil\n\nfrom . import defaultconfig\nfrom . import xdg\n\n\ndef get_user_config_path():\n    \"\"\"Get the path to the user config file.\"\"\"\n    return os.path.join(xdg.get_config_dir(), 'config.py')\n\n\ndef initialize():\n    \"\"\"Initialize a default config file if it doesn't exist yet.\n\n    Returns:\n        tuple: A tuple of (copied, dst_path). `copied` is a bool indicating if\n            this function created the default config file. `dst_path` is the\n            path of the user config file.\n    \"\"\"\n    dst_path = get_user_config_path()\n    copied = False\n    if not os.path.exists(dst_path):\n        src_path = os.path.join(os.path.dirname(__file__), 'defaultconfig.py')\n        shutil.copyfile(src_path, dst_path)\n        copied = True\n    return copied, dst_path\n\n\ndef _module_to_dict(module):\n    attrs = {}\n    attr_names = filter(lambda n: not n.startswith('_'), dir(module))\n    for name in attr_names:\n        value = getattr(module, name)\n        attrs[name] = value\n    return attrs\n\n\ndef load_default():\n    \"\"\"Return default config as a dict.\"\"\"\n    return _module_to_dict(defaultconfig)\n\n\ndef load_user():\n    \"\"\"Read user config file and return it as a dict.\"\"\"\n    config_path = get_user_config_path()\n    config = {}\n\n    # TODO: This may be overkill and too slow just for reading a config file\n    with open(config_path) as f:\n        code = compile(f.read(), config_path, 'exec')\n    exec(code, config)\n\n    keys = list(config.keys())\n    for k in keys:\n        if k.startswith('_'):\n            del config[k]\n\n    return config\n\n\ndef load():\n    \"\"\"Read default and user config files and return them as a dict.\"\"\"\n    config = load_default()\n    config.update(load_user())\n    return config\n"
  },
  {
    "path": "http_prompt/context/__init__.py",
    "content": "from http_prompt.tree import Node\n\n\nclass Context(object):\n\n    def __init__(self, url=None, spec=None):\n        self.url = url\n        self.headers = {}\n        self.querystring_params = {}\n        self.body_params = {}\n        self.body_json_params = {}\n        self.options = {}\n        self.should_exit = False\n\n        # Create a tree for supporting API spec and ls command\n        self.root = Node('root')\n        if spec:\n            if not self.url:\n                schemes = spec.get('schemes')\n                scheme = schemes[0] if schemes else 'https'\n                self.url = (scheme + '://' +\n                            spec.get('host', 'http://localhost:8000') +\n                            spec.get('basePath', ''))\n\n            base_path_tokens = list(filter(lambda s: s,\n                                    spec.get('basePath', '').split('/')))\n            paths = spec.get('paths')\n            if paths:\n                for path in paths:\n                    path_tokens = (base_path_tokens +\n                                   list(filter(lambda s: s, path.split('/'))))\n                    if path == '/':  # Path is a trailing slash\n                        path_tokens.insert(len(base_path_tokens), '/')\n                    elif path[-1] == '/':  # Path ends with a trailing slash\n                        path_tokens[-1] = path_tokens[-1] + '/'\n                    self.root.add_path(*path_tokens)\n                    endpoint = dict(paths[path])\n                    # path parameters (apply to all paths if not overriden)\n                    # exclude $ref as we have no system to handle that now\n                    global_parameters = list(endpoint.pop('parameters', []))\n                    # not used\n                    endpoint.pop('servers', None)\n                    endpoint.pop('$ref', None)\n                    endpoint.pop('summary', None)\n                    endpoint.pop('description', None)\n                    for method, info in endpoint.items():\n                        params = info.get('parameters', [])\n                        params = list(global_parameters + params)\n                        if params:\n                            def parameter_key(i): return (\n                                i.get('$ref', None),\n                                i.get('name', None),\n                                i.get('in', None)\n                            )\n                            # parameter is overriden based on $ref/in/name value\n                            # last value (local definition) takes precedence\n                            params_map = {parameter_key(p): p for p in params}\n                            params = params_map.values()\n                            for param in params:\n                                if param.get('$ref'):\n                                    for section in param.get('$ref').split('/'):\n                                        param = param.get(\n                                            section) if not section == '#' else spec\n\n                                if param.get('in') != 'path':\n                                    # Note that for completion mechanism, only\n                                    # name/node_type is used\n                                    # Parameters from methods/location\n                                    # are merged\n                                    full_path = path_tokens + [param['name']]\n                                    self.root.add_path(*full_path,\n                                                       node_type='file')\n        elif not self.url:\n            self.url = 'http://localhost:8000'\n\n    def __eq__(self, other):\n        return (self.url == other.url and\n                self.headers == other.headers and\n                self.options == other.options and\n                self.querystring_params == other.querystring_params and\n                self.body_params == other.body_params and\n                self.body_json_params == other.body_json_params and\n                self.should_exit == other.should_exit)\n\n    def copy(self):\n        context = Context(self.url)\n        context.headers = self.headers.copy()\n        context.querystring_params = self.querystring_params.copy()\n        context.body_params = self.body_params.copy()\n        context.body_json_params = self.body_json_params.copy()\n        context.options = self.options.copy()\n        context.should_exit = self.should_exit\n        return context\n\n    def update(self, context):\n        if context.url:\n            self.url = context.url\n\n        self.headers.update(context.headers)\n        self.querystring_params.update(context.querystring_params)\n        self.body_params.update(context.body_params)\n        self.body_json_params.update(context.body_json_params)\n        self.options.update(context.options)\n        self.should_exit = self.should_exit\n"
  },
  {
    "path": "http_prompt/context/transform.py",
    "content": "\"\"\"Functions that transform a Context object to a different representation.\"\"\"\n\nimport json\n\nfrom http_prompt.utils import smart_quote\n\n\ndef _noop(s):\n    return s\n\n\ndef _extract_httpie_options(context, quote=False, join_key_value=False,\n                            excluded_keys=None):\n    if quote:\n        quote_func = smart_quote\n    else:\n        quote_func = _noop\n\n    if join_key_value:\n        def form_new_opts(k, v): return [k + '=' + v]\n    else:\n        def form_new_opts(k, v): return [k, v]\n\n    excluded_keys = excluded_keys or []\n\n    opts = []\n    for k, v in sorted(context.options.items()):\n        if k not in excluded_keys:\n            if v is not None:\n                v = quote_func(v)\n                new_opts = form_new_opts(k, v)\n            else:\n                new_opts = [k]\n            opts += new_opts\n    return opts\n\n\ndef _extract_httpie_request_items(context, quote=False):\n    if quote:\n        quote_func = smart_quote\n    else:\n        quote_func = _noop\n\n    items = []\n    operators_and_items = [\n        # (separator, dict_of_request_items)\n        ('==', context.querystring_params),\n        (':=', context.body_json_params),\n        ('=', context.body_params),\n        (':', context.headers)\n    ]\n    for sep, item_dict in operators_and_items:\n        for k, value in sorted(item_dict.items()):\n            if sep == ':=':\n                json_str = json.dumps(value,\n                                      sort_keys=True)\n                item = '%s:=%s' % (k, quote_func(json_str))\n                items.append(item)\n            elif isinstance(value, (list, tuple)):\n                for v in value:\n                    item = quote_func('%s%s%s' % (k, sep, v))\n                    items.append(item)\n            else:\n                item = quote_func('%s%s%s' % (k, sep, value))\n                items.append(item)\n    return items\n\n\ndef extract_args_for_httpie_main(context, method=None):\n    \"\"\"Transform a Context object to a list of arguments that can be passed to\n    HTTPie main function.\n    \"\"\"\n    args = _extract_httpie_options(context)\n\n    if method:\n        args.append(method.upper())\n\n    args.append(context.url)\n    args += _extract_httpie_request_items(context)\n    return args\n\n\ndef format_to_curl(context, method=None):\n    \"\"\"Format a Context object to a cURL command.\"\"\"\n    raise NotImplementedError('curl format is not supported yet')\n\n\ndef format_to_raw(context, method=None):\n    \"\"\"Format a Context object to HTTP raw text.\"\"\"\n    raise NotImplementedError('raw format is not supported yet')\n\n\ndef format_to_httpie(context, method=None):\n    \"\"\"Format a Context object to an HTTPie command.\"\"\"\n    cmd = ['http'] + _extract_httpie_options(context, quote=True,\n                                             join_key_value=True)\n    if method:\n        cmd.append(method.upper())\n    cmd.append(context.url)\n    cmd += _extract_httpie_request_items(context, quote=True)\n    return ' '.join(cmd) + '\\n'\n\n\ndef format_to_http_prompt(context, excluded_options=None):\n    \"\"\"Format a Context object to HTTP Prompt commands.\"\"\"\n    cmds = _extract_httpie_options(context, quote=True, join_key_value=True,\n                                   excluded_keys=excluded_options)\n    cmds.append('cd ' + smart_quote(context.url))\n    cmds += _extract_httpie_request_items(context, quote=True)\n    return '\\n'.join(cmds) + '\\n'\n"
  },
  {
    "path": "http_prompt/contextio.py",
    "content": "\"\"\"Serialization and deserialization of a Context object.\"\"\"\n\nimport io\nimport os\n\nfrom . import xdg\nfrom .context.transform import format_to_http_prompt\nfrom .execution import execute\n\n\n# Don't save these HTTPie options to avoid collision with user config file\nEXCLUDED_OPTIONS = ['--style']\n\n# Filename the current environment context will be saved to\nCONTEXT_FILENAME = 'context.hp'\n\n\ndef _get_context_filepath():\n    dir_path = xdg.get_data_dir()\n    return os.path.join(dir_path, CONTEXT_FILENAME)\n\n\ndef load_context(context, file_path=None):\n    \"\"\"Load a Context object in place from user data directory.\"\"\"\n    if not file_path:\n        file_path = _get_context_filepath()\n    if os.path.exists(file_path):\n        with open(file_path, encoding='utf-8') as f:\n            for line in f:\n                execute(line, context)\n\n\ndef save_context(context):\n    \"\"\"Save a Context object to user data directory.\"\"\"\n    file_path = _get_context_filepath()\n    content = format_to_http_prompt(context, excluded_options=EXCLUDED_OPTIONS)\n    with open(file_path, 'w', encoding='utf-8') as f:\n        f.write(content)\n"
  },
  {
    "path": "http_prompt/defaultconfig.py",
    "content": "# Highlighting style for prompt commands. Available values:\n# algol, algol_nu, autumn, borland, bw, colorful, default, emacs, friendly,\n# fruity, igor, lovelace, manni, monokai, murphy, native, paraiso-dark,\n# paraiso-light, pastie, perldoc, rrt, solarized, tango, trac, vim, vs, xcode.\n# Preview themes at http://http-prompt.com/themes\ncommand_style = 'solarized'\n\n# Highlighting style for HTTPie's output. Available values are the same as\n# command_style. Set this to None to use HTTPie's default style, which you\n# can refer to https://httpie.org/doc#config-file-location\noutput_style = None\n\n# The tool used to paginate output. Available values: 'less' and 'more'.\n# Note that 'more' does not support ANSI colors.\npager = 'less'\n\n# What to do when a response has a 'Set-Cookie' header? Available values:\n# 'auto': set the cookie automatically and silently\n# 'ask': ask the user if they want to set the cookie\n# 'off': do nothing with the 'Set-Cookie' header\nset_cookies = 'auto'\n\n# Enable Vi editor mode? Available values: True / False.\n# When Vi mode is enabled, you use Vi-like keybindings to edit your commands.\n# When it is disabled, you use Emacs keybindings.\nvi = False\n"
  },
  {
    "path": "http_prompt/execution.py",
    "content": "import io\nimport json\nimport re\nimport os\nimport sys\nfrom urllib.parse import urlparse, urljoin\n\nimport click\n\nfrom subprocess import CalledProcessError, Popen, PIPE\n\nfrom httpie.context import Environment\nfrom httpie.core import main as httpie_main\nfrom parsimonious.exceptions import ParseError, VisitationError\nfrom parsimonious.grammar import Grammar\nfrom parsimonious.nodes import NodeVisitor\nfrom parsimonious.nodes import Node\nfrom pygments.token import String, Name\n\nfrom .completion import ROOT_COMMANDS, ACTIONS, OPTION_NAMES, HEADER_NAMES\nfrom .context import Context\nfrom .context.transform import (\n    extract_args_for_httpie_main,\n    format_to_curl,\n    format_to_httpie,\n    format_to_http_prompt)\nfrom .output import Printer, TextWriter\nfrom .utils import unescape, unquote, colformat\n\n\nHTTPIE_PROGRAM_NAME = 'http'\n\n\ngrammar = r\"\"\"\n    command = mutation / immutation\n\n    mutation = concat_mut+ / nonconcat_mut\n    immutation = preview / action / ls / env / help / exit / exec / source / clear / _\n\n    concat_mut = option_mut / full_quoted_mut / value_quoted_mut / unquoted_mut\n    nonconcat_mut = cd / rm\n\n    preview = _ tool _ (method _)? (urlpath _)? concat_mut* redir_out? _\n    action = _ method _ (urlpath _)? concat_mut* redir_out? _\n    urlpath = (~r\"https?://\" unquoted_string) /\n              (!concat_mut !redir_out string)\n\n    clear = _ \"clear\" _\n    help = _ \"help\" _\n    exit = _ \"exit\" _\n    ls = _ \"ls\" _ (urlpath _)? (redir_out)?\n    env  = _ \"env\" _ (redir_out)?\n    source = _ \"source\" _ filepath _\n    exec = _ \"exec\" _ filepath _\n\n    redir_out = redir_append / redir_write / pipe\n    redir_append = _ \">>\" _ filepath _\n    redir_write = _ \">\" _ filepath _\n    pipe = _ \"|\" _ (shell_subs / shell_code) _\n\n    unquoted_mut = _ unquoted_mutkey mutop unquoted_mutval _\n    full_quoted_mut = full_squoted_mut / full_dquoted_mut\n    value_quoted_mut = value_squoted_mut / value_dquoted_mut\n    full_squoted_mut = _ \"'\" squoted_mutkey mutop squoted_mutval \"'\" _\n    full_dquoted_mut = _ '\"' dquoted_mutkey mutop dquoted_mutval '\"' _\n    value_squoted_mut = _ unquoted_mutkey mutop \"'\" squoted_mutval \"'\" _\n    value_dquoted_mut = _ unquoted_mutkey mutop '\"' dquoted_mutval '\"' _\n    mutop = \":=\" / \":\" / \"==\" / \"=\"\n    unquoted_mutkey = unquoted_mutkey_item+\n    unquoted_mutval = unquoted_stringitem*\n    unquoted_mutkey_item = shell_subs / unquoted_mutkey_char / escapeseq\n    unquoted_mutkey_char = ~r\"[^\\s'\\\"\\\\=:>]\"\n    squoted_mutkey = squoted_mutkey_item+\n    squoted_mutval = squoted_stringitem*\n    squoted_mutkey_item = shell_subs / squoted_mutkey_char / escapeseq\n    squoted_mutkey_char = ~r\"[^\\r\\n'\\\\=:]\"\n    dquoted_mutkey = dquoted_mutkey_item+\n    dquoted_mutval = dquoted_stringitem*\n    dquoted_mutkey_item = shell_subs / dquoted_mutkey_char / escapeseq\n    dquoted_mutkey_char = ~r'[^\\r\\n\"\\\\=:]'\n\n    option_mut = flag_option_mut / value_option_mut\n    flag_option_mut = _ flag_optname _\n    flag_optname = \"--json\" / \"-j\" / \"--form\" / \"-f\" / \"--verbose\" / \"-v\" /\n                   \"--headers\" / \"-h\" / \"--body\" / \"-b\" / \"--stream\" / \"-S\" /\n                   \"--download\" / \"-d\" / \"--continue\" / \"-c\" / \"--follow\" /\n                   \"--check-status\" / \"--ignore-stdin\" / \"--help\" /\n                   \"--version\" / \"--traceback\" / \"--debug\"\n    value_option_mut = _ value_optname ~r\"(\\s+|=)\" string _\n    value_optname = \"--pretty\" / \"--style\" / \"-s\" / \"--print\" / \"-p\" /\n                    \"--output\" / \"-o\" / \"--session-read-only\" / \"--session\" /\n                    \"--auth-type\" / \"--auth\" / \"-a\" / \"--proxy\" / \"--verify\" /\n                    \"--cert\" / \"--cert-key\" / \"--timeout\" / \"--raw\"\n\n    cd = _ \"cd\" _ string? _\n    rm = (_ \"rm\" _ \"*\" _) / (_ \"rm\" _ ~r\"\\-(h|q|b|o)\" _ mutkey _)\n    tool = \"httpie\" / \"curl\"\n    method = ~r\"get\"i / ~r\"head\"i / ~r\"post\"i / ~r\"put\"i / ~r\"delete\"i /\n             ~r\"patch\"i / ~r\"options\"i / ~r\"connect\"i\n    mutkey = unquoted_mutkey / (\"'\" squoted_mutkey \"'\") /\n             ('\"' dquoted_mutkey '\"') / flag_optname / value_optname\n\n    string = quoted_string / unquoted_string\n    quoted_string = ('\"' dquoted_stringitem* '\"') /\n                    (\"'\" squoted_stringitem* \"'\")\n    unquoted_string = unquoted_stringitem+\n    dquoted_stringitem = shell_subs / dquoted_stringchar / escapeseq\n    squoted_stringitem = shell_subs / squoted_stringchar / escapeseq\n    unquoted_stringitem = shell_subs / unquoted_stringchar / escapeseq\n    dquoted_stringchar = ~r'[^\\r\\n\"\\\\]'\n    squoted_stringchar = ~r\"[^\\r\\n'\\\\]\"\n    unquoted_stringchar = ~r\"[^\\s'\\\\]\"\n    escapeseq = ~r\"\\\\.\"\n    _ = ~r\"\\s*\"\n\n    shell_subs = \"`\" shell_code \"`\"\n    shell_code = ~r\"[^`]*\"\n\"\"\"\n\nif sys.platform == 'win32':\n    # XXX: Windows use backslashes as separators in its filesystem path, so we\n    # have to avoid using backslashes to escape chars here.\n    grammar += r\"\"\"\n        filepath = quoted_filepath / unquoted_filepath\n        quoted_filepath = ('\"' dquoted_filepath_char+ '\"') /\n                          (\"'\" squoted_filepath_char+ \"'\")\n        dquoted_filepath_char = ~r'[^\\r\\n\"]'\n        squoted_filepath_char = ~r\"[^\\r\\n']\"\n        unquoted_filepath = unquoted_filepath_char+\n        unquoted_filepath_char = ~r\"[^\\s\\\"]\"\n    \"\"\"\nelse:\n    grammar += r\"\"\"\n        filepath = string\n    \"\"\"\n\ngrammar = Grammar(grammar)\n\n\nif Environment.colors == 256:\n    from pygments.formatters.terminal256 import (\n        Terminal256Formatter as TerminalFormatter)\nelse:\n    from pygments.formatters.terminal import TerminalFormatter\n\n\ndef urljoin2(base, path, **kwargs):\n    if not base.endswith('/'):\n        base += '/'\n    url = urljoin(base, path, **kwargs)\n    if url.endswith('/') and not path.endswith('/'):\n        url = url[:-1]\n    return url\n\n\ndef generate_help_text():\n    \"\"\"Return a formatted string listing commands, HTTPie options, and HTTP\n    actions.\n    \"\"\"\n    def generate_cmds_with_explanations(summary, cmds):\n        text = '{0}:\\n'.format(summary)\n        for cmd, explanation in cmds:\n            text += '\\t{0:<10}\\t{1:<20}\\n'.format(cmd, explanation)\n        return text + '\\n'\n\n    text = generate_cmds_with_explanations('Commands', ROOT_COMMANDS.items())\n    text += generate_cmds_with_explanations('Options', OPTION_NAMES.items())\n    text += generate_cmds_with_explanations('Actions', ACTIONS.items())\n    text += generate_cmds_with_explanations('Headers', HEADER_NAMES.items())\n    return text\n\n\nif sys.platform == 'win32':  # nocover\n    def normalize_filepath(path):\n        return unquote(path)\nelse:\n    def normalize_filepath(path):\n        return unescape(unquote(path))\n\n\nclass DummyExecutionListener(object):\n\n    def context_changed(self, context):\n        pass\n\n    def response_returned(self, context, response):\n        pass\n\n\nclass ExecutionVisitor(NodeVisitor):\n\n    unwrapped_exceptions = (CalledProcessError,)\n\n    def __init__(self, context, listener=None, style=None):\n        super(ExecutionVisitor, self).__init__()\n        self.context = context\n\n        self.context_override = Context(context.url)\n        self.method = None\n        self.tool = None\n        self._output = Printer()\n\n        # If there's a pipe, as in \"httpie post | sed s/POST/GET/\", this\n        # variable points to the \"sed\" Popen object. The variable is necessary\n        # because the we need to redirect Popen.stdout to Printer, which does\n        # output pagination.\n        self.pipe_proc = None\n\n        self.listener = listener or DummyExecutionListener()\n\n        # Last response object returned by HTTPie\n        self.last_response = None\n\n        # Pygments formatter, used to render output with colors in some cases\n        if style:\n            self.formatter = TerminalFormatter(style=style)\n        else:\n            self.formatter = None\n\n    @property\n    def output(self):\n        return self._output\n\n    @output.setter\n    def output(self, new_output):\n        if self._output:\n            self._output.close()\n        self._output = new_output\n\n    def visit_method(self, node, children):\n        self.method = node.text\n        return node\n\n    def visit_urlpath(self, node, children):\n        path = node.text\n        self.context_override.url = urljoin2(self.context_override.url, path)\n        return node\n\n    def visit_cd(self, node, children):\n        _, _, _, path, _ = children\n\n        if isinstance(path, Node):\n            seg = urlparse(self.context_override.url)\n            self.context_override.url = seg.scheme + '://' + seg.netloc\n        else:\n            self.context_override.url = urljoin2(\n                self.context_override.url, path)\n\n        return node\n\n    def visit_rm(self, node, children):\n        children = children[0]\n        kind = children[3].text\n\n        if kind == '*':\n            # Clear context\n            for target in [self.context.headers,\n                           self.context.querystring_params,\n                           self.context.body_params,\n                           self.context.body_json_params,\n                           self.context.options]:\n                target.clear()\n            return node\n\n        name = children[5]\n        if kind == '-h':\n            target = self.context.headers\n        elif kind == '-q':\n            target = self.context.querystring_params\n        elif kind == '-o':\n            target = self.context.options\n        else:\n            assert kind == '-b'\n            # TODO: This is kind of ugly, will fix it\n            if name == '*':\n                self.context.body_params.clear()\n                self.context.body_json_params.clear()\n            else:\n                try:\n                    del self.context.body_params[name]\n                except KeyError:\n                    del self.context.body_json_params[name]\n            return node\n\n        if name == '*':\n            target.clear()\n        else:\n            del target[name]\n\n        return node\n\n    def visit_help(self, node, children):\n        self.output.write(generate_help_text())\n        return node\n\n    def _redirect_output(self, filepath, mode):\n        filepath = normalize_filepath(filepath)\n        self.output = TextWriter(open(os.path.expandvars(filepath), mode))\n\n    def visit_redir_append(self, node, children):\n        self._redirect_output(children[3], 'ab')\n        return node\n\n    def visit_redir_write(self, node, children):\n        self._redirect_output(children[3], 'wb')\n        return node\n\n    def visit_pipe(self, node, children):\n        cmd = children[3]\n        self.pipe_proc = Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE)\n        self.output = TextWriter(self.pipe_proc.stdin)\n        return node\n\n    def visit_exec(self, node, children):\n        path = normalize_filepath(children[3])\n        with open(path, encoding='utf-8') as f:\n            # Wipe out context first\n            execute('rm *', self.context, self.listener)\n            for line in f:\n                execute(line, self.context, self.listener)\n        return node\n\n    def visit_source(self, node, children):\n        path = normalize_filepath(children[3])\n        with open(path, encoding='utf-8') as f:\n            for line in f:\n                execute(line, self.context, self.listener)\n        return node\n\n    def _colorize(self, text, token_type):\n        if not self.formatter:\n            return text\n\n        out = io.StringIO()\n        self.formatter.format([(token_type, text)], out)\n        return out.getvalue()\n\n    def visit_ls(self, node, children):\n        path = urlparse(self.context_override.url).path\n        path = filter(None, path.split('/'))\n        nodes = self.context.root.ls(*path)\n        if self.output.isatty():\n            names = []\n            for node in nodes:\n                token_type = String if node.data.get('type') == 'dir' else Name\n                name = self._colorize(node.name, token_type)\n                names.append(name)\n            lines = list(colformat(list(names)))\n        else:\n            lines = [n.name for n in nodes]\n        if lines:\n            self.output.write('\\n'.join(lines))\n        return node\n\n    def visit_env(self, node, children):\n        text = format_to_http_prompt(self.context)\n        self.output.write(text)\n        return node\n\n    def visit_exit(self, node, children):\n        self.context.should_exit = True\n        return node\n\n    def visit_clear(self, node, children):\n        self.output.clear()\n        return node\n\n    def visit_mutkey(self, node, children):\n        if isinstance(children[0], list):\n            return children[0][1]\n        return children[0]\n\n    def _mutate(self, node, key, op, val):\n        if op == ':=':\n            self.context_override.body_json_params[key] = json.loads(val)\n        elif op == ':':\n            self.context_override.headers[key] = val\n        elif op == '=':\n            self.context_override.body_params[key] = val\n        elif op == '==':\n            # You can have multiple querystring params with the same name,\n            # so we use a list to store multiple values (#20)\n            params = self.context_override.querystring_params\n            if key not in params:\n                params[key] = [val]\n            else:\n                params[key].append(val)\n        return node\n\n    def visit_unquoted_mut(self, node, children):\n        _, key, op, val, _ = children\n        return self._mutate(node, key, op, val)\n\n    def visit_full_squoted_mut(self, node, children):\n        _, _, key, op, val, _, _ = children\n        return self._mutate(node, key, op, val)\n\n    def visit_full_dquoted_mut(self, node, children):\n        _, _, key, op, val, _, _ = children\n        return self._mutate(node, key, op, val)\n\n    def visit_value_squoted_mut(self, node, children):\n        _, key, op, _, val, _, _ = children\n        return self._mutate(node, key, op, val)\n\n    def visit_value_dquoted_mut(self, node, children):\n        _, key, op, _, val, _, _ = children\n        return self._mutate(node, key, op, val)\n\n    def _visit_mut_key_or_val(self, node, children):\n        return unescape(''.join(children), exclude=':=')\n\n    visit_unquoted_mutkey = _visit_mut_key_or_val\n    visit_unquoted_mutval = _visit_mut_key_or_val\n    visit_squoted_mutkey = _visit_mut_key_or_val\n    visit_squoted_mutval = _visit_mut_key_or_val\n    visit_dquoted_mutkey = _visit_mut_key_or_val\n    visit_dquoted_mutval = _visit_mut_key_or_val\n\n    def visit_mutop(self, node, children):\n        return node.text\n\n    def visit_flag_option_mut(self, node, children):\n        _, key, _ = children\n        self.context_override.options[key] = None\n        return node\n\n    def visit_flag_optname(self, node, children):\n        return node.text\n\n    def visit_value_option_mut(self, node, children):\n        _, key, _, val, _ = children\n        self.context_override.options[key] = val\n        return node\n\n    def visit_value_optname(self, node, children):\n        return node.text\n\n    def visit_filepath(self, node, children):\n        return children[0]\n\n    def visit_string(self, node, children):\n        return children[0]\n\n    def visit_quoted_filepath(self, node, children):\n        return node.text[1:-1]\n\n    def visit_unquoted_filepath(self, node, children):\n        return node.text\n\n    def visit_unquoted_string(self, node, children):\n        return unescape(''.join(children))\n\n    def visit_quoted_string(self, node, children):\n        return self._visit_mut_key_or_val(node, children[0][1])\n\n    def _visit_stringitem(self, node, children):\n        child = children[0]\n        if hasattr(child, 'text'):\n            return child.text\n        return child\n\n    visit_unquoted_mutkey_item = _visit_stringitem\n    visit_unquoted_stringitem = _visit_stringitem\n    visit_squoted_mutkey_item = _visit_stringitem\n    visit_squoted_stringitem = _visit_stringitem\n    visit_dquoted_mutkey_item = _visit_stringitem\n    visit_dquoted_stringitem = _visit_stringitem\n\n    def visit_tool(self, node, children):\n        self.tool = node.text\n        return node\n\n    def visit_mutation(self, node, children):\n        self.context.update(self.context_override)\n        self.listener.context_changed(self.context)\n        return node\n\n    def _final_context(self):\n        context = self.context.copy()\n        context.update(self.context_override)\n        return context\n\n    def _trace_get_response(self, frame, event, arg):\n        func_name = frame.f_code.co_name\n        if func_name == 'get_response':\n            if event == 'call':\n                return self._trace_get_response\n            elif event == 'return':\n                self.last_response = arg\n\n    def _call_httpie_main(self):\n        context = self._final_context()\n        args = extract_args_for_httpie_main(context, self.method)\n        env = Environment(stdout=self.output, stdin=sys.stdin,\n                          is_windows=False)\n        env.stdout_isatty = self.output.isatty()\n        env.stdin_isatty = sys.stdin.isatty()\n\n        # XXX: httpie_main() doesn't provide an API for us to get the\n        # HTTP response object, so we use this super dirty hack -\n        # sys.settrace() to intercept get_response() that is called in\n        # httpie_main() internally. The HTTP response intercepted is\n        # assigned to self.last_response, which self.listener may be\n        # interested in.\n        sys.settrace(self._trace_get_response)\n        try:\n            httpie_main([HTTPIE_PROGRAM_NAME, *args], env=env)\n        finally:\n            sys.settrace(None)\n\n    def visit_immutation(self, node, children):\n        self.output.close()\n        if self.pipe_proc:\n            Printer().write(self.pipe_proc.stdout.read())\n        return node\n\n    def visit_preview(self, node, children):\n        context = self._final_context()\n        if self.tool == 'httpie':\n            command = format_to_httpie(context, self.method)\n        else:\n            assert self.tool == 'curl'\n            command = format_to_curl(context, self.method)\n        self.output.write(command)\n        return node\n\n    def visit_action(self, node, children):\n        self._call_httpie_main()\n        if self.last_response:\n            self.listener.response_returned(self.context, self.last_response)\n        return node\n\n    def visit_shell_subs(self, node, children):\n        cmd = children[1]\n        p = Popen(cmd, shell=True, stdout=PIPE)\n        return p.stdout.read().decode().rstrip()\n\n    def visit_shell_code(self, node, children):\n        return node.text\n\n    def generic_visit(self, node, children):\n        if not node.expr_name and node.children:\n            if len(children) == 1:\n                return children[0]\n            return children\n        return node\n\n\ndef execute(command, context, listener=None, style=None):\n    try:\n        root = grammar.parse(command)\n    except ParseError as err:\n        # TODO: Better error message\n        part = command[err.pos:err.pos + 10]\n        click.secho('Syntax error near \"%s\"' % part, err=True, fg='red')\n    else:\n        visitor = ExecutionVisitor(context, listener=listener, style=style)\n        try:\n            visitor.visit(root)\n        except VisitationError as err:\n            exc_class = err.original_class\n            if exc_class is KeyError:\n                # XXX: Need to parse VisitationError error message to get the\n                # original error message as VisitationError doesn't hold the\n                # original exception object\n                key = re.search(r\"KeyError: u?'(.*)'\", str(err)).group(1)\n                click.secho(\"Key '%s' not found\" % key, err=True,\n                            fg='red')\n            elif issubclass(exc_class, OSError):\n                msg = str(err).splitlines()[0]\n\n                # Remove the exception class name at the beginning\n                msg = msg[msg.find(':') + 2:]\n                click.secho(msg, err=True, fg='red')\n            else:\n                # TODO: Better error message\n                click.secho(str(err), err=True, fg='red')\n        except CalledProcessError as err:\n            click.secho(err.output + ' (exit status %d)' % err.returncode,\n                        fg='red')\n"
  },
  {
    "path": "http_prompt/lexer.py",
    "content": "from pygments.lexer import (RegexLexer, bygroups, words, using, include,\n                            combined)\nfrom pygments.lexers import BashLexer\n\nfrom pygments.token import Text, String, Keyword, Name, Operator\n\nfrom . import options as opt\n\n\n__all__ = ['HttpPromptLexer']\n\n\nFLAG_OPTIONS = [name for name, _ in opt.FLAG_OPTIONS]\nVALUE_OPTIONS = [name for name, _ in opt.VALUE_OPTIONS]\nHTTP_METHODS = ('get', 'head', 'post', 'put', 'patch',\n                'delete', 'options', 'connect')\n\n\ndef string_rules(state):\n    return [\n        (r'(\")((?:[^\\r\\n\"\\\\]|(?:\\\\.))+)(\")',\n         bygroups(Text, String, Text), state),\n\n        (r'(\")((?:[^\\r\\n\"\\\\]|(?:\\\\.))+)', bygroups(Text, String), state),\n\n        (r\"(')((?:[^\\r\\n'\\\\]|(?:\\\\.))+)(')\",\n         bygroups(Text, String, Text), state),\n\n        (r\"(')((?:[^\\r\\n'\\\\]|(?:\\\\.))+)\", bygroups(Text, String), state),\n\n        (r'([^\\s\\'\\\\]|(\\\\.))+', String, state)\n    ]\n\n\nclass HttpPromptLexer(RegexLexer):\n\n    name = 'HttpPrompt'\n    aliases = ['http-prompt']\n    filenames = ['*.http-prompt']\n\n    tokens = {\n        'root': [\n            (r'\\s+', Text),\n            (r'(cd)(\\s*)', bygroups(Keyword, Text), 'cd'),\n            (r'(rm)(\\s*)', bygroups(Keyword, Text), 'rm_option'),\n            (r'(httpie|curl)(\\s*)', bygroups(Keyword, Text), 'action'),\n\n            (words(HTTP_METHODS, prefix='(?i)', suffix=r'(?!\\S)(\\s*)'),\n             bygroups(Keyword, Text), combined('redir_out', 'urlpath')),\n\n            (r'(clear)(\\s*)', bygroups(Keyword, Text), 'end'),\n            (r'(exit)(\\s*)', bygroups(Keyword, Text), 'end'),\n            (r'(help)(\\s)*', bygroups(Keyword, Text), 'end'),\n            (r'(env)(\\s*)', bygroups(Keyword, Text),\n             combined('redir_out', 'pipe')),\n            (r'(source)(\\s*)', bygroups(Keyword, Text), 'file_path'),\n            (r'(exec)(\\s*)', bygroups(Keyword, Text), 'file_path'),\n            (r'(ls)(\\s*)', bygroups(Keyword, Text),\n             combined('redir_out', 'urlpath')),\n            (r'', Text, 'concat_mut')\n        ],\n\n        'cd': string_rules('end'),\n\n        'rm_option': [\n            (r'(\\-(?:h|o|b|q))(\\s*)', bygroups(Name, Text), 'rm_name'),\n            (r'(\\*)(\\s*)', bygroups(Name, Text), 'end')\n        ],\n        'rm_name': string_rules('end'),\n\n        'shell_command': [\n            (r'(`)([^`]*)(`)', bygroups(Text, using(BashLexer), Text)),\n        ],\n        'pipe': [\n            (r'(\\s*)(\\|)(.*)', bygroups(Text, Operator, using(BashLexer))),\n        ],\n        'concat_mut': [\n            (r'$', Text, 'end'),\n            (r'\\s+', Text),\n\n            # Flag options, such as (--form) and (--json)\n            (words(FLAG_OPTIONS, suffix=r'\\b'), Name, 'concat_mut'),\n\n            # Options with values, such as (--style=default) and (--pretty all)\n            (words(VALUE_OPTIONS, suffix=r'\\b'), Name,\n             combined('shell_command', 'option_op')),\n\n            include('shell_command'),\n\n            # Unquoted or value-quoted request mutation,\n            # such as (name=\"John Doe\") and (name=John\\ Doe)\n            (r'((?:[^\\s\\'\"\\\\=:]|(?:\\\\.))*)(:=|:|==|=)',\n             bygroups(Name, Operator),\n             combined('shell_command', 'unquoted_mut')),\n\n            # Full single-quoted request mutation, such as ('name=John Doe')\n            (r\"(')((?:[^\\r\\n'\\\\=:]|(?:\\\\.))+)(:=|:|==|=)\",\n             bygroups(Text, Name, Operator),\n             combined('shell_command', 'squoted_mut')),\n\n            # Full double-quoted request mutation, such as (\"name=John Doe\")\n            (r'(\")((?:[^\\r\\n\"\\\\=:]|(?:\\\\.))+)(:=|:|==|=)',\n             bygroups(Text, Name, Operator),\n             combined('shell_command', 'dquoted_mut'))\n        ],\n\n        'option_op': [\n            (r'(\\s+|=)', Operator, 'option_value'),\n        ],\n        'option_value': string_rules('#pop:2'),\n        'file_path': string_rules('end'),\n        'redir_out': [\n            (r'(?i)(>>?)(\\s*)', bygroups(Operator, Text), 'file_path')\n        ],\n\n        'unquoted_mut': string_rules('#pop'),\n        'squoted_mut': [\n            (r\"((?:[^\\r\\n'\\\\]|(?:\\\\.))+)(')\", bygroups(String, Text), '#pop'),\n            (r\"([^\\r\\n'\\\\]|(\\\\.))+\", String, '#pop')\n        ],\n        'dquoted_mut': [\n            (r'((?:[^\\r\\n\"\\\\]|(?:\\\\.))+)(\")', bygroups(String, Text), '#pop'),\n            (r'([^\\r\\n\"\\\\]|(\\\\.))+', String, '#pop')\n        ],\n\n        'action': [\n            (words(HTTP_METHODS, prefix='(?i)', suffix=r'(\\s*)'),\n             bygroups(Keyword, Text),\n             combined('redir_out', 'pipe', 'urlpath')),\n            (r'', Text, combined('redir_out', 'pipe', 'urlpath'))\n        ],\n        'urlpath': [\n            (r'https?://([^\\s\"\\'\\\\]|(\\\\.))+', String,\n             combined('concat_mut', 'redir_out', 'pipe')),\n\n            (r'(\")(https?://(?:[^\\r\\n\"\\\\]|(?:\\\\.))+)(\")',\n             bygroups(Text, String, Text),\n             combined('concat_mut', 'redir_out', 'pipe')),\n\n            (r'(\")(https?://(?:[^\\r\\n\"\\\\]|(?:\\\\.))+)',\n             bygroups(Text, String)),\n\n            (r\"(')(https?://(?:[^\\r\\n'\\\\]|(?:\\\\.))+)(')\",\n             bygroups(Text, String, Text),\n             combined('concat_mut', 'redir_out', 'pipe')),\n\n            (r\"(')(https?://(?:[^\\r\\n'\\\\]|(?:\\\\.))+)\",\n             bygroups(Text, String)),\n\n            (r'(\")((?:[^\\r\\n\"\\\\=:]|(?:\\\\.))+)(\")',\n             bygroups(Text, String, Text),\n             combined('concat_mut', 'redir_out', 'pipe')),\n\n            (r'(\")((?:[^\\r\\n\"\\\\=:]|(?:\\\\.))+)', bygroups(Text, String)),\n\n            (r\"(')((?:[^\\r\\n'\\\\=:]|(?:\\\\.))+)(')\",\n             bygroups(Text, String, Text),\n             combined('concat_mut', 'redir_out', 'pipe')),\n\n            (r\"(')((?:[^\\r\\n'\\\\=:]|(?:\\\\.))+)\", bygroups(Text, String)),\n\n            (r'([^\\-](?:[^\\s\"\\'\\\\=:]|(?:\\\\.))+)(\\s+|$)',\n             bygroups(String, Text),\n             combined('concat_mut', 'redir_out', 'pipe')),\n\n            (r'', Text,\n             combined('concat_mut', 'redir_out', 'pipe'))\n        ],\n\n        'end': [\n            (r'\\n', Text, 'root')\n        ]\n    }\n"
  },
  {
    "path": "http_prompt/options.py",
    "content": "\"\"\"Meta data for HTTPie options.\"\"\"\n\nFLAG_OPTIONS = [\n    ('--body', 'Print only response body'),\n    ('--check-status', 'Check HTTP status code'),\n    ('--continue', 'Resume an interrupted download'),\n    ('--debug', 'Print debug information'),\n    ('--download', 'Download as a file'),\n    ('--follow', 'Allow full redirects'),\n    ('--form', 'Send as form fields'),\n    ('--headers', 'Print only response headers'),\n    ('--help', 'Show tool (HTTPie, cURL) help message'),\n    ('--ignore-stdin', 'Do not read stdin'),\n    ('--json', 'Send as a JSON object (default)'),\n    ('--stream', 'Stream the output'),\n    ('--traceback', 'Print exception traceback'),\n    ('--verbose', 'Print the whole request and response'),\n    ('--version', 'Show version'),\n    ('-b', 'Shorthand for --body'),\n    ('-c', 'Shorthand for --continue'),\n    ('-d', 'Shorthand for --download'),\n    ('-f', 'Shorthand for --form'),\n    ('-h', 'Shorthand for --headers'),\n    ('-j', 'Shorthand for --json'),\n    ('-S', 'Shorthand for --stream'),\n    ('-v', 'Shorthand for --verbose'),\n]\n\nVALUE_OPTIONS = [\n    ('--auth', 'Do authentication'),\n    ('--auth-type', 'Authentication mechanism to be used'),\n    ('--cert', 'Specify client SSL certificate'),\n    ('--cert-key', 'The private key to use with SSL'),\n    ('--output', 'Save output to a file'),\n    ('--pretty', 'Control output processing'),\n    ('--print', 'Specify what output should contain'),\n    ('--proxy', 'Specify proxy URL'),\n    ('--raw', 'Pass raw request data without extra processing'),\n    ('--session', 'Create, or reuse and update a session'),\n    ('--session-read-only', 'Create or read a session'),\n    ('--style', 'Output coloring style'),\n    ('--timeout', 'Connection timeout in seconds'),\n    ('--verify', 'Set to \"no\" to skip SSL certificate checking'),\n    ('-a', 'Shorthand for --auth'),\n    ('-o', 'Shorthand for --output'),\n    ('-p', 'Shorthand for --print'),\n    ('-s', 'Shorthand for --style'),\n]\n\nPRETTY_CHOICES = ('all', 'colors', 'format', 'none')\n\nSTYLE_CHOICES = ('algol', 'algol_nu', 'autumn', 'borland', 'bw', 'colorful',\n                 'default', 'emacs', 'friendly', 'fruity', 'igor', 'lovelace',\n                 'manni', 'monokai', 'murphy', 'native', 'paraiso-dark',\n                 'paraiso-light', 'pastie', 'perldoc', 'rrt', 'solarized',\n                 'tango', 'trac', 'vim', 'vs', 'xcode')\n\nAUTH_TYPE_CHOICES = ('basic', 'digest')\n\nVERIFY_CHOICES = ('no', 'yes')\n\nOPTION_VALUE_CHOICES = {\n    '--auth-type': AUTH_TYPE_CHOICES,\n    '--pretty': PRETTY_CHOICES,\n    '--style': STYLE_CHOICES,\n    '--verify': VERIFY_CHOICES,\n    '-p': PRETTY_CHOICES,\n    '-s': STYLE_CHOICES,\n}\n"
  },
  {
    "path": "http_prompt/output.py",
    "content": "import sys\n\nimport click\n\n\nclass Printer(object):\n    \"\"\"Wrap click.echo_via_pager() so it accepts binary data.\"\"\"\n\n    def write(self, data):\n        if isinstance(data, bytes):\n            data = data.decode()\n\n        # echo_via_pager() already appends a '\\n' at the end of text,\n        # so we use rstrip() to remove extra newlines (#89)\n        click.echo_via_pager(data.rstrip())\n\n    def flush(self):\n        pass\n\n    def close(self):\n        pass\n\n    def isatty(self):\n        return True\n\n    def fileno(self):\n        return sys.stdout.fileno()\n\n    def clear(self):\n        click.clear()\n\n\nclass TextWriter(object):\n    \"\"\"Wrap a file-like object, opened with 'wb' or 'ab', so it accepts text\n    data.\n    \"\"\"\n\n    def __init__(self, fp):\n        self.fp = fp\n\n    def write(self, data):\n        if isinstance(data, str):\n            data = data.encode()\n        self.fp.write(data)\n\n    def flush(self):\n        self.fp.flush()\n\n    def close(self):\n        self.fp.close()\n\n    def isatty(self):\n        return self.fp.isatty()\n\n    def fileno(self):\n        return self.fp.fileno()\n"
  },
  {
    "path": "http_prompt/tree.py",
    "content": "\"\"\"Tree data structure for ls command to work with OpenAPI specification.\"\"\"\n\n\nclass Node(object):\n\n    def __init__(self, name, data=None, parent=None):\n        if name in ('.', '..'):\n            raise ValueError(\"name cannot be '.' or '..'\")\n\n        self.name = name\n        self.data = data or {}\n        self.parent = parent\n        self.children = set()\n\n    def __str__(self):\n        return self.name\n\n    def __repr__(self):\n        return \"Node('{}', '{}')\".format(self.name, self.data.get('type'))\n\n    def __lt__(self, other):\n        ta = self.data.get('type')\n        tb = other.data.get('type')\n        if ta != tb:\n            return ta < tb\n        return self.name < other.name\n\n    def __eq__(self, other):\n        return self.name == other.name and self.data == other.data\n\n    def __hash__(self):\n        return hash((self.name, self.data.get('type')))\n\n    def add_path(self, *path, **kwargs):\n        node_type = kwargs.get('node_type', 'dir')\n        name = path[0]\n        tail = path[1:]\n        child = self.find_child(name, wildcard=False)\n        if not child:\n            data = {'type': 'dir' if tail else node_type}\n            child = Node(name, data=data, parent=self)\n            self.children.add(child)\n\n        if tail:\n            child.add_path(*tail, node_type=node_type)\n\n    def find_child(self, name, wildcard=True):\n        for child in self.children:\n            if child.name == name:\n                return child\n\n        # Attempt to match wildcard like /users/{user_id}\n        if wildcard:\n            for child in self.children:\n                if child.name.startswith('{') and child.name.endswith('}'):\n                    return child\n\n        return None\n\n    def ls(self, *path):\n        success = True\n        cur = self\n        for name in path:\n            if not name or name == '.':\n                continue\n            elif name == '..':\n                if cur.parent:\n                    cur = cur.parent\n            else:\n                child = cur.find_child(name)\n                if child:\n                    cur = child\n                else:\n                    success = False\n                    break\n        if success:\n            for node in sorted(cur.children):\n                yield node\n"
  },
  {
    "path": "http_prompt/utils.py",
    "content": "import math\nimport re\nimport shlex\n\nfrom prompt_toolkit.output.defaults import create_output\n\n\nRE_ANSI_ESCAPE = re.compile(r'\\x1b[^m]*m')\n\n\ndef smart_quote(s):\n    return shlex.quote(s)\n\n\ndef unquote(s):\n    quotes = [\"'\", '\"']\n    quote_str = None\n    if s[0] in quotes:\n        quote_str = s[0]\n\n    if quote_str and s[-1] == quote_str:\n        return s[1: -1]\n    return s\n\n\ndef unescape(s, exclude=None):\n    if exclude:\n        char = '[^%s]' % exclude\n    else:\n        char = '.'\n    return re.sub(r'\\\\(%s)' % char, r'\\1', s)\n\n\ndef get_terminal_size():\n    return create_output().get_size()\n\n\ndef strip_ansi_escapes(text):\n    return RE_ANSI_ESCAPE.sub('', text)\n\n\ndef colformat(strings, num_sep_spaces=1, terminal_width=None):\n    \"\"\"Format a list of strings like ls does multi-column output.\"\"\"\n    if terminal_width is None:\n        terminal_width = get_terminal_size().columns\n\n    if not strings:\n        return\n\n    num_items = len(strings)\n    max_len = max([len(strip_ansi_escapes(s)) for s in strings])\n\n    num_columns = min(\n        int((terminal_width + num_sep_spaces) / (max_len + num_sep_spaces)),\n        num_items)\n    num_columns = max(1, num_columns)\n\n    num_lines = int(math.ceil(float(num_items) / num_columns))\n    num_columns = int(math.ceil(float(num_items) / num_lines))\n\n    num_elements_last_column = num_items % num_lines\n    if num_elements_last_column == 0:\n        num_elements_last_column = num_lines\n\n    lines = []\n    for i in range(num_lines):\n        line_size = num_columns\n        if i >= num_elements_last_column:\n            line_size -= 1\n        lines.append([None] * line_size)\n\n    for i, line in enumerate(lines):\n        line_size = len(line)\n        for j in range(line_size):\n            k = i + num_lines * j\n            item = strings[k]\n            if j % line_size != line_size - 1:\n                item_len = len(strip_ansi_escapes(item))\n                item = item + ' ' * (max_len - item_len)\n            line[j] = item\n\n    sep = ' ' * num_sep_spaces\n    for line in lines:\n        yield sep.join(line)\n"
  },
  {
    "path": "http_prompt/xdg.py",
    "content": "\"\"\"XDG Base Directory Specification.\n\nSee:\n    https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html\n    https://github.com/ActiveState/appdirs\n\"\"\"\nimport os\nimport sys\n\nfrom functools import partial\n\n\ndef _get_dir(envvar_name, default_dir, resource_name=None):\n    base_dir = os.getenv(envvar_name) or default_dir\n    app_dir = os.path.join(base_dir, 'http-prompt')\n    if not os.path.exists(app_dir):\n        os.makedirs(app_dir, mode=0o700)\n\n    if resource_name:\n        app_dir = os.path.join(app_dir, resource_name)\n        if not os.path.exists(app_dir):\n            os.mkdir(app_dir)\n\n    return app_dir\n\n\nif sys.platform == 'win32':  # nocover\n    # NOTE: LOCALAPPDATA is not available on Windows XP\n    get_data_dir = partial(_get_dir, 'LOCALAPPDATA',\n                           os.path.expanduser('~/AppData/Local'))\n    get_config_dir = partial(_get_dir, 'LOCALAPPDATA',\n                             os.path.expanduser('~/AppData/Local'))\nelse:\n    get_data_dir = partial(_get_dir, 'XDG_DATA_HOME',\n                           os.path.expanduser('~/.local/share'))\n    get_config_dir = partial(_get_dir, 'XDG_CONFIG_HOME',\n                             os.path.expanduser('~/.config'))\n"
  },
  {
    "path": "requirements-test.txt",
    "content": "pexpect>=4.2.1\npytest>=3.0.6\npytest-cov>=2.4.0\nwheel\ntwine"
  },
  {
    "path": "requirements.txt",
    "content": "click>=5.0\nhttpie>=2.5.0\nparsimonious>=0.6.2\nprompt-toolkit>=2.0.0,<3.0.0\nPygments>=2.1.0\nPyYAML>=3.0\n"
  },
  {
    "path": "setup.cfg",
    "content": "[wheel]\nuniversal = 1\n"
  },
  {
    "path": "setup.py",
    "content": "import os\nimport re\nfrom setuptools import setup\n\n\nhere = os.path.abspath(os.path.dirname(__file__))\n\n\n# Read the version number from a source file.\n# Why read it, and not import?\n# see https://groups.google.com/d/topic/pypa-dev/0PkjVpcxTzQ/discussion\ndef find_version(*file_paths):\n    # Open in Latin-1 so that we avoid encoding errors.\n    with open(os.path.join(here, *file_paths), encoding='latin1') as f:\n        version_file = f.read()\n\n    # The version line must have the form\n    # __version__ = 'ver'\n    version_match = re.search(r\"^__version__ = ['\\\"]([^'\\\"]*)['\\\"]\",\n                              version_file, re.M)\n    if version_match:\n        return version_match.group(1)\n    raise RuntimeError('Unable to find version string')\n\n\ndef read_description(filename):\n    with open(filename, encoding='utf-8') as f:\n        return f.read()\n\n\ndef read_requirements(filename):\n    try:\n        with open(filename) as f:\n            return [line.rstrip() for line in f]\n    except OSError:\n        raise OSError(os.getcwd())\n\n\nsetup(\n    name='http-prompt',\n    version=find_version('http_prompt', '__init__.py'),\n    url='https://github.com/httpie/http-prompt',\n    description='An interactive HTTP command-line client',\n    long_description=read_description('README.rst'),\n    author='Chang-Hung Liang',\n    author_email='eliang.cs@gmail.com',\n    license='MIT',\n    packages=['http_prompt', 'http_prompt.context'],\n    entry_points=\"\"\"\n        [console_scripts]\n        http-prompt=http_prompt.cli:cli\n    \"\"\",\n    install_requires=read_requirements('requirements.txt'),\n    classifiers=[\n        'Development Status :: 3 - Alpha',\n        'Environment :: Console',\n        'Intended Audience :: Developers',\n        'Intended Audience :: System Administrators',\n        'License :: OSI Approved :: MIT License',\n        'Topic :: Internet :: WWW/HTTP',\n        'Topic :: Software Development',\n        'Topic :: System :: Networking',\n        'Topic :: Terminals',\n        'Topic :: Text Processing',\n        'Topic :: Utilities',\n        'Operating System :: OS Independent',\n        'Programming Language :: Python',\n        'Programming Language :: Python :: 3.6',\n        'Programming Language :: Python :: 3.7',\n        'Programming Language :: Python :: 3.8',\n        'Programming Language :: Python :: 3.9',\n        'Programming Language :: Python :: 3.10',\n    ]\n)\n"
  },
  {
    "path": "snap/snapcraft.yaml",
    "content": "name: http-prompt\nsummary: Interactive command-line HTTP client\ndescription: |\n    HTTP Prompt is an interactive command-line HTTP client featuring autocomplete\n    and syntax highlighting, built on HTTPie and prompt_toolkit.\n    Home: http://http-prompt.com\nadopt-info: http-prompt\nconfinement: strict\n\napps:\n    http-prompt:\n        command: bin/http-prompt\n        plugs: [network]\nparts:\n    http-prompt:\n        source: .\n        plugin: python\n        override-pull: |\n            snapcraftctl pull\n            version=\"$(git describe --always | sed -e 's/-/+git/;y/-/./')\"\n            case $version in\n                v*) version=$(echo $version | tail -c +2) ;;\n                *)  version=$(echo $version | head -c 32) ;;\n            esac\n            [ -n \"$(echo $version | grep \"+git\")\" ] && grade=devel || grade=stable\n            snapcraftctl set-version \"$version\"\n            snapcraftctl set-grade \"$grade\"\n"
  },
  {
    "path": "tests/__init__.py",
    "content": ""
  },
  {
    "path": "tests/base.py",
    "content": "import os\nimport shutil\nimport sys\nimport tempfile\nimport unittest\n\n\nclass TempAppDirTestCase(unittest.TestCase):\n    \"\"\"Set up temporary app data and config directories before every test\n    method, and delete them afterwards.\n    \"\"\"\n\n    def setUp(self):\n        # Create a temp dir that will contain data and config directories\n        self.temp_dir = tempfile.mkdtemp()\n\n        if sys.platform == 'win32':\n            self.homes = {\n                # subdir_name: envvar_name\n                'data': 'LOCALAPPDATA',\n                'config': 'LOCALAPPDATA'\n            }\n        else:\n            self.homes = {\n                # subdir_name: envvar_name\n                'data': 'XDG_DATA_HOME',\n                'config': 'XDG_CONFIG_HOME'\n            }\n\n        # Used to restore\n        self.orig_envvars = {}\n\n        for subdir_name, envvar_name in self.homes.items():\n            if envvar_name in os.environ:\n                self.orig_envvars[envvar_name] = os.environ[envvar_name]\n            os.environ[envvar_name] = os.path.join(self.temp_dir, subdir_name)\n\n    def tearDown(self):\n        # Restore envvar values\n        for name in self.homes.values():\n            if name in self.orig_envvars:\n                os.environ[name] = self.orig_envvars[name]\n            else:\n                del os.environ[name]\n\n        shutil.rmtree(self.temp_dir)\n\n    def make_tempfile(self, data='', subdir_name=''):\n        \"\"\"Create a file under self.temp_dir and return the path.\"\"\"\n        full_tempdir = os.path.join(self.temp_dir, subdir_name)\n        if not os.path.exists(full_tempdir):\n            os.makedirs(full_tempdir)\n\n        if isinstance(data, str):\n            data = data.encode()\n\n        with tempfile.NamedTemporaryFile(dir=full_tempdir, delete=False) as f:\n            f.write(data)\n            return f.name\n"
  },
  {
    "path": "tests/context/test_context.py",
    "content": "from http_prompt.context import Context\n\n\ndef test_creation():\n    context = Context('http://example.com')\n    assert context.url == 'http://example.com'\n    assert context.options == {}\n    assert context.headers == {}\n    assert context.querystring_params == {}\n    assert context.body_params == {}\n    assert not context.should_exit\n\n\ndef test_creation_with_longer_url():\n    context = Context('http://example.com/a/b/c/index.html')\n    assert context.url == 'http://example.com/a/b/c/index.html'\n    assert context.options == {}\n    assert context.headers == {}\n    assert context.querystring_params == {}\n    assert context.body_params == {}\n    assert not context.should_exit\n\n\ndef test_eq():\n    c1 = Context('http://localhost')\n    c2 = Context('http://localhost')\n    assert c1 == c2\n\n    c1.options['--verify'] = 'no'\n    assert c1 != c2\n\n\ndef test_copy():\n    c1 = Context('http://localhost')\n    c2 = c1.copy()\n    assert c1 == c2\n    assert c1 is not c2\n\n\ndef test_update():\n    c1 = Context('http://localhost')\n    c1.headers['Accept'] = 'application/json'\n    c1.querystring_params['flag'] = '1'\n    c1.body_params.update({\n        'name': 'John Doe',\n        'email': 'john@example.com'\n    })\n\n    c2 = Context('http://example.com')\n    c2.headers['Content-Type'] = 'text/html'\n    c2.body_params['name'] = 'John Smith'\n\n    c1.update(c2)\n\n    assert c1.url == 'http://example.com'\n    assert c1.headers == {\n        'Accept': 'application/json',\n        'Content-Type': 'text/html'\n    }\n    assert c1.querystring_params == {'flag': '1'}\n    assert c1.body_params == {\n        'name': 'John Smith',\n        'email': 'john@example.com'\n    }\n\n\ndef test_spec():\n    c = Context('http://localhost', spec={\n        'paths': {\n            '/users': {\n                'get': {\n                    'parameters': [\n                        {'name': 'username', 'in': 'path'},\n                        {'name': 'since', 'in': 'query'},\n                        {'name': 'Accept'}\n                    ]\n                }\n            },\n            '/orgs/{org}': {\n                'get': {\n                    'parameters': [\n                        {'name': 'org', 'in': 'path'},\n                        {'name': 'featured', 'in': 'query'},\n                        {'name': 'X-Foo', 'in': 'header'}\n                    ]\n                }\n            }\n        }\n    })\n    assert c.url == 'http://localhost'\n\n    root_children = list(sorted(c.root.children))\n    assert len(root_children) == 2\n    assert root_children[0].name == 'orgs'\n    assert root_children[1].name == 'users'\n\n    orgs_children = list(sorted(root_children[0].children))\n    assert len(orgs_children) == 1\n\n    org_children = list(sorted(list(orgs_children)[0].children))\n    assert len(org_children) == 2\n    assert org_children[0].name == 'X-Foo'\n    assert org_children[1].name == 'featured'\n\n    users_children = list(sorted(root_children[1].children))\n    assert len(users_children) == 2\n    assert users_children[0].name == 'Accept'\n    assert users_children[1].name == 'since'\n\n\ndef test_override():\n    \"\"\"Parameters can be defined at path level\n    \"\"\"\n    c = Context('http://localhost', spec={\n        'paths': {\n            '/users': {\n                'parameters': [\n                    {'name': 'username', 'in': 'query'},\n                    {'name': 'Accept', 'in': 'header'}\n                ],\n                'get': {\n                    'parameters': [\n                        {'name': 'custom1', 'in': 'query'}\n                    ]\n                },\n                'post': {\n                    'parameters': [\n                        {'name': 'custom2', 'in': 'query'},\n                    ]\n                },\n            },\n            '/orgs': {\n                'parameters': [\n                    {'name': 'username', 'in': 'query'},\n                    {'name': 'Accept', 'in': 'header'}\n                ],\n                'get': {}\n            }\n        }\n    })\n    assert c.url == 'http://localhost'\n\n    root_children = list(sorted(c.root.children))\n    # one path\n    assert len(root_children) == 2\n    assert root_children[0].name == 'orgs'\n    assert root_children[1].name == 'users'\n\n    orgs_methods = list(sorted(list(root_children)[0].children))\n    # path parameters are used even if no method parameter\n    assert len(orgs_methods) == 2\n    assert next(filter(lambda i:i.name == 'username', orgs_methods), None) is not None\n    assert next(filter(lambda i:i.name == 'Accept', orgs_methods), None) is not None\n\n    users_methods = list(sorted(list(root_children)[1].children))\n    # path and methods parameters are merged\n    assert len(users_methods) == 4\n    assert next(filter(lambda i:i.name == 'username', users_methods), None) is not None\n    assert next(filter(lambda i:i.name == 'custom1', users_methods), None) is not None\n    assert next(filter(lambda i:i.name == 'custom2', users_methods), None) is not None\n    assert next(filter(lambda i:i.name == 'Accept', users_methods), None) is not None\n"
  },
  {
    "path": "tests/context/test_transform.py",
    "content": "from http_prompt.context import Context\nfrom http_prompt.context import transform as t\n\n\ndef test_extract_args_for_httpie_main_get():\n    c = Context('http://localhost/things')\n    c.headers.update({\n        'Authorization': 'ApiKey 1234',\n        'Accept': 'text/html'\n    })\n    c.querystring_params.update({\n        'page': '2',\n        'limit': '10'\n    })\n\n    args = t.extract_args_for_httpie_main(c, method='get')\n    assert args == ['GET', 'http://localhost/things', 'limit==10', 'page==2',\n                    'Accept:text/html', 'Authorization:ApiKey 1234']\n\n\ndef test_extract_args_for_httpie_main_post():\n    c = Context('http://localhost/things')\n    c.headers.update({\n        'Authorization': 'ApiKey 1234',\n        'Accept': 'text/html'\n    })\n    c.options.update({\n        '--verify': 'no',\n        '--form': None\n    })\n    c.body_params.update({\n        'full name': 'Jane Doe',\n        'email': 'jane@example.com'\n    })\n\n    args = t.extract_args_for_httpie_main(c, method='post')\n    assert args == ['--form', '--verify', 'no',\n                    'POST', 'http://localhost/things',\n                    'email=jane@example.com', 'full name=Jane Doe',\n                    'Accept:text/html', 'Authorization:ApiKey 1234']\n\n\ndef test_extract_raw_json_args_for_httpie_main_post():\n    c = Context('http://localhost/things')\n    c.body_json_params.update({\n        'enabled': True,\n        'items': ['foo', 'bar'],\n        'object': {\n            'id': 10,\n            'name': 'test'\n        }\n    })\n\n    args = t.extract_args_for_httpie_main(c, method='post')\n    assert args == ['POST', 'http://localhost/things',\n                    'enabled:=true', 'items:=[\"foo\", \"bar\"]',\n                    'object:={\"id\": 10, \"name\": \"test\"}']\n\n\ndef test_format_to_httpie_get():\n    c = Context('http://localhost/things')\n    c.headers.update({\n        'Authorization': 'ApiKey 1234',\n        'Accept': 'text/html'\n    })\n    c.querystring_params.update({\n        'page': '2',\n        'limit': '10',\n        'name': ['alice', 'bob bob']\n    })\n\n    output = t.format_to_httpie(c, method='get')\n    assert output == (\"http GET http://localhost/things \"\n                      \"limit==10 name==alice 'name==bob bob' page==2 \"\n                      \"Accept:text/html 'Authorization:ApiKey 1234'\\n\")\n\n\ndef test_format_to_httpie_post():\n    c = Context('http://localhost/things')\n    c.headers.update({\n        'Authorization': 'ApiKey 1234',\n        'Accept': 'text/html'\n    })\n    c.options.update({\n        '--verify': 'no',\n        '--form': None\n    })\n    c.body_params.update({\n        'full name': 'Jane Doe',\n        'email': 'jane@example.com'\n    })\n\n    output = t.format_to_httpie(c, method='post')\n    assert output == (\"http --form --verify=no POST http://localhost/things \"\n                      \"email=jane@example.com 'full name=Jane Doe' \"\n                      \"Accept:text/html 'Authorization:ApiKey 1234'\\n\")\n\n\ndef test_format_to_http_prompt_1():\n    c = Context('http://localhost/things')\n    c.headers.update({\n        'Authorization': 'ApiKey 1234',\n        'Accept': 'text/html'\n    })\n    c.querystring_params.update({\n        'page': '2',\n        'limit': '10'\n    })\n\n    output = t.format_to_http_prompt(c)\n    assert output == (\"cd http://localhost/things\\n\"\n                      \"limit==10\\n\"\n                      \"page==2\\n\"\n                      \"Accept:text/html\\n\"\n                      \"'Authorization:ApiKey 1234'\\n\")\n\n\ndef test_format_to_http_prompt_2():\n    c = Context('http://localhost/things')\n    c.headers.update({\n        'Authorization': 'ApiKey 1234',\n        'Accept': 'text/html'\n    })\n    c.options.update({\n        '--verify': 'no',\n        '--form': None\n    })\n    c.body_params.update({\n        'full name': 'Jane Doe',\n        'email': 'jane@example.com'\n    })\n\n    output = t.format_to_http_prompt(c)\n    assert output == (\"--form\\n\"\n                      \"--verify=no\\n\"\n                      \"cd http://localhost/things\\n\"\n                      \"email=jane@example.com\\n\"\n                      \"'full name=Jane Doe'\\n\"\n                      \"Accept:text/html\\n\"\n                      \"'Authorization:ApiKey 1234'\\n\")\n\n\ndef test_format_raw_json_string_to_http_prompt():\n    c = Context('http://localhost/things')\n    c.body_json_params.update({\n        'bar': 'baz',\n    })\n\n    output = t.format_to_http_prompt(c)\n    assert output == (\"cd http://localhost/things\\n\"\n                      \"bar:='\\\"baz\\\"'\\n\")\n\n\ndef test_extract_httpie_options():\n    c = Context('http://localhost')\n    c.options.update({\n        '--verify': 'no',\n        '--form': None\n    })\n\n    output = t._extract_httpie_options(c, excluded_keys=['--form'])\n    assert output == ['--verify', 'no']\n"
  },
  {
    "path": "tests/test_cli.py",
    "content": "import json\nimport os\nimport sys\nimport unittest\nfrom unittest.mock import patch, DEFAULT\n\nfrom click.testing import CliRunner\nfrom requests.models import Response\n\nfrom .base import TempAppDirTestCase\nfrom http_prompt import xdg\nfrom http_prompt.context import Context\nfrom http_prompt.cli import cli, execute, ExecutionListener\n\n\ndef run_and_exit(cli_args=None, prompt_commands=None):\n    \"\"\"Run http-prompt executable, execute some prompt commands, and exit.\"\"\"\n    if cli_args is None:\n        cli_args = []\n\n        # Make sure last command is 'exit'\n    if prompt_commands is None:\n        prompt_commands = ['exit']\n    else:\n        prompt_commands += ['exit']\n\n    # Fool cli() so that it believes we're running from CLI instead of pytest.\n    # We will restore it at the end of the function.\n    orig_argv = sys.argv\n    sys.argv = ['http-prompt'] + cli_args\n\n    try:\n        with patch.multiple('http_prompt.cli',\n                            prompt=DEFAULT, execute=DEFAULT) as mocks:\n            mocks['execute'].side_effect = execute\n\n            # prompt() is mocked to return the command in 'prompt_commands' in\n            # sequence, i.e., prompt() returns prompt_commands[i-1] when it is\n            # called for the ith time\n            mocks['prompt'].side_effect = prompt_commands\n\n            result = CliRunner().invoke(cli, cli_args)\n            context = mocks['execute'].call_args[0][1]\n\n        return result, context\n    finally:\n        sys.argv = orig_argv\n\n\nclass TestCli(TempAppDirTestCase):\n\n    def test_without_args(self):\n        result, context = run_and_exit(['http://localhost'])\n        self.assertEqual(result.exit_code, 0)\n        self.assertEqual(context.url, 'http://localhost')\n        self.assertEqual(context.options, {})\n        self.assertEqual(context.body_params, {})\n        self.assertEqual(context.headers, {})\n        self.assertEqual(context.querystring_params, {})\n\n    def test_incomplete_url1(self):\n        result, context = run_and_exit(['://example.com'])\n        self.assertEqual(result.exit_code, 0)\n        self.assertEqual(context.url, 'http://example.com')\n        self.assertEqual(context.options, {})\n        self.assertEqual(context.body_params, {})\n        self.assertEqual(context.headers, {})\n        self.assertEqual(context.querystring_params, {})\n\n    def test_incomplete_url2(self):\n        result, context = run_and_exit(['//example.com'])\n        self.assertEqual(result.exit_code, 0)\n        self.assertEqual(context.url, 'http://example.com')\n        self.assertEqual(context.options, {})\n        self.assertEqual(context.body_params, {})\n        self.assertEqual(context.headers, {})\n        self.assertEqual(context.querystring_params, {})\n\n    def test_incomplete_url3(self):\n        result, context = run_and_exit(['example.com'])\n        self.assertEqual(result.exit_code, 0)\n        self.assertEqual(context.url, 'http://example.com')\n        self.assertEqual(context.options, {})\n        self.assertEqual(context.body_params, {})\n        self.assertEqual(context.headers, {})\n        self.assertEqual(context.querystring_params, {})\n\n    def test_httpie_oprions(self):\n        url = 'http://example.com'\n        custom_args = '--auth value: name=foo'\n        result, context = run_and_exit([url] + custom_args.split())\n        self.assertEqual(result.exit_code, 0)\n        self.assertEqual(context.url, 'http://example.com')\n        self.assertEqual(context.options, {'--auth': 'value:'})\n        self.assertEqual(context.body_params, {'name': 'foo'})\n        self.assertEqual(context.headers, {})\n        self.assertEqual(context.querystring_params, {})\n\n    def test_persistent_context(self):\n        result, context = run_and_exit(['//example.com', 'name=bob', 'id==10'])\n        self.assertEqual(result.exit_code, 0)\n        self.assertEqual(context.url, 'http://example.com')\n        self.assertEqual(context.options, {})\n        self.assertEqual(context.body_params, {'name': 'bob'})\n        self.assertEqual(context.headers, {})\n        self.assertEqual(context.querystring_params, {'id': ['10']})\n\n        result, context = run_and_exit()\n        self.assertEqual(result.exit_code, 0)\n        self.assertEqual(context.url, 'http://example.com')\n        self.assertEqual(context.options, {})\n        self.assertEqual(context.body_params, {'name': 'bob'})\n        self.assertEqual(context.headers, {})\n        self.assertEqual(context.querystring_params, {'id': ['10']})\n\n    def test_cli_args_bypasses_persistent_context(self):\n        result, context = run_and_exit(['//example.com', 'name=bob', 'id==10'])\n        self.assertEqual(result.exit_code, 0)\n        self.assertEqual(context.url, 'http://example.com')\n        self.assertEqual(context.options, {})\n        self.assertEqual(context.body_params, {'name': 'bob'})\n        self.assertEqual(context.headers, {})\n        self.assertEqual(context.querystring_params, {'id': ['10']})\n\n        result, context = run_and_exit(['//example.com', 'sex=M'])\n        self.assertEqual(result.exit_code, 0)\n        self.assertEqual(context.url, 'http://example.com')\n        self.assertEqual(context.options, {})\n        self.assertEqual(context.body_params, {'sex': 'M'})\n        self.assertEqual(context.headers, {})\n\n    def test_config_file(self):\n        # Config file is not there at the beginning\n        config_path = os.path.join(xdg.get_config_dir(), 'config.py')\n        self.assertFalse(os.path.exists(config_path))\n\n        # After user runs it for the first time, a default config file should\n        # be created\n        result, context = run_and_exit(['//example.com'])\n        self.assertEqual(result.exit_code, 0)\n        self.assertTrue(os.path.exists(config_path))\n\n    def test_cli_arguments_with_spaces(self):\n        result, context = run_and_exit(['example.com', \"name=John Doe\",\n                                        \"Authorization:Bearer API KEY\"])\n        self.assertEqual(result.exit_code, 0)\n        self.assertEqual(context.url, 'http://example.com')\n        self.assertEqual(context.options, {})\n        self.assertEqual(context.querystring_params, {})\n        self.assertEqual(context.body_params, {'name': 'John Doe'})\n        self.assertEqual(context.headers, {'Authorization': 'Bearer API KEY'})\n\n    def test_spec_from_local(self):\n        spec_filepath = self.make_tempfile(json.dumps({\n            'paths': {\n                '/users': {},\n                '/orgs': {}\n            }\n        }))\n        result, context = run_and_exit(['example.com', \"--spec\",\n                                        spec_filepath])\n        self.assertEqual(result.exit_code, 0)\n        self.assertEqual(context.url, 'http://example.com')\n        self.assertEqual(set([n.name for n in context.root.children]),\n                         set(['users', 'orgs']))\n\n    def test_spec_basePath(self):\n        spec_filepath = self.make_tempfile(json.dumps({\n            'basePath': '/api/v1',\n            'paths': {\n                '/users': {},\n                '/orgs': {}\n            }\n        }))\n        result, context = run_and_exit(['example.com', \"--spec\",\n                                        spec_filepath])\n        self.assertEqual(result.exit_code, 0)\n        self.assertEqual(context.url, 'http://example.com')\n\n        lv1_names = set([node.name for node in context.root.ls()])\n        lv2_names = set([node.name for node in context.root.ls('api')])\n        lv3_names = set([node.name for node in context.root.ls('api', 'v1')])\n\n        self.assertEqual(lv1_names, set(['api']))\n        self.assertEqual(lv2_names, set(['v1']))\n        self.assertEqual(lv3_names, set(['users', 'orgs']))\n\n    def test_spec_from_http(self):\n        spec_url = 'https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/api.github.com/api.github.com.json'\n        result, context = run_and_exit(['https://api.github.com', '--spec',\n                                        spec_url])\n        self.assertEqual(result.exit_code, 0)\n        self.assertEqual(context.url, 'https://api.github.com')\n\n        top_level_paths = set([n.name for n in context.root.children])\n        self.assertIn('repos', top_level_paths)\n        self.assertIn('users', top_level_paths)\n\n    def test_spec_from_http_only(self):\n        spec_url = (\n            'https://api.apis.guru/v2/specs/medium.com/1.0.0/swagger.json')\n        result, context = run_and_exit(['--spec', spec_url])\n        self.assertEqual(result.exit_code, 0)\n        self.assertEqual(context.url, 'https://api.medium.com/v1')\n\n        lv1_names = set([node.name for node in context.root.ls()])\n        lv2_names = set([node.name for node in context.root.ls('v1')])\n\n        self.assertEqual(lv1_names, set(['v1']))\n        self.assertEqual(lv2_names, set(['me', 'publications', 'users']))\n\n    def test_spec_with_trailing_slash(self):\n        spec_filepath = self.make_tempfile(json.dumps({\n            'basePath': '/api',\n            'paths': {\n                '/': {},\n                '/users/': {}\n            }\n        }))\n        result, context = run_and_exit(['example.com', \"--spec\",\n                                        spec_filepath])\n        self.assertEqual(result.exit_code, 0)\n        self.assertEqual(context.url, 'http://example.com')\n        lv1_names = set([node.name for node in context.root.ls()])\n        lv2_names = set([node.name for node in context.root.ls('api')])\n        self.assertEqual(lv1_names, set(['api']))\n        self.assertEqual(lv2_names, set(['/', 'users/']))\n\n    def test_env_only(self):\n        env_filepath = self.make_tempfile(\n            \"cd http://example.com\\nname=bob\\nid==10\")\n        result, context = run_and_exit([\"--env\", env_filepath])\n        self.assertEqual(result.exit_code, 0)\n        self.assertEqual(context.url, 'http://example.com')\n        self.assertEqual(context.options, {})\n        self.assertEqual(context.body_params, {'name': 'bob'})\n        self.assertEqual(context.headers, {})\n        self.assertEqual(context.querystring_params, {'id': ['10']})\n\n    def test_env_with_url(self):\n        env_filepath = self.make_tempfile(\n            \"cd http://example.com\\nname=bob\\nid==10\")\n        result, context = run_and_exit([\"--env\", env_filepath,\n                                        'other_example.com'])\n        self.assertEqual(result.exit_code, 0)\n        self.assertEqual(context.url, 'http://other_example.com')\n        self.assertEqual(context.options, {})\n        self.assertEqual(context.body_params, {'name': 'bob'})\n        self.assertEqual(context.headers, {})\n        self.assertEqual(context.querystring_params, {'id': ['10']})\n\n    def test_env_with_options(self):\n        env_filepath = self.make_tempfile(\n            \"cd http://example.com\\nname=bob\\nid==10\")\n        result, context = run_and_exit([\"--env\", env_filepath,\n                                        'other_example.com', 'name=alice'])\n        self.assertEqual(result.exit_code, 0)\n        self.assertEqual(context.url, 'http://other_example.com')\n        self.assertEqual(context.options, {})\n        self.assertEqual(context.body_params, {'name': 'alice'})\n        self.assertEqual(context.headers, {})\n        self.assertEqual(context.querystring_params, {'id': ['10']})\n\n    @patch('http_prompt.cli.prompt')\n    @patch('http_prompt.cli.execute')\n    def test_press_ctrl_d(self, execute_mock, prompt_mock):\n        prompt_mock.side_effect = EOFError\n        execute_mock.side_effect = execute\n        result = CliRunner().invoke(cli, [])\n        self.assertEqual(result.exit_code, 0)\n\n\nclass TestExecutionListenerSetCookies(unittest.TestCase):\n\n    def setUp(self):\n        self.listener = ExecutionListener({})\n\n        self.response = Response()\n        self.response.cookies.update({\n            'username': 'john',\n            'sessionid': 'abcd'\n        })\n\n        self.context = Context('http://localhost')\n        self.context.headers['Cookie'] = 'name=\"John Doe\"; sessionid=xyz'\n\n    def test_auto(self):\n        self.listener.cfg['set_cookies'] = 'auto'\n        self.listener.response_returned(self.context, self.response)\n\n        self.assertEqual(self.context.headers['Cookie'],\n                         'name=\"John Doe\"; sessionid=abcd; username=john')\n\n    @patch('http_prompt.cli.click.confirm')\n    def test_ask_and_yes(self, confirm_mock):\n        confirm_mock.return_value = True\n\n        self.listener.cfg['set_cookies'] = 'ask'\n        self.listener.response_returned(self.context, self.response)\n\n        self.assertEqual(self.context.headers['Cookie'],\n                         'name=\"John Doe\"; sessionid=abcd; username=john')\n\n    @patch('http_prompt.cli.click.confirm')\n    def test_ask_and_no(self, confirm_mock):\n        confirm_mock.return_value = False\n\n        self.listener.cfg['set_cookies'] = 'ask'\n        self.listener.response_returned(self.context, self.response)\n\n        self.assertEqual(self.context.headers['Cookie'],\n                         'name=\"John Doe\"; sessionid=xyz')\n\n    def test_off(self):\n        self.listener.cfg['set_cookies'] = 'off'\n        self.listener.response_returned(self.context, self.response)\n\n        self.assertEqual(self.context.headers['Cookie'],\n                         'name=\"John Doe\"; sessionid=xyz')\n"
  },
  {
    "path": "tests/test_completer.py",
    "content": "# -*- coding: utf-8 -*-\nimport unittest\n\nfrom prompt_toolkit.document import Document\n\nfrom http_prompt.completer import HttpPromptCompleter\nfrom http_prompt.context import Context\n\n\nclass TestCompleter(unittest.TestCase):\n\n    def setUp(self):\n        self.context = Context('http://localhost', spec={\n            'paths': {\n                '/users': {},\n                '/users/{username}': {},\n                '/users/{username}/events': {},\n                '/users/{username}/orgs': {},\n                '/orgs': {},\n                '/orgs/{org}': {},\n                '/orgs/{org}/events': {},\n                '/orgs/{org}/members': {}\n            }\n        })\n        self.completer = HttpPromptCompleter(self.context)\n        self.completer_event = None\n\n    def get_completions(self, command):\n        if not isinstance(command, str):\n            command = command.decode()\n        position = len(command)\n        completions = self.completer.get_completions(\n            Document(text=command, cursor_position=position),\n            self.completer_event)\n        return [c.text for c in completions]\n\n    def test_header_name(self):\n        result = self.get_completions('ctype')\n        self.assertEqual(result[0], 'Content-Type')\n\n    def test_header_value(self):\n        result = self.get_completions('Content-Type:json')\n        self.assertEqual(result[0], 'application/json')\n\n    def test_verify_option(self):\n        result = self.get_completions('--vfy')\n        self.assertEqual(result[0], '--verify')\n\n    def test_preview_then_action(self):\n        result = self.get_completions('httpie po')\n        self.assertEqual(result[0], 'post')\n\n    def test_rm_body_param(self):\n        self.context.body_params['my_name'] = 'dont_care'\n        result = self.get_completions('rm -b ')\n        self.assertEqual(result[0], 'my_name')\n\n    def test_rm_body_json_param(self):\n        self.context.body_json_params['number'] = 2\n        result = self.get_completions('rm -b ')\n        self.assertEqual(result[0], 'number')\n\n    def test_rm_querystring_param(self):\n        self.context.querystring_params['my_name'] = 'dont_care'\n        result = self.get_completions('rm -q ')\n        self.assertEqual(result[0], 'my_name')\n\n    def test_rm_header(self):\n        self.context.headers['Accept'] = 'dont_care'\n        result = self.get_completions('rm -h ')\n        self.assertEqual(result[0], 'Accept')\n\n    def test_rm_option(self):\n        self.context.options['--form'] = None\n        result = self.get_completions('rm -o ')\n        self.assertEqual(result[0], '--form')\n\n    def test_querystring_with_chinese(self):\n        result = self.get_completions('name==王')\n        self.assertFalse(result)\n\n    def test_header_with_spanish(self):\n        result = self.get_completions('X-Custom-Header:Jesú')\n        self.assertFalse(result)\n\n    def test_options_method(self):\n        result = self.get_completions('opt')\n        self.assertEqual(result[0], 'options')\n\n    def test_ls_no_path(self):\n        result = self.get_completions('ls ')\n        self.assertEqual(result, ['orgs', 'users'])\n\n    def test_ls_no_path_substring(self):\n        result = self.get_completions('ls o')\n        self.assertEqual(result, ['orgs'])\n\n    def test_ls_absolute_path(self):\n        result = self.get_completions('ls /users/1/')\n        self.assertEqual(result, ['events', 'orgs'])\n\n    def test_ls_absolute_path_substring(self):\n        result = self.get_completions('ls /users/1/e')\n        self.assertEqual(result, ['events'])\n\n    def test_ls_relative_path(self):\n        self.context.url = 'http://localhost/orgs'\n        result = self.get_completions('ls 1/')\n        self.assertEqual(result, ['events', 'members'])\n\n    def test_cd_no_path(self):\n        result = self.get_completions('cd ')\n        self.assertEqual(result, ['orgs', 'users'])\n\n    def test_cd_no_path_substring(self):\n        result = self.get_completions('cd o')\n        self.assertEqual(result, ['orgs'])\n\n    def test_cd_absolute_path(self):\n        result = self.get_completions('cd /users/1/')\n        self.assertEqual(result, ['events', 'orgs'])\n\n    def test_cd_absolute_path_substring(self):\n        result = self.get_completions('cd /users/1/e')\n        self.assertEqual(result, ['events'])\n\n    def test_cd_relative_path(self):\n        self.context.url = 'http://localhost/orgs'\n        result = self.get_completions('cd 1/')\n        self.assertEqual(result, ['events', 'members'])\n"
  },
  {
    "path": "tests/test_config.py",
    "content": "import hashlib\nimport os\n\nfrom .base import TempAppDirTestCase\nfrom http_prompt import config\n\n\ndef _hash_file(path):\n    with open(path, 'rb') as f:\n        data = f.read()\n    return hashlib.sha1(data).hexdigest()\n\n\nclass TestConfig(TempAppDirTestCase):\n\n    def test_initialize(self):\n        # Config file doesn't exist at first\n        expected_path = config.get_user_config_path()\n        self.assertFalse(os.path.exists(expected_path))\n\n        # Config file should exist after initialization\n        copied, actual_path = config.initialize()\n        self.assertTrue(copied)\n        self.assertEqual(actual_path, expected_path)\n        self.assertTrue(os.path.exists(expected_path))\n\n        # Change config file and hash the content to see if it's changed\n        with open(expected_path, 'a') as f:\n            f.write('dont_care\\n')\n        orig_hash = _hash_file(expected_path)\n\n        # Make sure it's fine to call config.initialize() twice\n        copied, actual_path = config.initialize()\n        self.assertFalse(copied)\n        self.assertEqual(actual_path, expected_path)\n        self.assertTrue(os.path.exists(expected_path))\n\n        # Make sure config file is unchanged\n        new_hash = _hash_file(expected_path)\n        self.assertEqual(new_hash, orig_hash)\n\n    def test_load_default(self):\n        cfg = config.load_default()\n        self.assertEqual(cfg['command_style'], 'solarized')\n        self.assertFalse(cfg['output_style'])\n        self.assertEqual(cfg['pager'], 'less')\n\n    def test_load_user(self):\n        copied, path = config.initialize()\n        self.assertTrue(copied)\n\n        with open(path, 'w') as f:\n            f.write(\"\\ngreeting = 'hello!'\\n\")\n\n        cfg = config.load_user()\n        self.assertEqual(cfg, {'greeting': 'hello!'})\n\n    def test_load(self):\n        copied, path = config.initialize()\n        self.assertTrue(copied)\n\n        with open(path, 'w') as f:\n            f.write(\"pager = 'more'\\n\"\n                    \"greeting = 'hello!'\\n\")\n\n        cfg = config.load()\n        self.assertEqual(cfg['command_style'], 'solarized')\n        self.assertFalse(cfg['output_style'])\n        self.assertEqual(cfg['pager'], 'more')\n        self.assertEqual(cfg['greeting'], 'hello!')\n"
  },
  {
    "path": "tests/test_contextio.py",
    "content": "# -*- coding: utf-8 -*-\nfrom .base import TempAppDirTestCase\nfrom http_prompt.context import Context\nfrom http_prompt.contextio import save_context, load_context\n\n\nclass TestContextIO(TempAppDirTestCase):\n\n    def test_save_and_load_context_non_ascii(self):\n        c = Context('http://localhost')\n        c.headers.update({\n            'User-Agent': 'Ö',\n            'Authorization': '中文'\n        })\n        save_context(c)\n\n        c = Context('http://0.0.0.0')\n        load_context(c)\n\n        self.assertEqual(c.url, 'http://localhost')\n        self.assertEqual(c.headers, {\n            'User-Agent': 'Ö',\n            'Authorization': '中文'\n        })\n"
  },
  {
    "path": "tests/test_execution.py",
    "content": "# -*- coding: utf-8 -*-\nimport hashlib\nimport io\nimport json\nimport shutil\nimport os\nimport sys\n\nimport pytest\n\nfrom collections import namedtuple\n\nfrom unittest.mock import patch\n\nfrom http_prompt.context import Context\nfrom http_prompt.execution import execute, HTTPIE_PROGRAM_NAME\n\nfrom .base import TempAppDirTestCase\n\n\nclass ExecutionTestCase(TempAppDirTestCase):\n\n    def setUp(self):\n        super(ExecutionTestCase, self).setUp()\n        self.patchers = [\n            ('httpie_main', patch('http_prompt.execution.httpie_main')),\n            ('echo_via_pager',\n             patch('http_prompt.output.click.echo_via_pager')),\n            ('secho', patch('http_prompt.execution.click.secho')),\n            ('get_terminal_size', patch('http_prompt.utils.get_terminal_size'))\n        ]\n        for attr_name, patcher in self.patchers:\n            setattr(self, attr_name, patcher.start())\n\n        self.context = Context('http://localhost', spec={\n            'paths': {\n                '/users': {},\n                '/users/{username}': {},\n                '/users/{username}/events': {},\n                '/users/{username}/orgs': {},\n                '/orgs': {},\n                '/orgs/{org}': {},\n                '/orgs/{org}/events': {},\n                '/orgs/{org}/members': {}\n            }\n        })\n\n        # pytest mocks to capture stdout so we can't really get_terminal_size()\n        Size = namedtuple('Size', ['columns', 'rows'])\n        self.get_terminal_size.return_value = Size(80, 30)\n\n    def tearDown(self):\n        super(ExecutionTestCase, self).tearDown()\n        for _, patcher in self.patchers:\n            patcher.stop()\n\n    def assert_httpie_main_called_with(self, args):\n        self.assertEqual(self.httpie_main.call_args[0][0], [\n                         HTTPIE_PROGRAM_NAME, *args])\n\n    def assert_stdout(self, expected_msg):\n        # Append '\\n' to simulate behavior of click.echo_via_pager(),\n        # which we use whenever we want to output anything to stdout\n        printed_msg = self.echo_via_pager.call_args[0][0] + '\\n'\n        self.assertEqual(printed_msg, expected_msg)\n\n    def assert_stdout_startswith(self, expected_prefix):\n        printed_msg = self.echo_via_pager.call_args[0][0]\n        self.assertTrue(printed_msg.startswith(expected_prefix))\n\n    def get_stdout(self):\n        return self.echo_via_pager.call_args[0][0]\n\n    def assert_stderr(self, expected_msg):\n        printed_msg = self.secho.call_args[0][0]\n        print_options = self.secho.call_args[1]\n        self.assertEqual(printed_msg, expected_msg)\n        self.assertEqual(print_options, {'err': True, 'fg': 'red'})\n\n\nclass TestExecution_noop(ExecutionTestCase):\n\n    def test_empty_string(self):\n        execute('', self.context)\n        self.assertEqual(self.context.url, 'http://localhost')\n        self.assertFalse(self.context.options)\n        self.assertFalse(self.context.headers)\n        self.assertFalse(self.context.querystring_params)\n        self.assertFalse(self.context.body_params)\n        self.assertFalse(self.context.should_exit)\n\n    def test_spaces(self):\n        execute('  \\t \\t  ', self.context)\n        self.assertEqual(self.context.url, 'http://localhost')\n        self.assertFalse(self.context.options)\n        self.assertFalse(self.context.headers)\n        self.assertFalse(self.context.querystring_params)\n        self.assertFalse(self.context.body_params)\n        self.assertFalse(self.context.should_exit)\n\n\nclass TestExecution_env(ExecutionTestCase):\n\n    def setUp(self):\n        super(TestExecution_env, self).setUp()\n\n        self.context.url = 'http://localhost:8000/api'\n        self.context.headers.update({\n            'Accept': 'text/csv',\n            'Authorization': 'ApiKey 1234'\n        })\n        self.context.querystring_params.update({\n            'page': ['1'],\n            'limit': ['50']\n        })\n        self.context.body_params.update({\n            'name': 'John Doe'\n        })\n        self.context.options.update({\n            '--verify': 'no',\n            '--form': None\n        })\n\n    def test_env(self):\n        execute('env', self.context)\n        self.assert_stdout(\"--form\\n--verify=no\\n\"\n                           \"cd http://localhost:8000/api\\n\"\n                           \"limit==50\\npage==1\\n\"\n                           \"'name=John Doe'\\n\"\n                           \"Accept:text/csv\\n\"\n                           \"'Authorization:ApiKey 1234'\\n\")\n\n    def test_env_with_spaces(self):\n        execute('  env   ', self.context)\n        self.assert_stdout(\"--form\\n--verify=no\\n\"\n                           \"cd http://localhost:8000/api\\n\"\n                           \"limit==50\\npage==1\\n\"\n                           \"'name=John Doe'\\n\"\n                           \"Accept:text/csv\\n\"\n                           \"'Authorization:ApiKey 1234'\\n\")\n\n    def test_env_non_ascii(self):\n        self.context.body_params['name'] = '許 功蓋'\n        execute('env', self.context)\n        self.assert_stdout(\"--form\\n--verify=no\\n\"\n                           \"cd http://localhost:8000/api\\n\"\n                           \"limit==50\\npage==1\\n\"\n                           \"'name=許 功蓋'\\n\"\n                           \"Accept:text/csv\\n\"\n                           \"'Authorization:ApiKey 1234'\\n\")\n\n    def test_env_write_to_file(self):\n        filename = self.make_tempfile()\n\n        # write something first to make sure it's a full overwrite\n        with open(filename, 'w') as f:\n            f.write('hello world\\n')\n\n        execute('env > %s' % filename, self.context)\n\n        with open(filename) as f:\n            content = f.read()\n\n        self.assertEqual(content,\n                         \"--form\\n--verify=no\\n\"\n                         \"cd http://localhost:8000/api\\n\"\n                         \"limit==50\\npage==1\\n\"\n                         \"'name=John Doe'\\n\"\n                         \"Accept:text/csv\\n\"\n                         \"'Authorization:ApiKey 1234'\\n\")\n\n    def test_env_write_to_file_with_env_vars(self):\n        filename = self.make_tempfile('hello world\\n', 'testenvvar')\n        filename_with_var = filename.replace(\"testenvvar\", \"${MYPRIVATEVAR}\")\n\n        os.environ['MYPRIVATEVAR'] = 'testenvvar'\n        execute('env > %s' % filename_with_var, self.context)\n        os.environ['MYPRIVATEVAR'] = ''\n\n        with open(filename) as f:\n            content = f.read()\n\n        self.assertEqual(content,\n                         \"--form\\n--verify=no\\n\"\n                         \"cd http://localhost:8000/api\\n\"\n                         \"limit==50\\npage==1\\n\"\n                         \"'name=John Doe'\\n\"\n                         \"Accept:text/csv\\n\"\n                         \"'Authorization:ApiKey 1234'\\n\")\n\n    def test_env_non_ascii_and_write_to_file(self):\n        filename = self.make_tempfile()\n\n        # write something first to make sure it's a full overwrite\n        with open(filename, 'w') as f:\n            f.write('hello world\\n')\n\n        self.context.body_params['name'] = '許 功蓋'\n        execute('env > %s' % filename, self.context)\n\n        with open(filename, encoding='utf-8') as f:\n            content = f.read()\n\n        self.assertEqual(content,\n                         \"--form\\n--verify=no\\n\"\n                         \"cd http://localhost:8000/api\\n\"\n                         \"limit==50\\npage==1\\n\"\n                         \"'name=許 功蓋'\\n\"\n                         \"Accept:text/csv\\n\"\n                         \"'Authorization:ApiKey 1234'\\n\")\n\n    def test_env_write_to_quoted_filename(self):\n        filename = self.make_tempfile()\n\n        # Write something first to make sure it's a full overwrite\n        with open(filename, 'w') as f:\n            f.write('hello world\\n')\n\n        execute(\"env > '%s'\" % filename, self.context)\n\n        with open(filename) as f:\n            content = f.read()\n\n        self.assertEqual(content,\n                         \"--form\\n--verify=no\\n\"\n                         \"cd http://localhost:8000/api\\n\"\n                         \"limit==50\\npage==1\\n\"\n                         \"'name=John Doe'\\n\"\n                         \"Accept:text/csv\\n\"\n                         \"'Authorization:ApiKey 1234'\\n\")\n\n    def test_env_append_to_file(self):\n        filename = self.make_tempfile()\n\n        # Write something first to make sure it's an append\n        with open(filename, 'w') as f:\n            f.write('hello world\\n')\n\n        execute('env >> %s' % filename, self.context)\n\n        with open(filename) as f:\n            content = f.read()\n\n        self.assertEqual(content,\n                         \"hello world\\n\"\n                         \"--form\\n--verify=no\\n\"\n                         \"cd http://localhost:8000/api\\n\"\n                         \"limit==50\\npage==1\\n\"\n                         \"'name=John Doe'\\n\"\n                         \"Accept:text/csv\\n\"\n                         \"'Authorization:ApiKey 1234'\\n\")\n\n\nclass TestExecution_source_and_exec(ExecutionTestCase):\n\n    def setUp(self):\n        super(TestExecution_source_and_exec, self).setUp()\n\n        self.context.url = 'http://localhost:8000/api'\n        self.context.headers.update({\n            'Accept': 'text/csv',\n            'Authorization': 'ApiKey 1234'\n        })\n        self.context.querystring_params.update({\n            'page': ['1'],\n            'limit': ['50']\n        })\n        self.context.body_params.update({\n            'name': 'John Doe'\n        })\n        self.context.options.update({\n            '--verify': 'no',\n            '--form': None\n        })\n\n        # The file that is about to be sourced/exec'd\n        self.filename = self.make_tempfile(\n            \"Language:en Authorization:'ApiKey 5678'\\n\"\n            \"name='Jane Doe'  username=jane   limit==25\\n\"\n            \"rm -o --form\\n\"\n            \"cd v2/user\\n\")\n\n    def test_source(self):\n        execute('source %s' % self.filename, self.context)\n\n        self.assertEqual(self.context.url,\n                         'http://localhost:8000/api/v2/user')\n        self.assertEqual(self.context.headers, {\n            'Accept': 'text/csv',\n            'Authorization': 'ApiKey 5678',\n            'Language': 'en'\n        })\n        self.assertEqual(self.context.querystring_params, {\n            'page': ['1'],\n            'limit': ['25']\n        })\n        self.assertEqual(self.context.body_params, {\n            'name': 'Jane Doe',\n            'username': 'jane'\n        })\n        self.assertEqual(self.context.options, {\n            '--verify': 'no'\n        })\n\n    def test_source_with_spaces(self):\n        execute(' source       %s   ' % self.filename, self.context)\n\n        self.assertEqual(self.context.url,\n                         'http://localhost:8000/api/v2/user')\n        self.assertEqual(self.context.headers, {\n            'Accept': 'text/csv',\n            'Authorization': 'ApiKey 5678',\n            'Language': 'en'\n        })\n        self.assertEqual(self.context.querystring_params, {\n            'page': ['1'],\n            'limit': ['25']\n        })\n        self.assertEqual(self.context.body_params, {\n            'name': 'Jane Doe',\n            'username': 'jane'\n        })\n        self.assertEqual(self.context.options, {\n            '--verify': 'no'\n        })\n\n    def test_source_non_existing_file(self):\n        c = self.context.copy()\n        execute('source no_such_file.txt', self.context)\n        self.assertEqual(self.context, c)\n\n        # Expect the error message would be the same as when we open the\n        # non-existing file\n        try:\n            with open('no_such_file.txt'):\n                pass\n        except OSError as err:\n            err_msg = str(err)\n        else:\n            assert False, 'what?! no_such_file.txt exists!'\n\n        self.assert_stderr(err_msg)\n\n    def test_source_quoted_filename(self):\n        execute('source \"%s\"' % self.filename, self.context)\n\n        self.assertEqual(self.context.url,\n                         'http://localhost:8000/api/v2/user')\n        self.assertEqual(self.context.headers, {\n            'Accept': 'text/csv',\n            'Authorization': 'ApiKey 5678',\n            'Language': 'en'\n        })\n        self.assertEqual(self.context.querystring_params, {\n            'page': ['1'],\n            'limit': ['25']\n        })\n        self.assertEqual(self.context.body_params, {\n            'name': 'Jane Doe',\n            'username': 'jane'\n        })\n        self.assertEqual(self.context.options, {\n            '--verify': 'no'\n        })\n\n    @pytest.mark.skipif(sys.platform == 'win32',\n                        reason=\"Windows doesn't use backslashes to escape\")\n    def test_source_escaped_filename(self):\n        new_filename = self.filename + r' copy'\n        shutil.copyfile(self.filename, new_filename)\n\n        new_filename = new_filename.replace(' ', r'\\ ')\n\n        execute('source %s' % new_filename, self.context)\n\n        self.assertEqual(self.context.url,\n                         'http://localhost:8000/api/v2/user')\n        self.assertEqual(self.context.headers, {\n            'Accept': 'text/csv',\n            'Authorization': 'ApiKey 5678',\n            'Language': 'en'\n        })\n        self.assertEqual(self.context.querystring_params, {\n            'page': ['1'],\n            'limit': ['25']\n        })\n        self.assertEqual(self.context.body_params, {\n            'name': 'Jane Doe',\n            'username': 'jane'\n        })\n        self.assertEqual(self.context.options, {\n            '--verify': 'no'\n        })\n\n    def test_exec(self):\n        execute('exec %s' % self.filename, self.context)\n\n        self.assertEqual(self.context.url,\n                         'http://localhost:8000/api/v2/user')\n        self.assertEqual(self.context.headers, {\n            'Authorization': 'ApiKey 5678',\n            'Language': 'en'\n        })\n        self.assertEqual(self.context.querystring_params, {\n            'limit': ['25']\n        })\n        self.assertEqual(self.context.body_params, {\n            'name': 'Jane Doe',\n            'username': 'jane'\n        })\n\n    def test_exec_with_spaces(self):\n        execute('  exec    %s   ' % self.filename, self.context)\n\n        self.assertEqual(self.context.url,\n                         'http://localhost:8000/api/v2/user')\n        self.assertEqual(self.context.headers, {\n            'Authorization': 'ApiKey 5678',\n            'Language': 'en'\n        })\n        self.assertEqual(self.context.querystring_params, {\n            'limit': ['25']\n        })\n        self.assertEqual(self.context.body_params, {\n            'name': 'Jane Doe',\n            'username': 'jane'\n        })\n\n    def test_exec_non_existing_file(self):\n        c = self.context.copy()\n        execute('exec no_such_file.txt', self.context)\n        self.assertEqual(self.context, c)\n\n        # Try to get the error message when opening a non-existing file\n        try:\n            with open('no_such_file.txt'):\n                pass\n        except OSError as err:\n            err_msg = str(err)\n        else:\n            assert False, 'what?! no_such_file.txt exists!'\n\n        self.assert_stderr(err_msg)\n\n    def test_exec_quoted_filename(self):\n        execute(\"exec '%s'\" % self.filename, self.context)\n\n        self.assertEqual(self.context.url,\n                         'http://localhost:8000/api/v2/user')\n        self.assertEqual(self.context.headers, {\n            'Authorization': 'ApiKey 5678',\n            'Language': 'en'\n        })\n        self.assertEqual(self.context.querystring_params, {\n            'limit': ['25']\n        })\n        self.assertEqual(self.context.body_params, {\n            'name': 'Jane Doe',\n            'username': 'jane'\n        })\n\n    @pytest.mark.skipif(sys.platform == 'win32',\n                        reason=\"Windows doesn't use backslashes to escape\")\n    def test_exec_escaped_filename(self):\n        new_filename = self.filename + r' copy'\n        shutil.copyfile(self.filename, new_filename)\n\n        new_filename = new_filename.replace(' ', r'\\ ')\n\n        execute('exec %s' % new_filename, self.context)\n        self.assertEqual(self.context.url,\n                         'http://localhost:8000/api/v2/user')\n        self.assertEqual(self.context.headers, {\n            'Authorization': 'ApiKey 5678',\n            'Language': 'en'\n        })\n        self.assertEqual(self.context.querystring_params, {\n            'limit': ['25']\n        })\n        self.assertEqual(self.context.body_params, {\n            'name': 'Jane Doe',\n            'username': 'jane'\n        })\n\n\nclass TestExecution_env_and_source(ExecutionTestCase):\n\n    def test_env_and_source(self):\n        c = Context()\n        c.url = 'http://localhost:8000/api'\n        c.headers.update({\n            'Accept': 'text/csv',\n            'Authorization': 'ApiKey 1234'\n        })\n        c.querystring_params.update({\n            'page': ['1'],\n            'limit': ['50']\n        })\n        c.body_params.update({\n            'name': 'John Doe'\n        })\n        c.options.update({\n            '--verify': 'no',\n            '--form': None\n        })\n\n        c2 = c.copy()\n\n        filename = self.make_tempfile()\n        execute('env > %s' % filename, c)\n        execute('rm *', c)\n\n        self.assertFalse(c.headers)\n        self.assertFalse(c.querystring_params)\n        self.assertFalse(c.body_params)\n        self.assertFalse(c.options)\n\n        execute('source %s' % filename, c)\n\n        self.assertEqual(c, c2)\n\n    def test_env_and_source_non_ascii(self):\n        c = Context()\n        c.url = 'http://localhost:8000/api'\n        c.headers.update({\n            'Accept': 'text/csv',\n            'Authorization': 'ApiKey 1234'\n        })\n        c.querystring_params.update({\n            'page': ['1'],\n            'limit': ['50']\n        })\n        c.body_params.update({\n            'name': '許 功蓋'\n        })\n        c.options.update({\n            '--verify': 'no',\n            '--form': None\n        })\n\n        c2 = c.copy()\n\n        filename = self.make_tempfile()\n        execute('env > %s' % filename, c)\n        execute('rm *', c)\n\n        self.assertFalse(c.headers)\n        self.assertFalse(c.querystring_params)\n        self.assertFalse(c.body_params)\n        self.assertFalse(c.options)\n\n        execute('source %s' % filename, c)\n\n        self.assertEqual(c, c2)\n\n\nclass TestExecution_help(ExecutionTestCase):\n\n    def test_help(self):\n        execute('help', self.context)\n        self.assert_stdout_startswith('Commands:\\n\\tcd')\n\n    def test_help_with_spaces(self):\n        execute('  help   ', self.context)\n        self.assert_stdout_startswith('Commands:\\n\\tcd')\n\n\nclass TestExecution_exit(ExecutionTestCase):\n\n    def test_exit(self):\n        execute('exit', self.context)\n        self.assertTrue(self.context.should_exit)\n\n    def test_exit_with_spaces(self):\n        execute('   exit  ', self.context)\n        self.assertTrue(self.context.should_exit)\n\n\nclass TestExecution_cd(ExecutionTestCase):\n\n    def test_single_level(self):\n        execute('cd api', self.context)\n        self.assertEqual(self.context.url, 'http://localhost/api')\n\n    def test_many_levels(self):\n        execute('cd api/v2/movie/50', self.context)\n        self.assertEqual(self.context.url, 'http://localhost/api/v2/movie/50')\n\n    def test_change_base(self):\n        execute('cd //example.com/api', self.context)\n        self.assertEqual(self.context.url, 'http://example.com/api')\n\n    def test_root(self):\n        execute('cd /api/v2', self.context)\n        self.assertEqual(self.context.url, 'http://localhost/api/v2')\n\n        execute('cd /index.html', self.context)\n        self.assertEqual(self.context.url, 'http://localhost/index.html')\n\n    def test_dot_dot(self):\n        execute('cd api/v1', self.context)\n        self.assertEqual(self.context.url, 'http://localhost/api/v1')\n\n        execute('cd ..', self.context)\n        self.assertEqual(self.context.url, 'http://localhost/api')\n\n        # If dot-dot has a trailing slash, the resulting URL should have a\n        # trailing slash\n        execute('cd ../rest/api/', self.context)\n        self.assertEqual(self.context.url, 'http://localhost/rest/api/')\n\n    def test_url_with_trailing_slash(self):\n        self.context.url = 'http://localhost/'\n        execute('cd api', self.context)\n        self.assertEqual(self.context.url, 'http://localhost/api')\n\n        execute('cd v2/', self.context)\n        self.assertEqual(self.context.url, 'http://localhost/api/v2/')\n\n        execute('cd /objects/', self.context)\n        self.assertEqual(self.context.url, 'http://localhost/objects/')\n\n    def test_path_with_trailing_slash(self):\n        execute('cd api/', self.context)\n        self.assertEqual(self.context.url, 'http://localhost/api/')\n\n        execute('cd movie/1/', self.context)\n        self.assertEqual(self.context.url, 'http://localhost/api/movie/1/')\n\n    def test_without_url(self):\n        execute('cd api/', self.context)\n        self.assertEqual(self.context.url, 'http://localhost/api/')\n\n        execute('cd', self.context)\n        self.assertEqual(self.context.url, 'http://localhost')\n\n\nclass TestExecution_rm(ExecutionTestCase):\n\n    def test_header(self):\n        self.context.headers['Content-Type'] = 'text/html'\n        execute('rm -h Content-Type', self.context)\n        self.assertFalse(self.context.headers)\n\n    def test_option(self):\n        self.context.options['--form'] = None\n        execute('rm -o --form', self.context)\n        self.assertFalse(self.context.options)\n\n    def test_querystring(self):\n        self.context.querystring_params['page'] = '1'\n        execute('rm -q page', self.context)\n        self.assertFalse(self.context.querystring_params)\n\n    def test_body_param(self):\n        self.context.body_params['name'] = 'alice'\n        execute('rm -b name', self.context)\n        self.assertFalse(self.context.body_params)\n\n    def test_body_json_param(self):\n        self.context.body_json_params['name'] = 'bob'\n        execute('rm -b name', self.context)\n        self.assertFalse(self.context.body_json_params)\n\n    def test_header_single_quoted(self):\n        self.context.headers['Content-Type'] = 'text/html'\n        execute(\"rm -h 'Content-Type'\", self.context)\n        self.assertFalse(self.context.headers)\n\n    def test_option_double_quoted(self):\n        self.context.options['--form'] = None\n        execute('rm -o \"--form\"', self.context)\n        self.assertFalse(self.context.options)\n\n    def test_querystring_double_quoted(self):\n        self.context.querystring_params['page size'] = '10'\n        execute('rm -q \"page size\"', self.context)\n        self.assertFalse(self.context.querystring_params)\n\n    def test_body_param_double_quoted(self):\n        self.context.body_params['family name'] = 'Doe Doe'\n        execute('rm -b \"family name\"', self.context)\n        self.assertFalse(self.context.body_params)\n\n    def test_body_param_escaped(self):\n        self.context.body_params['family name'] = 'Doe Doe'\n        execute(r'rm -b family\\ name', self.context)\n        self.assertFalse(self.context.body_params)\n\n    def test_body_json_param_escaped_colon(self):\n        self.context.body_json_params[r'where[id\\:gt]'] = 2\n        execute(r'rm -b where[id\\:gt]', self.context)\n        self.assertFalse(self.context.body_json_params)\n\n    def test_body_param_escaped_equal(self):\n        self.context.body_params[r'foo\\=bar'] = 'hello'\n        execute(r'rm -b foo\\=bar', self.context)\n        self.assertFalse(self.context.body_params)\n\n    def test_non_existing_key(self):\n        execute('rm -q abcd', self.context)\n        self.assert_stderr(\"Key 'abcd' not found\")\n\n    def test_non_existing_key_unicode(self):  # See #25\n        execute(u'rm -q abcd', self.context)\n        self.assert_stderr(\"Key 'abcd' not found\")\n\n    def test_body_reset(self):\n        self.context.body_params.update({\n            'first_name': 'alice',\n            'last_name': 'bryne'\n        })\n        execute('rm -b *', self.context)\n        self.assertFalse(self.context.body_params)\n\n    def test_querystring_reset(self):\n        self.context.querystring_params.update({\n            'first_name': 'alice',\n            'last_name': 'bryne'\n        })\n        execute('rm -q *', self.context)\n        self.assertFalse(self.context.querystring_params)\n\n    def test_headers_reset(self):\n        self.context.headers.update({\n            'Content-Type': 'text/html',\n            'Accept': 'application/json'\n        })\n        execute('rm -h *', self.context)\n        self.assertFalse(self.context.headers)\n\n    def test_options_reset(self):\n        self.context.options.update({\n            '--form': None,\n            '--body': None\n        })\n        execute('rm -o *', self.context)\n        self.assertFalse(self.context.options)\n\n    def test_reset(self):\n        self.context.options.update({\n            '--form': None,\n            '--verify': 'no'\n        })\n        self.context.headers.update({\n            'Accept': 'dontcare',\n            'Content-Type': 'dontcare'\n        })\n        self.context.querystring_params.update({\n            'name': 'dontcare',\n            'email': 'dontcare'\n        })\n        self.context.body_params.update({\n            'name': 'dontcare',\n            'email': 'dontcare'\n        })\n        self.context.body_json_params.update({\n            'name': 'dontcare'\n        })\n\n        execute('rm *', self.context)\n\n        self.assertFalse(self.context.options)\n        self.assertFalse(self.context.headers)\n        self.assertFalse(self.context.querystring_params)\n        self.assertFalse(self.context.body_params)\n        self.assertFalse(self.context.body_json_params)\n\n\nclass TestExecution_ls(ExecutionTestCase):\n\n    def test_root(self):\n        execute('ls', self.context)\n        self.assert_stdout('orgs  users\\n')\n\n    def test_relative_path(self):\n        self.context.url = 'http://localhost/users'\n        execute('ls 101', self.context)\n        self.assert_stdout('events orgs\\n')\n\n    def test_absolute_path(self):\n        self.context.url = 'http://localhost/users'\n        execute('ls /orgs/1', self.context)\n        self.assert_stdout('events  members\\n')\n\n    def test_redirect_write(self):\n        filename = self.make_tempfile()\n\n        # Write something first to make sure it's a full overwrite\n        with open(filename, 'w') as f:\n            f.write('hello world\\n')\n\n        execute('ls > %s' % filename, self.context)\n\n        with open(filename) as f:\n            content = f.read()\n        self.assertEqual(content, 'orgs\\nusers')\n\n    def test_redirect_append(self):\n        filename = self.make_tempfile()\n\n        # Write something first to make sure it's an append\n        with open(filename, 'w') as f:\n            f.write('hello world\\n')\n\n        execute('ls >> %s' % filename, self.context)\n\n        with open(filename) as f:\n            content = f.read()\n        self.assertEqual(content, 'hello world\\norgs\\nusers')\n\n    def test_grep(self):\n        execute('ls | grep users', self.context)\n        self.assert_stdout('users\\n')\n\n\nclass TestMutation(ExecutionTestCase):\n\n    def test_simple_headers(self):\n        execute('Accept:text/html User-Agent:HttpPrompt', self.context)\n        self.assertEqual(self.context.headers, {\n            'Accept': 'text/html',\n            'User-Agent': 'HttpPrompt'\n        })\n\n    def test_header_value_with_double_quotes(self):\n        execute('Accept:text/html User-Agent:\"HTTP Prompt\"', self.context)\n        self.assertEqual(self.context.headers, {\n            'Accept': 'text/html',\n            'User-Agent': 'HTTP Prompt'\n        })\n\n    def test_header_value_with_single_quotes(self):\n        execute(\"Accept:text/html User-Agent:'HTTP Prompt'\", self.context)\n        self.assertEqual(self.context.headers, {\n            'Accept': 'text/html',\n            'User-Agent': 'HTTP Prompt'\n        })\n\n    def test_header_with_double_quotes(self):\n        execute('Accept:text/html \"User-Agent:HTTP Prompt\"', self.context)\n        self.assertEqual(self.context.headers, {\n            'Accept': 'text/html',\n            'User-Agent': 'HTTP Prompt'\n        })\n\n    def test_header_with_single_quotes(self):\n        execute(\"Accept:text/html 'User-Agent:HTTP Prompt'\", self.context)\n        self.assertEqual(self.context.headers, {\n            'Accept': 'text/html',\n            'User-Agent': 'HTTP Prompt'\n        })\n\n    def test_header_escaped_chars(self):\n        execute(r'X-Name:John\\'s\\ Doe', self.context)\n        self.assertEqual(self.context.headers, {\n            'X-Name': \"John's Doe\"\n        })\n\n    def test_header_value_escaped_quote(self):\n        execute(r\"'X-Name:John\\'s Doe'\", self.context)\n        self.assertEqual(self.context.headers, {\n            'X-Name': \"John's Doe\"\n        })\n\n    def test_simple_querystring(self):\n        execute('page==1 limit==20', self.context)\n        self.assertEqual(self.context.querystring_params, {\n            'page': ['1'],\n            'limit': ['20']\n        })\n\n    def test_querystring_with_double_quotes(self):\n        execute('page==1 name==\"John Doe\"', self.context)\n        self.assertEqual(self.context.querystring_params, {\n            'page': ['1'],\n            'name': ['John Doe']\n        })\n\n    def test_querystring_with_single_quotes(self):\n        execute(\"page==1 name=='John Doe'\", self.context)\n        self.assertEqual(self.context.querystring_params, {\n            'page': ['1'],\n            'name': ['John Doe']\n        })\n\n    def test_querystring_with_chinese(self):\n        execute(\"name==王小明\", self.context)\n        self.assertEqual(self.context.querystring_params, {\n            'name': ['王小明']\n        })\n\n    def test_querystring_escaped_chars(self):\n        execute(r'name==John\\'s\\ Doe', self.context)\n        self.assertEqual(self.context.querystring_params, {\n            'name': [\"John's Doe\"]\n        })\n\n    def test_querytstring_value_escaped_quote(self):\n        execute(r\"'name==John\\'s Doe'\", self.context)\n        self.assertEqual(self.context.querystring_params, {\n            'name': [\"John's Doe\"]\n        })\n\n    def test_querystring_key_escaped_quote(self):\n        execute(r\"'john\\'s last name==Doe'\", self.context)\n        self.assertEqual(self.context.querystring_params, {\n            \"john's last name\": ['Doe']\n        })\n\n    def test_simple_body_params(self):\n        execute('username=john password=123', self.context)\n        self.assertEqual(self.context.body_params, {\n            'username': 'john',\n            'password': '123'\n        })\n\n    def test_body_param_value_with_double_quotes(self):\n        execute('name=\"John Doe\" password=123', self.context)\n        self.assertEqual(self.context.body_params, {\n            'name': 'John Doe',\n            'password': '123'\n        })\n\n    def test_body_param_value_with_single_quotes(self):\n        execute(\"name='John Doe' password=123\", self.context)\n        self.assertEqual(self.context.body_params, {\n            'name': 'John Doe',\n            'password': '123'\n        })\n\n    def test_body_param_with_double_quotes(self):\n        execute('\"name=John Doe\" password=123', self.context)\n        self.assertEqual(self.context.body_params, {\n            'name': 'John Doe',\n            'password': '123'\n        })\n\n    def test_body_param_with_spanish(self):\n        execute('name=Jesús', self.context)\n        self.assertEqual(self.context.body_params, {\n            'name': 'Jesús'\n        })\n\n    def test_body_param_escaped_chars(self):\n        execute(r'name=John\\'s\\ Doe', self.context)\n        self.assertEqual(self.context.body_params, {\n            'name': \"John's Doe\"\n        })\n\n    def test_body_param_value_escaped_quote(self):\n        execute(r\"'name=John\\'s Doe'\", self.context)\n        self.assertEqual(self.context.body_params, {\n            'name': \"John's Doe\"\n        })\n\n    def test_body_param_key_escaped_quote(self):\n        execute(r\"'john\\'s last name=Doe'\", self.context)\n        self.assertEqual(self.context.body_params, {\n            \"john's last name\": 'Doe'\n        })\n\n    def test_long_option_names(self):\n        execute('--auth user:pass --form', self.context)\n        self.assertEqual(self.context.options, {\n            '--form': None,\n            '--auth': 'user:pass'\n        })\n\n    def test_long_option_names_with_its_prefix(self):\n        execute('--auth-type basic --auth user:pass --session user '\n                '--session-read-only user', self.context)\n        self.assertEqual(self.context.options, {\n            '--auth-type': 'basic',\n            '--auth': 'user:pass',\n            '--session-read-only': 'user',\n            '--session': 'user'\n        })\n\n    def test_long_short_option_names_mixed(self):\n        execute('--style=default -j --stream', self.context)\n        self.assertEqual(self.context.options, {\n            '-j': None,\n            '--stream': None,\n            '--style': 'default'\n        })\n\n    def test_option_and_body_param(self):\n        execute('--form name=\"John Doe\"', self.context)\n        self.assertEqual(self.context.options, {\n            '--form': None\n        })\n        self.assertEqual(self.context.body_params, {\n            'name': 'John Doe'\n        })\n\n    def test_mixed(self):\n        execute('   --form  name=\"John Doe\"   password=1234\\\\ 5678    '\n                'User-Agent:HTTP\\\\ Prompt  -a   \\'john:1234 5678\\'  '\n                '\"Accept:text/html\"  ', self.context)\n        self.assertEqual(self.context.options, {\n            '--form': None,\n            '-a': 'john:1234 5678'\n        })\n        self.assertEqual(self.context.headers, {\n            'User-Agent': 'HTTP Prompt',\n            'Accept': 'text/html'\n        })\n        self.assertEqual(self.context.options, {\n            '--form': None,\n            '-a': 'john:1234 5678'\n        })\n        self.assertEqual(self.context.body_params, {\n            'name': 'John Doe',\n            'password': '1234 5678'\n        })\n\n    def test_multi_querystring(self):\n        execute('name==john name==doe', self.context)\n        self.assertEqual(self.context.querystring_params, {\n            'name': ['john', 'doe']\n        })\n\n        execute('name==jane', self.context)\n        self.assertEqual(self.context.querystring_params, {\n            'name': ['jane']\n        })\n\n    def test_raw_json_object(self):\n        execute(\"\"\"definition:={\"id\":819,\"name\":\"ML\"}\"\"\", self.context)\n        self.assertEqual(self.context.body_json_params, {\n            'definition': {\n                'id': 819,\n                'name': 'ML'\n            }\n        })\n\n    def test_raw_json_object_quoted(self):\n        execute(\"\"\"definition:='{\"id\": 819, \"name\": \"ML\"}'\"\"\", self.context)\n        self.assertEqual(self.context.body_json_params, {\n            'definition': {\n                'id': 819,\n                'name': 'ML'\n            }\n        })\n\n    def test_raw_json_array(self):\n        execute(\"\"\"names:=[\"foo\",\"bar\"]\"\"\", self.context)\n        self.assertEqual(self.context.body_json_params, {\n            'names': [\"foo\", \"bar\"]\n        })\n\n    def test_raw_json_array_quoted(self):\n        execute(\"\"\"names:='[\"foo\", \"bar\"]'\"\"\", self.context)\n        self.assertEqual(self.context.body_json_params, {\n            'names': [\"foo\", \"bar\"]\n        })\n\n    def test_raw_json_integer(self):\n        execute('number:=999', self.context)\n        self.assertEqual(self.context.body_json_params, {'number': 999})\n\n    def test_raw_json_string(self):\n        execute(\"\"\"name:='\"john doe\"'\"\"\", self.context)\n        self.assertEqual(self.context.body_json_params, {'name': 'john doe'})\n\n    def test_escape_colon(self):\n        execute(r'where[id\\:gt]:=2', self.context)\n        self.assertEqual(self.context.body_json_params, {\n            r'where[id\\:gt]': 2\n        })\n\n    def test_escape_equal(self):\n        execute(r'foo\\=bar=hello', self.context)\n        self.assertEqual(self.context.body_params, {\n            r'foo\\=bar': 'hello'\n        })\n\n\nclass TestHttpAction(ExecutionTestCase):\n\n    def test_get(self):\n        execute('get', self.context)\n        self.assert_httpie_main_called_with(['GET', 'http://localhost'])\n\n    def test_get_uppercase(self):\n        execute('GET', self.context)\n        self.assert_httpie_main_called_with(['GET', 'http://localhost'])\n\n    def test_get_multi_querystring(self):\n        execute('get foo==1 foo==2 foo==3', self.context)\n        self.assert_httpie_main_called_with([\n            'GET', 'http://localhost', 'foo==1', 'foo==2', 'foo==3'])\n\n    def test_post(self):\n        execute('post page==1', self.context)\n        self.assert_httpie_main_called_with(['POST', 'http://localhost',\n                                             'page==1'])\n        self.assertFalse(self.context.querystring_params)\n\n    def test_post_with_absolute_path(self):\n        execute('post /api/v3 name=bob', self.context)\n        self.assert_httpie_main_called_with(['POST', 'http://localhost/api/v3',\n                                             'name=bob'])\n        self.assertFalse(self.context.body_params)\n        self.assertEqual(self.context.url, 'http://localhost')\n\n    def test_post_with_relative_path(self):\n        self.context.url = 'http://localhost/api/v3'\n        execute('post ../v2/movie id=8', self.context)\n        self.assert_httpie_main_called_with([\n            'POST', 'http://localhost/api/v2/movie', 'id=8'])\n        self.assertFalse(self.context.body_params)\n        self.assertEqual(self.context.url, 'http://localhost/api/v3')\n\n    def test_post_with_full_url(self):\n        execute('post http://httpbin.org/post id=9', self.context)\n        self.assert_httpie_main_called_with([\n            'POST', 'http://httpbin.org/post', 'id=9'])\n        self.assertFalse(self.context.body_params)\n        self.assertEqual(self.context.url, 'http://localhost')\n\n    def test_post_with_full_https_url(self):\n        execute('post https://httpbin.org/post id=9', self.context)\n        self.assert_httpie_main_called_with([\n            'POST', 'https://httpbin.org/post', 'id=9'])\n        self.assertFalse(self.context.body_params)\n        self.assertEqual(self.context.url, 'http://localhost')\n\n    def test_post_uppercase(self):\n        execute('POST content=text', self.context)\n        self.assert_httpie_main_called_with(['POST', 'http://localhost',\n                                             'content=text'])\n        self.assertFalse(self.context.body_params)\n\n    def test_post_raw_json_object(self):\n        execute(\"\"\"post definition:={\"id\":819,\"name\":\"ML\"}\"\"\",\n                self.context)\n        self.assert_httpie_main_called_with([\n            'POST', 'http://localhost',\n            \"\"\"definition:={\"id\": 819, \"name\": \"ML\"}\"\"\"])\n        self.assertFalse(self.context.body_json_params)\n\n    def test_post_raw_json_object_quoted(self):\n        execute(\"\"\"post definition:='{\"id\": 819, \"name\": \"ML\"}'\"\"\",\n                self.context)\n        self.assert_httpie_main_called_with([\n            'POST', 'http://localhost',\n            'definition:={\"id\": 819, \"name\": \"ML\"}'])\n        self.assertFalse(self.context.body_json_params)\n\n    def test_post_raw_json_array(self):\n        execute(\"\"\"post hobbies:=[\"foo\",\"bar\"]\"\"\",\n                self.context)\n        self.assert_httpie_main_called_with([\n            'POST', 'http://localhost',\n            'hobbies:=[\"foo\", \"bar\"]'])\n        self.assertFalse(self.context.body_json_params)\n\n    def test_post_raw_json_array_quoted(self):\n        execute(\"\"\"post hobbies:='[\"foo\", \"bar\"]'\"\"\",\n                self.context)\n        self.assert_httpie_main_called_with([\n            'POST', 'http://localhost',\n            'hobbies:=[\"foo\", \"bar\"]'])\n        self.assertFalse(self.context.body_json_params)\n\n    def test_post_raw_json_integer(self):\n        execute('post number:=123',\n                self.context)\n        self.assert_httpie_main_called_with([\n            'POST', 'http://localhost', 'number:=123'])\n        self.assertFalse(self.context.body_json_params)\n\n    def test_post_raw_json_boolean(self):\n        execute('post foo:=true',\n                self.context)\n        self.assert_httpie_main_called_with([\n            'POST', 'http://localhost', 'foo:=true'])\n        self.assertFalse(self.context.body_json_params)\n\n    def test_delete(self):\n        execute('delete', self.context)\n        self.assert_httpie_main_called_with(['DELETE', 'http://localhost'])\n\n    def test_delete_uppercase(self):\n        execute('DELETE', self.context)\n        self.assert_httpie_main_called_with(['DELETE', 'http://localhost'])\n\n    def test_patch(self):\n        execute('patch', self.context)\n        self.assert_httpie_main_called_with(['PATCH', 'http://localhost'])\n\n    def test_patch_uppercase(self):\n        execute('PATCH', self.context)\n        self.assert_httpie_main_called_with(['PATCH', 'http://localhost'])\n\n    def test_head(self):\n        execute('head', self.context)\n        self.assert_httpie_main_called_with(['HEAD', 'http://localhost'])\n\n    def test_head_uppercase(self):\n        execute('HEAD', self.context)\n        self.assert_httpie_main_called_with(['HEAD', 'http://localhost'])\n\n    def test_options(self):\n        execute('options', self.context)\n        self.assert_httpie_main_called_with(['OPTIONS', 'http://localhost'])\n\n\nclass TestHttpActionRedirection(ExecutionTestCase):\n\n    def test_get(self):\n        execute('get > data.json', self.context)\n        self.assert_httpie_main_called_with(['GET', 'http://localhost'])\n\n        env = self.httpie_main.call_args[1]['env']\n        self.assertFalse(env.stdout_isatty)\n        self.assertEqual(env.stdout.fp.name, 'data.json')\n\n\n@pytest.mark.slow\nclass TestHttpBin(TempAppDirTestCase):\n    \"\"\"Send real requests to http://httpbin.org, save the responses to files,\n    and asserts on the file content.\n    \"\"\"\n\n    def setUp(self):\n        super(TestHttpBin, self).setUp()\n\n        # XXX: pytest doesn't allow HTTPie to read stdin while it's capturing\n        # stdout, so we replace stdin with a file temporarily during the test.\n        class MockStdin(object):\n            def __init__(self, fp):\n                self.fp = fp\n\n            def isatty(self):\n                return True\n\n            def __getattr__(self, name):\n                if name == 'isatty':\n                    return self.isatty\n                return getattr(self.fp, name)\n\n        self.orig_stdin = sys.stdin\n        filename = self.make_tempfile()\n        sys.stdin = MockStdin(open(filename, 'rb'))\n        sys.stdin.isatty = lambda: True\n\n        # Mock echo_via_pager() so that we can catch data fed to stdout\n        self.patcher = patch('http_prompt.output.click.echo_via_pager')\n        self.echo_via_pager = self.patcher.start()\n\n    def tearDown(self):\n        self.patcher.stop()\n\n        sys.stdin.close()\n        sys.stdin = self.orig_stdin\n\n        super(TestHttpBin, self).tearDown()\n\n    def get_stdout(self):\n        return self.echo_via_pager.call_args[0][0]\n\n    def execute_redirection(self, command):\n        context = Context('http://httpbin.org')\n        filename = self.make_tempfile()\n        execute('%s > %s' % (command, filename), context)\n\n        with open(filename, 'rb') as f:\n            return f.read()\n\n    def execute_pipe(self, command):\n        context = Context('http://httpbin.org')\n        execute(command, context)\n\n    def test_get_image(self):\n        data = self.execute_redirection('get /image/png')\n        self.assertTrue(data)\n        self.assertEqual(hashlib.sha1(data).hexdigest(),\n                         '379f5137831350c900e757b39e525b9db1426d53')\n\n    def test_get_querystring(self):\n        data = self.execute_redirection(\n            'get /get id==1234 X-Custom-Header:5678')\n        data = json.loads(data.decode())\n        self.assertEqual(data['args'], {\n            'id': '1234'\n        })\n        self.assertEqual(data['headers']['X-Custom-Header'], '5678')\n\n    def test_post_json(self):\n        data = self.execute_redirection(\n            'post /post id=1234 X-Custom-Header:5678')\n        data = json.loads(data.decode())\n        self.assertEqual(data['json'], {\n            'id': '1234'\n        })\n        self.assertEqual(data['headers']['X-Custom-Header'], '5678')\n\n    def test_post_form(self):\n        data = self.execute_redirection(\n            'post /post --form id=1234 X-Custom-Header:5678')\n        data = json.loads(data.decode())\n        self.assertEqual(data['form'], {\n            'id': '1234'\n        })\n        self.assertEqual(data['headers']['X-Custom-Header'], '5678')\n\n    @pytest.mark.skipif(sys.platform == 'win32', reason=\"Unix only\")\n    def test_get_and_tee(self):\n        filename = self.make_tempfile()\n        self.execute_pipe('get /get hello==world | tee %s' % filename)\n\n        with open(filename) as f:\n            data = json.load(f)\n        self.assertEqual(data['args'], {'hello': 'world'})\n\n        printed_msg = self.get_stdout()\n        data = json.loads(printed_msg)\n        self.assertEqual(data['args'], {'hello': 'world'})\n\n\nclass TestCommandPreview(ExecutionTestCase):\n\n    def test_httpie_without_args(self):\n        execute('httpie', self.context)\n        self.assert_stdout('http http://localhost\\n')\n\n    def test_httpie_with_post(self):\n        execute('httpie post name=alice', self.context)\n        self.assert_stdout('http POST http://localhost name=alice\\n')\n        self.assertFalse(self.context.body_params)\n\n    def test_httpie_with_absolute_path(self):\n        execute('httpie post /api name=alice', self.context)\n        self.assert_stdout('http POST http://localhost/api name=alice\\n')\n        self.assertFalse(self.context.body_params)\n\n    def test_httpie_with_full_url(self):\n        execute('httpie POST http://httpbin.org/post name=alice', self.context)\n        self.assert_stdout('http POST http://httpbin.org/post name=alice\\n')\n        self.assertEqual(self.context.url, 'http://localhost')\n        self.assertFalse(self.context.body_params)\n\n    def test_httpie_with_full_https_url(self):\n        execute('httpie post https://httpbin.org/post name=alice',\n                self.context)\n        self.assert_stdout('http POST https://httpbin.org/post name=alice\\n')\n        self.assertEqual(self.context.url, 'http://localhost')\n        self.assertFalse(self.context.body_params)\n\n    def test_httpie_with_quotes(self):\n        execute(r'httpie post http://httpbin.org/post name=\"john doe\" '\n                r\"apikey==abc\\ 123 'Authorization:ApiKey 1234'\",\n                self.context)\n        self.assert_stdout(\n            \"http POST http://httpbin.org/post 'apikey==abc 123' \"\n            \"'name=john doe' 'Authorization:ApiKey 1234'\\n\")\n        self.assertEqual(self.context.url, 'http://localhost')\n        self.assertFalse(self.context.body_params)\n        self.assertFalse(self.context.querystring_params)\n        self.assertFalse(self.context.headers)\n\n    def test_httpie_with_multi_querystring(self):\n        execute('httpie get foo==1 foo==2 foo==3', self.context)\n        self.assert_stdout('http GET http://localhost foo==1 foo==2 foo==3\\n')\n        self.assertEqual(self.context.url, 'http://localhost')\n        self.assertFalse(self.context.querystring_params)\n\n\nclass TestPipe(ExecutionTestCase):\n\n    @pytest.mark.skipif(sys.platform == 'win32', reason=\"Unix only\")\n    def test_httpie_sed(self):\n        execute(\"httpie get some==data | sed 's/data$/input/'\", self.context)\n        self.assert_stdout('http GET http://localhost some==input\\n')\n\n    @pytest.mark.skipif(sys.platform == 'win32', reason=\"Unix only\")\n    def test_httpie_sed_with_echo(self):\n        execute(\"httpie post | `echo \\\"sed 's/localhost$/127.0.0.1/'\\\"`\",\n                self.context)\n        self.assert_stdout(\"http POST http://127.0.0.1\\n\")\n\n    @pytest.mark.skipif(sys.platform == 'win32', reason=\"Unix only\")\n    def test_env_grep(self):\n        self.context.body_params = {\n            'username': 'jane',\n            'name': 'Jane',\n            'password': '1234'\n        }\n        execute('env | grep name', self.context)\n        self.assert_stdout('name=Jane\\nusername=jane\\n')\n\n\nclass TestShellSubstitution(ExecutionTestCase):\n\n    def test_unquoted_option(self):\n        execute(\"--auth `echo user:pass`\", self.context)\n        self.assertEqual(self.context.options, {\n            '--auth': 'user:pass'\n        })\n\n    def test_partial_unquoted_option(self):\n        execute(\"--auth user:`echo pass`\", self.context)\n        self.assertEqual(self.context.options, {\n            '--auth': 'user:pass'\n        })\n\n    def test_partial_squoted_option(self):\n        execute(\"--auth='user:`echo pass`'\", self.context)\n        self.assertEqual(self.context.options, {\n            '--auth': 'user:pass'\n        })\n\n    def test_partial_dquoted_option(self):\n        execute('--auth=\"user:`echo pass`\"', self.context)\n        self.assertEqual(self.context.options, {\n            '--auth': 'user:pass'\n        })\n\n    def test_unquoted_header(self):\n        execute(\"`echo 'X-Greeting'`:`echo 'hello world'`\", self.context)\n        if sys.platform == 'win32':\n            expected_key = \"'X-Greeting'\"\n            expected_value = \"'hello world'\"\n        else:\n            expected_key = 'X-Greeting'\n            expected_value = 'hello world'\n\n        self.assertEqual(self.context.headers, {\n            expected_key: expected_value\n        })\n\n    def test_full_squoted_header(self):\n        execute(\"'`echo X-Greeting`:`echo hello`'\", self.context)\n        self.assertEqual(self.context.headers, {\n            'X-Greeting': 'hello'\n        })\n\n    def test_full_dquoted_header(self):\n        execute('\"`echo X-Greeting`:`echo hello`\"', self.context)\n        self.assertEqual(self.context.headers, {\n            'X-Greeting': 'hello'\n        })\n\n    def test_value_squoted_header(self):\n        execute(\"`echo X-Greeting`:'`echo hello`'\", self.context)\n        self.assertEqual(self.context.headers, {\n            'X-Greeting': 'hello'\n        })\n\n    def test_value_dquoted_header(self):\n        execute('`echo X-Greeting`:\"`echo hello`\"', self.context)\n        self.assertEqual(self.context.headers, {\n            'X-Greeting': 'hello'\n        })\n\n    def test_partial_value_dquoted_header(self):\n        execute('Authorization:\"Bearer `echo OAUTH TOKEN`\"', self.context)\n        self.assertEqual(self.context.headers, {\n            'Authorization': 'Bearer OAUTH TOKEN'\n        })\n\n    def test_partial_full_dquoted_header(self):\n        execute('\"Authorization:Bearer `echo OAUTH TOKEN`\"', self.context)\n        self.assertEqual(self.context.headers, {\n            'Authorization': 'Bearer OAUTH TOKEN'\n        })\n\n    def test_unquoted_querystring(self):\n        execute(\"`echo greeting`==`echo 'hello world'`\", self.context)\n        expected = (\"'hello world'\"\n                    if sys.platform == 'win32' else 'hello world')\n        self.assertEqual(self.context.querystring_params, {\n            'greeting': [expected]\n        })\n\n    def test_full_squoted_querystring(self):\n        execute(\"'`echo greeting`==`echo hello`'\", self.context)\n        self.assertEqual(self.context.querystring_params, {\n            'greeting': ['hello']\n        })\n\n    def test_value_squoted_querystring(self):\n        execute(\"`echo greeting`=='`echo hello`'\", self.context)\n        self.assertEqual(self.context.querystring_params, {\n            'greeting': ['hello']\n        })\n\n    def test_value_dquoted_querystring(self):\n        execute('`echo greeting`==\"`echo hello`\"', self.context)\n        self.assertEqual(self.context.querystring_params, {\n            'greeting': ['hello']\n        })\n\n    def test_unquoted_body_param(self):\n        execute(\"`echo greeting`=`echo 'hello world'`\", self.context)\n        expected = (\"'hello world'\"\n                    if sys.platform == 'win32' else 'hello world')\n        self.assertEqual(self.context.body_params, {\n            'greeting': expected\n        })\n\n    def test_full_squoted_body_param(self):\n        execute(\"'`echo greeting`=`echo hello`'\", self.context)\n        self.assertEqual(self.context.body_params, {\n            'greeting': 'hello'\n        })\n\n    def test_value_squoted_body_param(self):\n        execute(\"`echo greeting`='`echo hello`'\", self.context)\n        self.assertEqual(self.context.body_params, {\n            'greeting': 'hello'\n        })\n\n    def test_full_dquoted_body_param(self):\n        execute('\"`echo greeting`=`echo hello`\"', self.context)\n        self.assertEqual(self.context.body_params, {\n            'greeting': 'hello'\n        })\n\n    def test_bad_command(self):\n        execute(\"name=`bad command test`\", self.context)\n        self.assertEqual(self.context.body_params, {'name': ''})\n\n    @pytest.mark.skipif(sys.platform == 'win32', reason=\"Unix only\")\n    def test_pipe_and_grep(self):\n        execute(\"greeting=`echo 'hello world\\nhihi\\n' | grep hello`\",\n                self.context)\n        self.assertEqual(self.context.body_params, {\n            'greeting': 'hello world'\n        })\n\n\nclass TestCommandPreviewRedirection(ExecutionTestCase):\n\n    def test_httpie_redirect_write(self):\n        filename = self.make_tempfile()\n\n        # Write something first to make sure it's a full overwrite\n        with open(filename, 'w') as f:\n            f.write('hello world\\n')\n\n        execute('httpie > %s' % filename, self.context)\n\n        with open(filename) as f:\n            content = f.read()\n        self.assertEqual(content, 'http http://localhost\\n')\n\n    def test_httpie_redirect_write_quoted_filename(self):\n        filename = self.make_tempfile()\n\n        # Write something first to make sure it's a full overwrite\n        with open(filename, 'w') as f:\n            f.write('hello world\\n')\n\n        execute('httpie > \"%s\"' % filename, self.context)\n\n        with open(filename) as f:\n            content = f.read()\n        self.assertEqual(content, 'http http://localhost\\n')\n\n    @pytest.mark.skipif(sys.platform == 'win32',\n                        reason=\"Windows doesn't use backslashes to escape\")\n    def test_httpie_redirect_write_escaped_filename(self):\n        filename = self.make_tempfile()\n        filename += r' copy'\n\n        # Write something first to make sure it's a full overwrite\n        with open(filename, 'w') as f:\n            f.write('hello world\\n')\n\n        execute('httpie > %s' % filename.replace(' ', r'\\ '), self.context)\n\n        with open(filename) as f:\n            content = f.read()\n        self.assertEqual(content, 'http http://localhost\\n')\n\n    def test_httpie_redirect_write_with_args(self):\n        filename = self.make_tempfile()\n\n        # Write something first to make sure it's a full overwrite\n        with open(filename, 'w') as f:\n            f.write('hello world\\n')\n\n        execute('httpie post http://example.org name=john > %s' % filename,\n                self.context)\n\n        with open(filename) as f:\n            content = f.read()\n        self.assertEqual(content, 'http POST http://example.org name=john\\n')\n\n    def test_httpie_redirect_append(self):\n        filename = self.make_tempfile()\n\n        # Write something first to make sure it's an append\n        with open(filename, 'w') as f:\n            f.write('hello world\\n')\n\n        execute('httpie >> %s' % filename, self.context)\n\n        with open(filename) as f:\n            content = f.read()\n        self.assertEqual(content, 'hello world\\nhttp http://localhost\\n')\n\n    def test_httpie_redirect_append_without_spaces(self):\n        filename = self.make_tempfile()\n\n        # Write something first to make sure it's an append\n        with open(filename, 'w') as f:\n            f.write('hello world\\n')\n\n        execute('httpie>>%s' % filename, self.context)\n\n        with open(filename) as f:\n            content = f.read()\n        self.assertEqual(content, 'hello world\\nhttp http://localhost\\n')\n\n    def test_httpie_redirect_append_quoted_filename(self):\n        filename = self.make_tempfile()\n\n        # Write something first to make sure it's an append\n        with open(filename, 'w') as f:\n            f.write('hello world\\n')\n\n        execute(\"httpie >> '%s'\" % filename, self.context)\n\n        with open(filename) as f:\n            content = f.read()\n        self.assertEqual(content, 'hello world\\nhttp http://localhost\\n')\n"
  },
  {
    "path": "tests/test_installation.py",
    "content": "\"\"\"Test if http-prompt is installed correctly.\"\"\"\n\nimport subprocess\n\nimport pytest\n\nfrom subprocess import PIPE\n\nfrom .utils import get_http_prompt_path\nfrom http_prompt import __version__\n\n\ndef run_http_prompt(args):\n    \"\"\"Run http-prompt from terminal.\"\"\"\n    bin_path = get_http_prompt_path()\n    p = subprocess.Popen([bin_path] + args, stdin=PIPE, stdout=PIPE)\n    return p.communicate()\n\n\n@pytest.mark.slow\ndef test_help():\n    out, err = run_http_prompt(['--help'])\n    assert out.startswith(b'Usage: http-prompt')\n\n\n@pytest.mark.slow\ndef test_version():\n    out, err = run_http_prompt(['--version'])\n    version = __version__\n    if hasattr(version, 'encode'):\n        version = version.encode('ascii')\n    assert out.rstrip() == version\n"
  },
  {
    "path": "tests/test_interaction.py",
    "content": "import os\nimport sys\n\nimport pexpect\nimport pytest\n\nfrom .base import TempAppDirTestCase\nfrom .utils import get_http_prompt_path\nfrom http_prompt import config\n\n\nclass TestInteraction(TempAppDirTestCase):\n\n    def setUp(self):\n        super(TestInteraction, self).setUp()\n\n        # Use temporary directory as user config home.\n        # Will restore it in tearDown().\n        self.orig_config_home = os.getenv('XDG_CONFIG_HOME')\n        os.environ['XDG_CONFIG_HOME'] = self.temp_dir\n\n        # Make sure pexpect uses the same terminal environment\n        self.orig_term = os.getenv('TERM')\n        os.environ['TERM'] = 'screen-256color'\n\n    def tearDown(self):\n        super(TestInteraction, self).tearDown()\n\n        os.environ['XDG_CONFIG_HOME'] = self.orig_config_home\n\n        if self.orig_term:\n            os.environ['TERM'] = self.orig_term\n        else:\n            os.environ.pop('TERM', None)\n\n    def write_config(self, content):\n        config_path = config.get_user_config_path()\n        with open(config_path, 'a') as f:\n            f.write(content)\n\n    @pytest.mark.skipif(sys.platform == 'win32',\n                        reason=\"pexpect doesn't work well on Windows\")\n    @pytest.mark.slow\n    def test_interaction(self):\n        bin_path = get_http_prompt_path()\n        child = pexpect.spawn(bin_path, env=os.environ)\n\n        # TODO: Test more interaction\n\n        child.sendline('exit')\n        child.expect_exact('Goodbye!', timeout=20)\n        child.close()\n\n    @pytest.mark.skipif(sys.platform == 'win32',\n                        reason=\"pexpect doesn't work well on Windows\")\n    @pytest.mark.slow\n    def test_vi_mode(self):\n        self.write_config('vi = True\\n')\n\n        bin_path = get_http_prompt_path()\n        child = pexpect.spawn(bin_path, env=os.environ)\n\n        child.expect_exact('http://localhost:8000>')\n\n        # Enter 'htpie', switch to command mode (ESC),\n        # move two chars left (hh), and insert (i) a 't'\n        child.send('htpie')\n        child.send('\\x1b')\n        child.sendline('hhit')\n\n        child.expect_exact('http http://localhost:8000')\n\n        # Enter 'exit'\n        child.send('\\x1b')\n        child.send('i')\n        child.sendline('exit')\n\n        child.expect_exact('Goodbye!', timeout=20)\n        child.close()\n"
  },
  {
    "path": "tests/test_lexer.py",
    "content": "import unittest\n\nfrom pygments.token import Keyword, String, Text, Error, Name, Operator\n\nfrom http_prompt.lexer import HttpPromptLexer\n\n\nclass LexerTestCase(unittest.TestCase):\n\n    def setUp(self):\n        self.lexer = HttpPromptLexer()\n\n    def get_tokens(self, text, filter_spaces=True):\n        tokens = self.lexer.get_tokens(text)\n        tokens = filter(lambda t: t[1], tokens)\n        if filter_spaces:\n            tokens = filter(lambda t: t[1].strip(), tokens)\n        return list(tokens)\n\n\nclass TestLexer_mutation(LexerTestCase):\n\n    def test_querystring(self):\n        self.assertEqual(self.get_tokens('foo==bar'), [\n            (Name, 'foo'),\n            (Operator, '=='),\n            (String, 'bar')\n        ])\n\n    def test_body_param(self):\n        self.assertEqual(self.get_tokens('foo=bar'), [\n            (Name, 'foo'),\n            (Operator, '='),\n            (String, 'bar')\n        ])\n\n    def test_header(self):\n        self.assertEqual(self.get_tokens('Accept:application/json'), [\n            (Name, 'Accept'),\n            (Operator, ':'),\n            (String, 'application/json')\n        ])\n\n    def test_json_integer(self):\n        self.assertEqual(self.get_tokens('number:=1'), [\n            (Name, 'number'),\n            (Operator, ':='),\n            (String, '1')\n        ])\n\n    def test_json_boolean(self):\n        self.assertEqual(self.get_tokens('enabled:=true'), [\n            (Name, 'enabled'),\n            (Operator, ':='),\n            (String, 'true')\n        ])\n\n    def test_json_string(self):\n        self.assertEqual(self.get_tokens('name:=\"foo bar\"'), [\n            (Name, 'name'),\n            (Operator, ':='),\n            (Text, '\"'),\n            (String, 'foo bar'),\n            (Text, '\"')\n        ])\n\n    def test_json_array(self):\n        self.assertEqual(self.get_tokens('list:=[1,\"two\"]'), [\n            (Name, 'list'),\n            (Operator, ':='),\n            (String, '[1,\"two\"]'),\n        ])\n\n    def test_json_array_quoted(self):\n        self.assertEqual(self.get_tokens(\"\"\"list:='[1,\"two\"]'\"\"\"), [\n            (Name, 'list'),\n            (Operator, ':='),\n            (Text, \"'\"),\n            (String, '[1,\"two\"]'),\n            (Text, \"'\"),\n        ])\n\n    def test_json_object(self):\n        self.assertEqual(self.get_tokens('object:={\"id\":123,\"name\":\"foo\"}'), [\n            (Name, 'object'),\n            (Operator, ':='),\n            (String, '{\"id\":123,\"name\":\"foo\"}'),\n        ])\n\n    def test_json_object_quoted(self):\n        self.assertEqual(self.get_tokens(\"\"\"object:='{\"id\": 123}'\"\"\"), [\n            (Name, 'object'),\n            (Operator, ':='),\n            (Text, \"'\"),\n            (String, '{\"id\": 123}'),\n            (Text, \"'\")\n        ])\n\n    def test_json_escaped_colon(self):\n        self.assertEqual(self.get_tokens(r'where[id\\:gt]:=2'), [\n            (Name, r'where[id\\:gt]'),\n            (Operator, ':='),\n            (String, '2')\n        ])\n\n    def test_body_param_escaped_equal(self):\n        self.assertEqual(self.get_tokens(r'foo\\=bar=hello'), [\n            (Name, r'foo\\=bar'),\n            (Operator, '='),\n            (String, 'hello')\n        ])\n\n    def test_parameter_name_including_http_method_name(self):\n        self.assertEqual(self.get_tokens('heading==hello'), [\n            (Name, 'heading'),\n            (Operator, '=='),\n            (String, 'hello')\n        ])\n\n\nclass TestLexer_cd(LexerTestCase):\n\n    def test_simple(self):\n        self.assertEqual(self.get_tokens('cd api/v1'), [\n            (Keyword, 'cd'),\n            (String, 'api/v1')\n        ])\n\n    def test_double_quoted(self):\n        self.assertEqual(self.get_tokens('cd \"api/v 1\"'), [\n            (Keyword, 'cd'),\n            (Text, '\"'),\n            (String, 'api/v 1'),\n            (Text, '\"')\n        ])\n\n    def test_single_quoted(self):\n        self.assertEqual(self.get_tokens(\"cd 'api/v 1'\"), [\n            (Keyword, 'cd'),\n            (Text, \"'\"),\n            (String, 'api/v 1'),\n            (Text, \"'\")\n        ])\n\n    def test_escape(self):\n        self.assertEqual(self.get_tokens(r\"cd api/v\\ 1\"), [\n            (Keyword, 'cd'),\n            (String, r'api/v\\ 1')\n        ])\n\n    def test_second_path(self):\n        self.assertEqual(self.get_tokens(r\"cd api v1\"), [\n            (Keyword, 'cd'),\n            (String, 'api'),\n            (Error, 'v'),\n            (Error, '1')\n        ])\n\n    def test_leading_trailing_spaces(self):\n        self.assertEqual(self.get_tokens('   cd   api/v1  '), [\n            (Keyword, 'cd'),\n            (String, 'api/v1')\n        ])\n\n\nclass TestLexer_ls(LexerTestCase):\n\n    def test_no_path(self):\n        self.assertEqual(self.get_tokens('ls'), [\n            (Keyword, 'ls')\n        ])\n\n    def test_path(self):\n        self.assertEqual(self.get_tokens('ls api/v1'), [\n            (Keyword, 'ls'),\n            (String, 'api/v1')\n        ])\n\n    def test_second_path(self):\n        self.assertEqual(self.get_tokens(r\"ls api v1\"), [\n            (Keyword, 'ls'),\n            (String, 'api'),\n            (Error, 'v'),\n            (Error, '1')\n        ])\n\n    def test_leading_trailing_spaces(self):\n        self.assertEqual(self.get_tokens('   ls   api/v1  '), [\n            (Keyword, 'ls'),\n            (String, 'api/v1')\n        ])\n\n    def test_redirect(self):\n        self.assertEqual(self.get_tokens('ls api/v1 > endpoints.txt'), [\n            (Keyword, 'ls'),\n            (String, 'api/v1'),\n            (Operator, '>'),\n            (String, 'endpoints.txt')\n        ])\n\n\nclass TestLexer_env(LexerTestCase):\n\n    def test_env_simple(self):\n        self.assertEqual(self.get_tokens('env'), [\n            (Keyword, 'env'),\n        ])\n\n    def test_env_with_spaces(self):\n        self.assertEqual(self.get_tokens('   env    '), [\n            (Keyword, 'env'),\n        ])\n\n    def test_env_write(self):\n        self.assertEqual(self.get_tokens('env > /tmp/file.txt'), [\n            (Keyword, 'env'), (Operator, '>'),\n            (String, '/tmp/file.txt')\n        ])\n\n    def test_env_append(self):\n        self.assertEqual(self.get_tokens('env >> /tmp/file.txt'), [\n            (Keyword, 'env'), (Operator, '>>'),\n            (String, '/tmp/file.txt')\n        ])\n\n    def test_env_write_quoted_filename(self):\n        self.assertEqual(self.get_tokens('env > \"/tmp/my file.txt\"'), [\n            (Keyword, 'env'), (Operator, '>'),\n            (Text, '\"'), (String, '/tmp/my file.txt'), (Text, '\"')\n        ])\n\n    def test_env_append_escaped_filename(self):\n        self.assertEqual(self.get_tokens(r'env >> /tmp/my\\ file.txt'), [\n            (Keyword, 'env'), (Operator, '>>'),\n            (String, r'/tmp/my\\ file.txt')\n        ])\n\n    def test_env_pipe(self):\n        self.assertEqual(self.get_tokens('env | grep name'), [\n            (Keyword, 'env'), (Operator, '|'),\n            (Text, 'grep'), (Text, 'name')\n        ])\n\n\nclass TestLexer_rm(LexerTestCase):\n\n    def test_header(self):\n        self.assertEqual(self.get_tokens('rm -h Accept'), [\n            (Keyword, 'rm'),\n            (Name, '-h'),\n            (String, 'Accept')\n        ])\n\n    def test_header_escaped(self):\n        self.assertEqual(self.get_tokens(r'rm -h Custom\\ Header'), [\n            (Keyword, 'rm'),\n            (Name, '-h'),\n            (String, r'Custom\\ Header')\n        ])\n\n    def test_querystring(self):\n        self.assertEqual(self.get_tokens('rm -q page'), [\n            (Keyword, 'rm'),\n            (Name, '-q'),\n            (String, 'page')\n        ])\n\n    def test_querystring_double_quoted(self):\n        self.assertEqual(self.get_tokens('rm -q \"page size\"'), [\n            (Keyword, 'rm'),\n            (Name, '-q'),\n            (Text, '\"'),\n            (String, 'page size'),\n            (Text, '\"')\n        ])\n\n    def test_body_param(self):\n        self.assertEqual(self.get_tokens('rm -b name'), [\n            (Keyword, 'rm'),\n            (Name, '-b'),\n            (String, 'name')\n        ])\n\n    def test_body_param_single_quoted(self):\n        self.assertEqual(self.get_tokens(\"rm -b 'first name'\"), [\n            (Keyword, 'rm'),\n            (Name, '-b'),\n            (Text, \"'\"),\n            (String, 'first name'),\n            (Text, \"'\")\n        ])\n\n    def test_option(self):\n        self.assertEqual(self.get_tokens('rm -o --json'), [\n            (Keyword, 'rm'),\n            (Name, '-o'),\n            (String, '--json')\n        ])\n\n    def test_reset(self):\n        self.assertEqual(self.get_tokens('rm *'), [\n            (Keyword, 'rm'),\n            (Name, '*')\n        ])\n\n    def test_option_leading_trailing_spaces(self):\n        self.assertEqual(self.get_tokens('  rm  -o    --json   '), [\n            (Keyword, 'rm'),\n            (Name, '-o'),\n            (String, '--json')\n        ])\n\n    def test_invalid_type(self):\n        self.assertEqual(self.get_tokens('rm -a foo'), [\n            (Keyword, 'rm'),\n            (Error, '-'), (Error, 'a'),\n            (Error, 'f'), (Error, 'o'), (Error, 'o')\n        ])\n\n\nclass TestLexer_help(LexerTestCase):\n\n    def test_help_simple(self):\n        self.assertEqual(self.get_tokens('help'), [\n            (Keyword, 'help')\n        ])\n\n    def test_help_with_spaces(self):\n        self.assertEqual(self.get_tokens('  help   '), [\n            (Keyword, 'help')\n        ])\n\n\nclass TestLexer_source(LexerTestCase):\n\n    def test_source_simple_filename(self):\n        self.assertEqual(self.get_tokens('source file.txt'), [\n            (Keyword, 'source'), (String, 'file.txt')\n        ])\n\n    def test_source_with_spaces(self):\n        self.assertEqual(self.get_tokens('  source    file.txt    '), [\n            (Keyword, 'source'), (String, 'file.txt')\n        ])\n\n    def test_source_quoted_filename(self):\n        self.assertEqual(self.get_tokens(\"source '/tmp/my file.txt'\"), [\n            (Keyword, 'source'),\n            (Text, \"'\"), (String, '/tmp/my file.txt'), (Text, \"'\")\n        ])\n\n    def test_source_escaped_filename(self):\n        self.assertEqual(self.get_tokens(r\"source /tmp/my\\ file.txt\"), [\n            (Keyword, 'source'), (String, r'/tmp/my\\ file.txt')\n        ])\n\n\nclass TestLexer_exec(LexerTestCase):\n\n    def test_exec_simple_filename(self):\n        self.assertEqual(self.get_tokens('exec file.txt'), [\n            (Keyword, 'exec'), (String, 'file.txt')\n        ])\n\n    def test_exec_with_spaces(self):\n        self.assertEqual(self.get_tokens('  exec    file.txt    '), [\n            (Keyword, 'exec'), (String, 'file.txt')\n        ])\n\n    def test_exec_quoted_filename(self):\n        self.assertEqual(self.get_tokens(\"exec '/tmp/my file.txt'\"), [\n            (Keyword, 'exec'),\n            (Text, \"'\"), (String, '/tmp/my file.txt'), (Text, \"'\")\n        ])\n\n    def test_exec_escaped_filename(self):\n        self.assertEqual(self.get_tokens(r\"exec /tmp/my\\ file.txt\"), [\n            (Keyword, 'exec'), (String, r'/tmp/my\\ file.txt')\n        ])\n\n\nclass TestLexer_exit(LexerTestCase):\n\n    def test_exit_simple(self):\n        self.assertEqual(self.get_tokens('exit'), [\n            (Keyword, 'exit')\n        ])\n\n    def test_exit_with_spaces(self):\n        self.assertEqual(self.get_tokens('  exit   '), [\n            (Keyword, 'exit')\n        ])\n\n\nclass TestLexerPreview(LexerTestCase):\n\n    def test_httpie_without_action(self):\n        cmd = 'httpie http://example.com name=jack'\n        self.assertEqual(self.get_tokens(cmd), [\n            (Keyword, 'httpie'),\n            (String, 'http://example.com'),\n            (Name, 'name'), (Operator, '='), (String, 'jack')\n        ])\n\n    def test_httpie_without_action_and_url(self):\n        cmd = 'httpie name=jack Accept:*/*'\n        self.assertEqual(self.get_tokens(cmd), [\n            (Keyword, 'httpie'),\n            (Name, 'name'), (Operator, '='), (String, 'jack'),\n            (Name, 'Accept'), (Operator, ':'), (String, '*/*')\n        ])\n\n    def test_httpie_absolute_url(self):\n        cmd = 'httpie post http://example.com name=jack'\n        self.assertEqual(self.get_tokens(cmd), [\n            (Keyword, 'httpie'), (Keyword, 'post'),\n            (String, 'http://example.com'),\n            (Name, 'name'), (Operator, '='), (String, 'jack')\n        ])\n\n    def test_httpie_option_first(self):\n        self.assertEqual(self.get_tokens('httpie post --form name=jack'), [\n            (Keyword, 'httpie'), (Keyword, 'post'),\n            (Name, '--form'),\n            (Name, 'name'), (Operator, '='), (String, 'jack')\n        ])\n\n    def test_httpie_body_param_first(self):\n        self.assertEqual(self.get_tokens('httpie post name=jack --form'), [\n            (Keyword, 'httpie'), (Keyword, 'post'),\n            (Name, 'name'), (Operator, '='), (String, 'jack'),\n            (Name, '--form')\n        ])\n\n    def test_httpie_options(self):\n        self.assertEqual(self.get_tokens('httpie options test --body'), [\n            (Keyword, 'httpie'), (Keyword, 'options'),\n            (String, 'test'), (Name, '--body')\n        ])\n\n    def test_httpie_relative_path(self):\n        tokens = self.get_tokens('httpie /api/test name==foo',\n                                 filter_spaces=False)\n        self.assertEqual(tokens, [\n            (Keyword, 'httpie'), (Text, ' '),\n            (String, '/api/test'), (Text, ' '),\n            (Name, 'name'), (Operator, '=='), (String, 'foo'),\n            (Text, '\\n')\n        ])\n\n\nclass TestShellCode(LexerTestCase):\n\n    def test_unquoted_querystring(self):\n        self.assertEqual(self.get_tokens('`echo name`==john'), [\n            (Text, '`'),\n            (Name.Builtin, 'echo'),\n            (Text, 'name'),\n            (Text, '`'),\n            (Operator, '=='),\n            (String, 'john')\n        ])\n        self.assertEqual(self.get_tokens('name==`echo john`'), [\n            (Name, 'name'),\n            (Operator, '=='),\n            (Text, '`'),\n            (Name.Builtin, 'echo'),\n            (Text, 'john'),\n            (Text, '`')\n        ])\n\n    def test_unquoted_bodystring(self):\n        self.assertEqual(self.get_tokens('`echo name`=john'), [\n            (Text, '`'),\n            (Name.Builtin, 'echo'),\n            (Text, 'name'),\n            (Text, '`'),\n            (Operator, '='),\n            (String, 'john')\n        ])\n        self.assertEqual(self.get_tokens('name=`echo john`'), [\n            (Name, 'name'),\n            (Operator, '='),\n            (Text, '`'),\n            (Name.Builtin, 'echo'),\n            (Text, 'john'),\n            (Text, '`')\n        ])\n\n    def test_header_option_value(self):\n        self.assertEqual(self.get_tokens('Accept:`echo \"application/json\"`'), [\n            (Name, 'Accept'),\n            (Operator, ':'),\n            (Text, '`'),\n            (Name.Builtin, 'echo'),\n            (String.Double, '\"application/json\"'),\n            (Text, '`'),\n        ])\n\n    def test_httpie_body_param(self):\n        self.assertEqual(self.get_tokens('httpie post name=`echo john`'), [\n            (Keyword, 'httpie'),\n            (Keyword, 'post'),\n            (Name, 'name'),\n            (Operator, '='),\n            (Text, '`'),\n            (Name.Builtin, 'echo'),\n            (Text, 'john'),\n            (Text, '`'),\n        ])\n\n    def test_httpie_post_pipe(self):\n        self.assertEqual(self.get_tokens('httpie post | tee \"/tmp/test\"'), [\n            (Keyword, 'httpie'),\n            (Keyword, 'post'),\n            (Operator, '|'),\n            (Text, 'tee'),\n            (String.Double, '\"/tmp/test\"'),\n        ])\n\n    def test_post_pipe(self):\n        self.assertEqual(self.get_tokens('post | tee \"/tmp/test\"'), [\n            (Keyword, 'post'),\n            (Operator, '|'),\n            (Text, 'tee'),\n            (String.Double, '\"/tmp/test\"'),\n        ])\n\n\nclass TestLexerPreviewRedirection(LexerTestCase):\n\n    def test_httpie_write(self):\n        self.assertEqual(self.get_tokens('httpie > file.txt'), [\n            (Keyword, 'httpie'),\n            (Operator, '>'), (String, 'file.txt')\n        ])\n\n    def test_httpie_write_without_spaces(self):\n        self.assertEqual(self.get_tokens('httpie>file.txt'), [\n            (Keyword, 'httpie'),\n            (Operator, '>'), (String, 'file.txt')\n        ])\n\n    def test_httpie_append(self):\n        self.assertEqual(self.get_tokens('httpie >> file.txt'), [\n            (Keyword, 'httpie'),\n            (Operator, '>>'), (String, 'file.txt')\n        ])\n\n    def test_httpie_append_without_spaces(self):\n        self.assertEqual(self.get_tokens('httpie>>file.txt'), [\n            (Keyword, 'httpie'),\n            (Operator, '>>'), (String, 'file.txt')\n        ])\n\n    def test_httpie_write_with_post_param(self):\n        self.assertEqual(self.get_tokens('httpie post name=jack > file.txt'), [\n            (Keyword, 'httpie'), (Keyword, 'post'),\n            (Name, 'name'), (Operator, '='), (String, 'jack'),\n            (Operator, '>'), (String, 'file.txt')\n        ])\n\n    def test_httpie_append_with_post_param(self):\n        self.assertEqual(self.get_tokens('httpie post name=doe >> file.txt'), [\n            (Keyword, 'httpie'), (Keyword, 'post'),\n            (Name, 'name'), (Operator, '='), (String, 'doe'),\n            (Operator, '>>'), (String, 'file.txt')\n        ])\n\n    def test_httpie_write_quoted_filename(self):\n        self.assertEqual(self.get_tokens(\"httpie > 'my file.txt'\"), [\n            (Keyword, 'httpie'), (Operator, '>'),\n            (Text, \"'\"), (String, 'my file.txt'), (Text, \"'\")\n        ])\n\n    def test_httpie_append_quoted_filename(self):\n        self.assertEqual(self.get_tokens('httpie >> \"my file.txt\"'), [\n            (Keyword, 'httpie'), (Operator, '>>'),\n            (Text, '\"'), (String, 'my file.txt'), (Text, '\"')\n        ])\n\n    def test_httpie_append_with_many_params(self):\n        command = (\"httpie post --auth user:pass --verify=no  \"\n                   \"name='john doe'  page==2 >> file.txt\")\n        self.assertEqual(self.get_tokens(command), [\n            (Keyword, 'httpie'), (Keyword, 'post'),\n            (Name, '--auth'), (String, 'user:pass'),\n            (Name, '--verify'), (Operator, '='), (String, 'no'),\n            (Name, 'name'), (Operator, '='),\n            (Text, \"'\"), (String, 'john doe'), (Text, \"'\"),\n            (Name, 'page'), (Operator, '=='), (String, '2'),\n            (Operator, '>>'), (String, 'file.txt')\n        ])\n\n    def test_curl_write(self):\n        self.assertEqual(self.get_tokens('curl > file.txt'), [\n            (Keyword, 'curl'),\n            (Operator, '>'), (String, 'file.txt')\n        ])\n\n    def test_curl_write_without_spaces(self):\n        self.assertEqual(self.get_tokens('curl>file.txt'), [\n            (Keyword, 'curl'),\n            (Operator, '>'), (String, 'file.txt')\n        ])\n\n    def test_curl_append(self):\n        self.assertEqual(self.get_tokens('curl >> file.txt'), [\n            (Keyword, 'curl'),\n            (Operator, '>>'), (String, 'file.txt')\n        ])\n\n    def test_curl_append_without_spaces(self):\n        self.assertEqual(self.get_tokens('curl>>file.txt'), [\n            (Keyword, 'curl'),\n            (Operator, '>>'), (String, 'file.txt')\n        ])\n\n    def test_curl_write_with_post_param(self):\n        self.assertEqual(self.get_tokens('curl post name=jack > file.txt'), [\n            (Keyword, 'curl'), (Keyword, 'post'),\n            (Name, 'name'), (Operator, '='), (String, 'jack'),\n            (Operator, '>'), (String, 'file.txt')\n        ])\n\n    def test_curl_append_with_post_param(self):\n        self.assertEqual(self.get_tokens('curl post name=doe >> file.txt'), [\n            (Keyword, 'curl'), (Keyword, 'post'),\n            (Name, 'name'), (Operator, '='), (String, 'doe'),\n            (Operator, '>>'), (String, 'file.txt')\n        ])\n\n    def test_curl_write_quoted_filename(self):\n        self.assertEqual(self.get_tokens(\"curl > 'my file.txt'\"), [\n            (Keyword, 'curl'), (Operator, '>'),\n            (Text, \"'\"), (String, 'my file.txt'), (Text, \"'\")\n        ])\n\n    def test_curl_append_quoted_filename(self):\n        self.assertEqual(self.get_tokens('curl >> \"my file.txt\"'), [\n            (Keyword, 'curl'), (Operator, '>>'),\n            (Text, '\"'), (String, 'my file.txt'), (Text, '\"')\n        ])\n\n    def test_curl_append_with_many_params(self):\n        command = (\"curl post --auth user:pass --verify=no  \"\n                   \"name='john doe'  page==2 >> file.txt\")\n        self.assertEqual(self.get_tokens(command), [\n            (Keyword, 'curl'), (Keyword, 'post'),\n            (Name, '--auth'), (String, 'user:pass'),\n            (Name, '--verify'), (Operator, '='), (String, 'no'),\n            (Name, 'name'), (Operator, '='),\n            (Text, \"'\"), (String, 'john doe'), (Text, \"'\"),\n            (Name, 'page'), (Operator, '=='), (String, '2'),\n            (Operator, '>>'), (String, 'file.txt')\n        ])\n\n\nclass TestLexerAction(LexerTestCase):\n\n    def test_get(self):\n        self.assertEqual(self.get_tokens('get'), [\n            (Keyword, 'get')\n        ])\n\n    def test_post_with_spaces(self):\n        self.assertEqual(self.get_tokens('   post  '), [\n            (Keyword, 'post')\n        ])\n\n    def test_capital_head(self):\n        self.assertEqual(self.get_tokens('HEAD'), [\n            (Keyword, 'HEAD')\n        ])\n\n    def test_delete_random_capitals(self):\n        self.assertEqual(self.get_tokens('dElETe'), [\n            (Keyword, 'dElETe')\n        ])\n\n    def test_patch(self):\n        self.assertEqual(self.get_tokens('patch'), [\n            (Keyword, 'patch')\n        ])\n\n    def test_get_with_querystring_params(self):\n        command = 'get page==10 id==200'\n        self.assertEqual(self.get_tokens(command), [\n            (Keyword, 'get'),\n            (Name, 'page'), (Operator, '=='), (String, '10'),\n            (Name, 'id'), (Operator, '=='), (String, '200')\n        ])\n\n    def test_capital_get_with_querystring_params(self):\n        command = 'GET page==10 id==200'\n        self.assertEqual(self.get_tokens(command), [\n            (Keyword, 'GET'),\n            (Name, 'page'), (Operator, '=='), (String, '10'),\n            (Name, 'id'), (Operator, '=='), (String, '200')\n        ])\n\n    def test_post_with_body_params(self):\n        command = 'post name=\"john doe\" username=john'\n        self.assertEqual(self.get_tokens(command), [\n            (Keyword, 'post'), (Name, 'name'), (Operator, '='),\n            (Text, '\"'), (String, 'john doe'), (Text, '\"'),\n            (Name, 'username'), (Operator, '='), (String, 'john')\n        ])\n\n    def test_post_with_spaces_and_body_params(self):\n        command = '  post   name=\"john doe\"     username=john  '\n        self.assertEqual(self.get_tokens(command), [\n            (Keyword, 'post'), (Name, 'name'), (Operator, '='),\n            (Text, '\"'), (String, 'john doe'), (Text, '\"'),\n            (Name, 'username'), (Operator, '='), (String, 'john')\n        ])\n\n    def test_options(self):\n        self.assertEqual(self.get_tokens('options'), [\n            (Keyword, 'options')\n        ])\n\n    def test_post_relative_path(self):\n        tokens = self.get_tokens('post /api/test name=foo',\n                                 filter_spaces=False)\n        self.assertEqual(tokens, [\n            (Keyword, 'post'), (Text, ' '),\n            (String, '/api/test'), (Text, ' '),\n            (Name, 'name'), (Operator, '='), (String, 'foo'),\n            (Text, '\\n')\n        ])\n\n\nclass TestLexerActionRedirection(LexerTestCase):\n\n    def test_get_write(self):\n        self.assertEqual(self.get_tokens('get > file.txt'), [\n            (Keyword, 'get'), (Operator, '>'), (String, 'file.txt')\n        ])\n\n    def test_get_write_quoted_filename(self):\n        self.assertEqual(self.get_tokens('get > \"/tmp/my file.txt\"'), [\n            (Keyword, 'get'), (Operator, '>'),\n            (Text, '\"'), (String, '/tmp/my file.txt'), (Text, '\"')\n        ])\n\n    def test_get_append(self):\n        self.assertEqual(self.get_tokens('get >> file.txt'), [\n            (Keyword, 'get'), (Operator, '>>'), (String, 'file.txt')\n        ])\n\n    def test_get_append_escaped_filename(self):\n        self.assertEqual(self.get_tokens(r'get >> /tmp/my\\ file.txt'), [\n            (Keyword, 'get'), (Operator, '>>'),\n            (String, r'/tmp/my\\ file.txt')\n        ])\n\n    def test_post_append_with_spaces(self):\n        self.assertEqual(self.get_tokens('   post  >>   file.txt'), [\n            (Keyword, 'post'), (Operator, '>>'), (String, 'file.txt')\n        ])\n\n    def test_capital_head_write(self):\n        self.assertEqual(self.get_tokens('HEAD > file.txt'), [\n            (Keyword, 'HEAD'), (Operator, '>'), (String, 'file.txt')\n        ])\n\n    def test_get_append_with_querystring_params(self):\n        command = 'get page==10 id==200 >> /tmp/file.txt'\n        self.assertEqual(self.get_tokens(command), [\n            (Keyword, 'get'),\n            (Name, 'page'), (Operator, '=='), (String, '10'),\n            (Name, 'id'), (Operator, '=='), (String, '200'),\n            (Operator, '>>'), (String, '/tmp/file.txt')\n        ])\n\n    def test_post_write_escaped_filename_with_body_params(self):\n        command = r'post name=\"john doe\" username=john > /tmp/my\\ file.txt'\n        self.assertEqual(self.get_tokens(command), [\n            (Keyword, 'post'), (Name, 'name'), (Operator, '='),\n            (Text, '\"'), (String, 'john doe'), (Text, '\"'),\n            (Name, 'username'), (Operator, '='), (String, 'john'),\n            (Operator, '>'), (String, r'/tmp/my\\ file.txt')\n        ])\n\n    def test_post_append_with_spaces_and_body_params(self):\n        command = ' post    name=\"john doe\"  username=john  >> /tmp/file.txt  '\n        self.assertEqual(self.get_tokens(command), [\n            (Keyword, 'post'), (Name, 'name'), (Operator, '='),\n            (Text, '\"'), (String, 'john doe'), (Text, '\"'),\n            (Name, 'username'), (Operator, '='), (String, 'john'),\n            (Operator, '>>'), (String, '/tmp/file.txt')\n        ])\n"
  },
  {
    "path": "tests/test_tree.py",
    "content": "import unittest\n\nfrom http_prompt.tree import Node\n\n\nclass TestNode(unittest.TestCase):\n\n    def setUp(self):\n        # Make a tree like this:\n        #          root\n        #     a             h\n        #  b     d        i   n\n        # c f   e g     k     o\n        #             l m p\n        self.root = Node('root')\n        self.root.add_path('a', 'b', 'c')\n        self.root.add_path('a', 'b', 'f')\n        self.root.add_path('a', 'd', 'e')\n        self.root.add_path('a', 'd', 'g')\n        self.root.add_path('h', 'i', 'k', 'l')\n        self.root.add_path('h', 'i', 'k', 'm')\n        self.root.add_path('h', 'i', 'k', 'p')\n        self.root.add_path('h', 'n', 'o')\n\n    def test_illegal_name(self):\n        self.assertRaises(ValueError, Node, '.')\n        self.assertRaises(ValueError, Node, '..')\n\n    def test_str(self):\n        node = Node('my node')\n        self.assertEqual(str(node), 'my node')\n\n    def test_cmp_same_type(self):\n        a = Node('a', data={'type': 'dir'})\n        b = Node('b', data={'type': 'dir'})\n        self.assertTrue(a < b)\n\n    def test_cmp_different_type(self):\n        a = Node('a', data={'type': 'file'})\n        b = Node('b', data={'type': 'dir'})\n        self.assertTrue(b < a)\n\n    def test_eq(self):\n        a = Node('a', data={'type': 'file'})\n        b = Node('b', data={'type': 'dir'})\n        self.assertNotEqual(a, b)\n\n        a = Node('a', data={'type': 'file'})\n        b = Node('a', data={'type': 'file'})\n        self.assertEqual(a, b)\n\n    def test_add_path_and_find_child(self):\n        # Level 1 (root)\n        self.assertEqual(set(c.name for c in self.root.children), set('ah'))\n\n        # Level 2\n        node_a = self.root.find_child('a')\n        node_h = self.root.find_child('h')\n        self.assertEqual(set(c.name for c in node_a.children), set('bd'))\n        self.assertEqual(set(c.name for c in node_h.children), set('in'))\n\n        # Level 3\n        node_b = node_a.find_child('b')\n        node_i = node_h.find_child('i')\n        self.assertEqual(set(c.name for c in node_b.children), set('cf'))\n        self.assertEqual(set(c.name for c in node_i.children), set('k'))\n\n        # Level 4\n        node_c = node_b.find_child('c')\n        node_k = node_i.find_child('k')\n        self.assertEqual(set(c.name for c in node_c.children), set())\n        self.assertEqual(set(c.name for c in node_k.children), set('lmp'))\n\n        # Return None if child can't be found\n        self.assertFalse(node_c.find_child('x'))\n\n    def test_find_child_wildcard(self):\n        root = Node('root')\n        root.add_path('a')\n        root.add_path('{b}')\n        root.add_path('c')\n\n        self.assertEqual(root.find_child('a').name, 'a')\n        self.assertEqual(root.find_child('c').name, 'c')\n        self.assertEqual(root.find_child('x').name, '{b}')\n        self.assertFalse(root.find_child('x', wildcard=False))\n\n    def test_ls(self):\n        self.assertEqual([n.name for n in self.root.ls('a')], list('bd'))\n        self.assertEqual([n.name for n in self.root.ls('a', 'b')], list('cf'))\n        self.assertEqual([n.name for n in self.root.ls('a', 'b', 'c')], [])\n        self.assertEqual([n.name for n in self.root.ls('h', 'i', 'k')],\n                         list('lmp'))\n\n    def test_ls_root(self):\n        self.assertEqual([n.name for n in self.root.ls()], list('ah'))\n\n    def test_ls_non_existing(self):\n        self.assertEqual([n.name for n in self.root.ls('x')], [])\n        self.assertEqual([n.name for n in self.root.ls('a', 'b', 'x')], [])\n\n    def test_ls_parent(self):\n        self.assertEqual([n.name for n in self.root.ls('..')], list('ah'))\n        self.assertEqual([n.name for n in self.root.ls('..', '..', '..')],\n                         list('ah'))\n        self.assertEqual([n.name for n in self.root.ls('..', '..', 'h')],\n                         list('in'))\n        self.assertEqual(\n            [n.name for n in self.root.ls('..', '..', 'h', '..', 'a')],\n            list('bd'))\n\n    def test_ls_dot(self):\n        self.assertEqual([n.name for n in self.root.ls('.')], list('ah'))\n        self.assertEqual([n.name for n in self.root.ls('.', '.', '.')],\n                         list('ah'))\n        self.assertEqual([n.name for n in self.root.ls('.', 'a', 'b')],\n                         list('cf'))\n        self.assertEqual([n.name for n in self.root.ls('.', 'h', '.')],\n                         list('in'))\n        self.assertEqual(\n            [n.name for n in self.root.ls('.', 'h', '.', '.', 'n')], ['o'])\n\n    def test_ls_sort_by_types(self):\n        self.root.add_path('q', 'r')\n        self.root.add_path('q', 's', node_type='file')\n        self.root.add_path('q', 't', node_type='file')\n        self.root.add_path('q', 'u')\n        self.root.add_path('q', 'v', node_type='file')\n\n        self.assertEqual([n.name for n in self.root.ls('q')],\n                         list('rustv'))\n"
  },
  {
    "path": "tests/test_utils.py",
    "content": "from http_prompt import utils\n\n\ndef test_colformat_zero_items():\n    assert list(utils.colformat([], terminal_width=80)) == []\n\n\ndef test_colformat_one_item():\n    assert list(utils.colformat(['hello'], terminal_width=80)) == ['hello']\n\n\ndef test_colformat_single_line():\n    items = ['hello', 'world', 'foo', 'bar']\n    assert list(utils.colformat(items, terminal_width=80)) == [\n        'hello world foo   bar'\n    ]\n\n\ndef test_colformat_single_column():\n    items = ['chap1.txt', 'chap2.txt', 'chap3.txt', 'chap4.txt',\n             'chap5.txt', 'chap6.txt', 'chap7.txt', 'chap8.txt']\n    assert list(utils.colformat(items, terminal_width=10)) == [\n        'chap1.txt', 'chap2.txt', 'chap3.txt', 'chap4.txt',\n        'chap5.txt', 'chap6.txt', 'chap7.txt', 'chap8.txt'\n    ]\n\n\ndef test_colformat_multi_columns_no_remainder():\n    items = ['chap1.txt', 'chap2.txt', 'chap3.txt', 'chap4.txt',\n             'chap5.txt', 'chap6.txt', 'chap7.txt', 'chap8.txt',\n             'chap9.txt', 'chap10.txt', 'chap11.txt', 'chap12.txt']\n    assert list(utils.colformat(items, terminal_width=50)) == [\n        'chap1.txt  chap4.txt  chap7.txt  chap10.txt',\n        'chap2.txt  chap5.txt  chap8.txt  chap11.txt',\n        'chap3.txt  chap6.txt  chap9.txt  chap12.txt'\n    ]\n\n\ndef test_colformat_multi_columns_remainder_1():\n    items = ['chap1.txt', 'chap2.txt', 'chap3.txt', 'chap4.txt',\n             'chap5.txt', 'chap6.txt', 'chap7.txt', 'chap8.txt',\n             'chap9.txt', 'chap10.txt', 'chap11.txt', 'chap12.txt',\n             'chap13.txt']\n    assert list(utils.colformat(items, terminal_width=50)) == [\n        'chap1.txt  chap5.txt  chap9.txt  chap13.txt',\n        'chap2.txt  chap6.txt  chap10.txt',\n        'chap3.txt  chap7.txt  chap11.txt',\n        'chap4.txt  chap8.txt  chap12.txt'\n    ]\n\n\ndef test_colformat_multi_columns_remainder_2():\n    items = ['chap1.txt', 'chap2.txt', 'chap3.txt', 'chap4.txt',\n             'chap5.txt', 'chap6.txt', 'chap7.txt', 'chap8.txt',\n             'chap9.txt', 'chap10.txt', 'chap11.txt', 'chap12.txt',\n             'chap13.txt', 'chap14.txt']\n    assert list(utils.colformat(items, terminal_width=50)) == [\n        'chap1.txt  chap5.txt  chap9.txt  chap13.txt',\n        'chap2.txt  chap6.txt  chap10.txt chap14.txt',\n        'chap3.txt  chap7.txt  chap11.txt',\n        'chap4.txt  chap8.txt  chap12.txt'\n    ]\n\n\ndef test_colformat_wider_than_terminal():\n    items = ['a very long long name', '1111 2222 3333 4444 5555']\n    assert list(utils.colformat(items, terminal_width=10)) == [\n        'a very long long name',\n        '1111 2222 3333 4444 5555'\n    ]\n\n\ndef test_colformat_long_short_mixed():\n    items = ['a', '1122334455667788', 'hello world', 'foo bar',\n             'b', '8877665544332211', 'abcd', 'yeah']\n    assert list(utils.colformat(items, terminal_width=50)) == [\n        'a                foo bar          abcd',\n        '1122334455667788 b                yeah',\n        'hello world      8877665544332211'\n    ]\n\n\ndef test_colformat_github_top_endpoints():\n    items = ['emojis', 'events', 'feeds', 'gists', 'gitignore', 'issues',\n             'legacy', 'markdown', 'meta', 'networks', 'notifications',\n             'orgs', 'rate_limit', 'repos', 'repositories', 'search',\n             'teams', 'user', 'users']\n    assert list(utils.colformat(items, terminal_width=136)) == [\n        'emojis        gists         legacy        networks      rate_limit''    search        users',  # noqa\n        'events        gitignore     markdown      notifications repos         teams',  # noqa\n        'feeds         issues        meta          orgs          repositories  user'  # noqa\n    ]\n"
  },
  {
    "path": "tests/test_xdg.py",
    "content": "import os\nimport stat\nimport sys\n\nfrom .base import TempAppDirTestCase\nfrom http_prompt import xdg\n\n\nclass TestXDG(TempAppDirTestCase):\n\n    def test_get_app_data_home(self):\n        path = xdg.get_data_dir()\n        expected_path = os.path.join(os.environ[self.homes['data']],\n                                     'http-prompt')\n        self.assertEqual(path, expected_path)\n        self.assertTrue(os.path.exists(path))\n\n        if sys.platform != 'win32':\n            # Make sure permission for the directory is 700\n            mask = stat.S_IMODE(os.stat(path).st_mode)\n            self.assertTrue(mask & stat.S_IRWXU)\n            self.assertFalse(mask & stat.S_IRWXG)\n            self.assertFalse(mask & stat.S_IRWXO)\n\n    def test_get_app_config_home(self):\n        path = xdg.get_config_dir()\n        expected_path = os.path.join(os.environ[self.homes['config']],\n                                     'http-prompt')\n        self.assertEqual(path, expected_path)\n        self.assertTrue(os.path.exists(path))\n\n        if sys.platform != 'win32':\n            # Make sure permission for the directory is 700\n            mask = stat.S_IMODE(os.stat(path).st_mode)\n            self.assertTrue(mask & stat.S_IRWXU)\n            self.assertFalse(mask & stat.S_IRWXG)\n            self.assertFalse(mask & stat.S_IRWXO)\n\n    def test_get_resource_data_dir(self):\n        path = xdg.get_data_dir('something')\n        expected_path = os.path.join(\n            os.environ[self.homes['data']], 'http-prompt', 'something')\n        self.assertEqual(path, expected_path)\n        self.assertTrue(os.path.exists(path))\n\n        # Make sure we can write a file to the directory\n        with open(os.path.join(path, 'test'), 'wb') as f:\n            f.write(b'hello')\n\n    def test_get_resource_config_dir(self):\n        path = xdg.get_config_dir('something')\n        expected_path = os.path.join(\n            os.environ[self.homes['config']], 'http-prompt', 'something')\n        self.assertEqual(path, expected_path)\n        self.assertTrue(os.path.exists(path))\n\n        # Make sure we can write a file to the directory\n        with open(os.path.join(path, 'test'), 'wb') as f:\n            f.write(b'hello')\n"
  },
  {
    "path": "tests/utils.py",
    "content": "import os\nimport sys\n\n\ndef get_http_prompt_path():\n    \"\"\"Get the path to http-prompt executable.\"\"\"\n    python_dir = os.path.dirname(sys.executable)\n    bin_name = 'http-prompt'\n    if sys.platform == 'win32':\n        bin_name += '.exe'\n\n    paths = [\n        os.path.join(python_dir, bin_name),\n        os.path.join(python_dir, 'Scripts', bin_name),  # Windows\n        '/usr/bin/http-prompt'  # Homebrew installation\n    ]\n    for path in paths:\n        if os.path.exists(path):\n            return path\n\n    raise OSError(\"could not locate http-prompt executable, \"\n                  \"Python directory: %s\" % python_dir)\n"
  },
  {
    "path": "tox.ini",
    "content": "# Tox (http://tox.testrun.org/) is a tool for running tests\n# in multiple virtualenvs. This configuration file will run the\n# test suite on all supported python versions. To use it, \"pip install tox\"\n# and then run \"tox\" from this directory.\n\n[tox]\nenvlist = py3{6,7,8,9,10}, pypy3\n\n[testenv]\ncommands = pytest\ndeps =\n    -rrequirements-test.txt\nsetenv =\n    LC_ALL = en_US.utf-8\n    LANG = en_US.utf-8\n\n[pytest]\ntestpaths = tests\naddopts = --cov-config .coveragerc --cov http_prompt\n"
  }
]