[
  {
    "path": ".changes/0.2.0.json",
    "content": "{\n  \"schema-version\": \"1.0\",\n  \"changes\": [\n    {\n      \"category\": \"Command History\",\n      \"description\": \"Ensure aws prefix is not prepended twice in command history. Fixes `#157 <https://github.com/awslabs/aws-shell/issues/157>`__\",\n      \"type\": \"bugfix\"\n    },\n    {\n      \"category\": \"Keybinding\",\n      \"description\": \"Switching between emacs/vi keybindings now functions properly\",\n      \"type\": \"bugfix\"\n    },\n    {\n      \"category\": \"Documentation\",\n      \"description\": \"The documentation pane can now be focused and navigated. Fixes `#74 <https://github.com/awslabs/aws-shell/issues/74>`__, `#159 <https://github.com/awslabs/aws-shell/issues/159>`__\",\n      \"type\": \"enhancement\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/0.2.1.json",
    "content": "{\n  \"schema-version\": \"1.0\",\n  \"changes\": [\n    {\n      \"category\": \"AWS CLI\",\n      \"description\": \"Fixes `#208 <https://github.com/awslabs/aws-shell/issues/208>`__. Update the AWS Shell to support the latest version of the AWS CLI.\",\n      \"type\": \"bugfix\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".changes/0.2.2.json",
    "content": "{\n  \"schema-version\": \"1.0\",\n  \"changes\": [\n    {\n      \"type\": \"bugfix\",\n      \"category\": \"Dependency\",\n      \"description\": \"Fix bcdoc import errors. Fixes `#247 <https://github.com/awslabs/aws-shell/issues/247>`__.\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".gitignore",
    "content": "*.py[co]\n*.DS_Store\n\n# Packages\n*.egg\n*.egg-info\ndist\nbuild\neggs\nparts\nvar\nsdist\ndevelop-eggs\n.installed.cfg\n\n# Installer logs\npip-log.txt\n\n# Unit test / coverage reports\n.coverage\n.tox\n.cache\n\n#Translations\n*.mo\n\n#Mr Developer\n.mr.developer.cfg\n\n# Emacs backup files\n*~\n\n# Eclipse IDE\n/.project\n/.pydevproject\n\n# IDEA IDE\n.idea*\nsrc/\n\n# Completions Index\ncompletions.idx\n"
  },
  {
    "path": ".pylintrc",
    "content": "[MASTER]\n\n# Specify a configuration file.\n#rcfile=\n\n# Python code to execute, usually for sys.path manipulation such as\n# pygtk.require().\n#init-hook=\n\n# Add files or directories to the blacklist. They should be base names, not\n# paths.\nignore=compat.py\n\n# Pickle collected data for later comparisons.\npersistent=yes\n\n# List of plugins (as comma separated values of python modules names) to load,\n# usually to register additional checkers.\nload-plugins=\n\n# Use multiple processes to speed up Pylint.\njobs=1\n\n# Allow loading of arbitrary C extensions. Extensions are imported into the\n# active Python interpreter and may run arbitrary code.\nunsafe-load-any-extension=no\n\n# A comma-separated list of package or module names from where C extensions may\n# be loaded. Extensions are loading into the active Python interpreter and may\n# run arbitrary code\nextension-pkg-whitelist=\n\n# Allow optimization of some AST trees. This will activate a peephole AST\n# optimizer, which will apply various small optimizations. For instance, it can\n# be used to obtain the result of joining multiple strings with the addition\n# operator. Joining a lot of strings can lead to a maximum recursion error in\n# Pylint and this flag can prevent that. It has one side effect, the resulting\n# AST will be different than the one from reality.\noptimize-ast=no\n\n\n[MESSAGES CONTROL]\n\n# Only show warnings with the listed confidence levels. Leave empty to show\n# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED\nconfidence=\n\n# Enable the message, report, category or checker with the given id(s). You can\n# either give multiple identifier separated by comma (,) or put this option\n# multiple time. See also the \"--disable\" option for examples.\n#enable=\n\n# Disable the message, report, category or checker with the given id(s). You\n# can either give multiple identifiers separated by comma (,) or put this\n# option multiple times (only on the command line, not in the configuration\n# file where it should appear only once).You can also use \"--disable=all\" to\n# disable everything first and then reenable specific checks. For example, if\n# you want to run only the similarities checker, you can use \"--disable=all\n# --enable=similarities\". If you want to run only the classes checker, but have\n# no Warning level messages displayed, use\"--disable=all --enable=classes\n# --disable=W\"\ndisable=E1608,R0201,W1627,E1601,E1603,E1602,E1605,E1604,E1607,E1606,W1621,W1620,W1623,W1622,W1625,W1624,W1609,W1608,W1607,W1606,W1605,W1604,W1603,W1602,W1601,W1639,W0613,W1640,I0021,W1638,I0020,C0111,W1618,W1619,W1630,W1626,W1637,W1634,W1635,W1610,W1611,W1612,W1613,W1614,W1615,W1616,W1617,W1632,R0903,W1633,W0231,W0704,W0232,W1628,W1629,W1636\n\n\n[REPORTS]\n\n# Set the output format. Available formats are text, parseable, colorized, msvs\n# (visual studio) and html. You can also give a reporter class, eg\n# mypackage.mymodule.MyReporterClass.\noutput-format=text\n\n# Put messages in a separate file for each module / package specified on the\n# command line instead of printing them on stdout. Reports (if any) will be\n# written in a file name \"pylint_global.[txt|html]\".\nfiles-output=no\n\n# Tells whether to display a full report or only the messages\nreports=no\n\n# Python expression which should return a note less than 10 (10 is the highest\n# note). You have access to the variables errors warning, statement which\n# respectively contain the number of errors / warnings messages and the total\n# number of statements analyzed. This is used by the global evaluation report\n# (RP0004).\nevaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)\n\n# Template used to display messages. This is a python new-style format string\n# used to format the message information. See doc for all details\n#msg-template=\n\n\n[BASIC]\n\n# List of builtins function names that should not be used, separated by a comma\nbad-functions=apply,reduce\n\n# Good variable names which should always be accepted, separated by a comma\ngood-names=e,i,j,k,n,ex,Run,_\n\n# Bad variable names which should always be refused, separated by a comma\nbad-names=foo,bar,baz,toto,tutu,tata\n\n# Colon-delimited sets of names that determine each other's naming style when\n# the name regexes allow several styles.\nname-group=\n\n# Include a hint for the correct naming format with invalid-name\ninclude-naming-hint=no\n\n# Regular expression matching correct function names\nfunction-rgx=[a-z_][a-z0-9_]{2,50}$\n\n# Naming hint for function names\nfunction-name-hint=[a-z_][a-z0-9_]{2,30}$\n\n# Regular expression matching correct variable names\nvariable-rgx=[a-z_][a-z0-9_]{0,50}$\n\n# Naming hint for variable names\nvariable-name-hint=[a-z_][a-z0-9_]{2,30}$\n\n# Regular expression matching correct constant names\nconst-rgx=(([a-zA-Z_][a-zA-Z0-9_]*)|(__.*__))$\n\n# Naming hint for constant names\nconst-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$\n\n# Regular expression matching correct attribute names\nattr-rgx=[a-z_][a-z0-9_]{1,50}$\n\n# Naming hint for attribute names\nattr-name-hint=[a-z_][a-z0-9_]{2,30}$\n\n# Regular expression matching correct argument names\nargument-rgx=[a-z_][a-z0-9_]{0,50}$\n\n# Naming hint for argument names\nargument-name-hint=[a-z_][a-z0-9_]{2,30}$\n\n# Regular expression matching correct class attribute names\nclass-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$\n\n# Naming hint for class attribute names\nclass-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$\n\n# Regular expression matching correct inline iteration names\ninlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$\n\n# Naming hint for inline iteration names\ninlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$\n\n# Regular expression matching correct class names\nclass-rgx=[A-Z_][a-zA-Z0-9]+$\n\n# Naming hint for class names\nclass-name-hint=[A-Z_][a-zA-Z0-9]+$\n\n# Regular expression matching correct module names\nmodule-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$\n\n# Naming hint for module names\nmodule-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$\n\n# Regular expression matching correct method names\nmethod-rgx=[a-z_][a-z0-9_]{2,50}$\n\n# Naming hint for method names\nmethod-name-hint=[a-z_][a-z0-9_]{2,30}$\n\n# Regular expression which should only match function or class names that do\n# not require a docstring.\nno-docstring-rgx=.*\n\n# Minimum line length for functions/classes that require docstrings, shorter\n# ones are exempt.\ndocstring-min-length=-1\n\n\n[FORMAT]\n\n# Maximum number of characters on a single line.\nmax-line-length=80\n\n# Regexp for a line that is allowed to be longer than the limit.\nignore-long-lines=^\\s*(# )?<?https?://\\S+>?$\n\n# Allow the body of an if to be on the same line as the test if there is no\n# else.\nsingle-line-if-stmt=no\n\n# List of optional constructs for which whitespace checking is disabled\nno-space-check=trailing-comma,dict-separator\n\n# Maximum number of lines in a module\nmax-module-lines=1000\n\n# String used as indentation unit. This is usually \" \" (4 spaces) or \"\\t\" (1\n# tab).\nindent-string='    '\n\n# Number of spaces of indent required inside a hanging or continued line.\nindent-after-paren=4\n\n# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.\nexpected-line-ending-format=\n\n\n[LOGGING]\n\n# Logging modules to check that the string format arguments are in logging\n# function parameter format\nlogging-modules=logging\n\n\n[MISCELLANEOUS]\n\n# List of note tags to take in consideration, separated by a comma.\nnotes=FIXME,XXX\n\n\n[SIMILARITIES]\n\n# Minimum lines number of a similarity.\nmin-similarity-lines=3\n\n# Ignore comments when computing similarities.\nignore-comments=no\n\n# Ignore docstrings when computing similarities.\nignore-docstrings=no\n\n# Ignore imports when computing similarities.\nignore-imports=no\n\n\n[SPELLING]\n\n# Spelling dictionary name. Available dictionaries: none. To make it working\n# install python-enchant package.\nspelling-dict=\n\n# List of comma separated words that should not be checked.\nspelling-ignore-words=\n\n# A path to a file that contains private dictionary; one word per line.\nspelling-private-dict-file=\n\n# Tells whether to store unknown words to indicated private dictionary in\n# --spelling-private-dict-file option instead of raising a message.\nspelling-store-unknown-words=no\n\n\n[TYPECHECK]\n\n# Tells whether missing members accessed in mixin class should be ignored. A\n# mixin class is detected if its name ends with \"mixin\" (case insensitive).\nignore-mixin-members=yes\n\n# List of module names for which member attributes should not be checked\n# (useful for modules/projects where namespaces are manipulated during runtime\n# and thus existing member attributes cannot be deduced by static analysis\nignored-modules=\n\n# List of classes names for which member attributes should not be checked\n# (useful for classes with attributes dynamically set).\nignored-classes=SQLObject\n\n# List of members which are set dynamically and missed by pylint inference\n# system, and so shouldn't trigger E0201 when accessed. Python regular\n# expressions are accepted.\ngenerated-members=REQUEST,acl_users,aq_parent,objects,DoesNotExist,md5,sha1,sha224,sha256,sha384,sha512\n\n\n[VARIABLES]\n\n# Tells whether we should check for unused import in __init__ files.\ninit-import=no\n\n# A regular expression matching the name of dummy variables (i.e. expectedly\n# not used).\ndummy-variables-rgx=_|dummy|ignore\n\n# List of additional names supposed to be defined in builtins. Remember that\n# you should avoid to define new builtins when possible.\nadditional-builtins=\n\n# List of strings which can identify a callback function by name. A callback\n# name must start or end with one of those strings.\ncallbacks=cb_,_cb\n\n\n[CLASSES]\n\n# List of method names used to declare (i.e. assign) instance attributes.\ndefining-attr-methods=__init__,__new__,setUp\n\n# List of valid names for the first argument in a class method.\nvalid-classmethod-first-arg=cls\n\n# List of valid names for the first argument in a metaclass class method.\nvalid-metaclass-classmethod-first-arg=mcs\n\n# List of member names, which should be excluded from the protected access\n# warning.\nexclude-protected=_asdict,_fields,_replace,_source,_make\n\n\n[DESIGN]\n\n# Maximum number of arguments for function / method\nmax-args=5\n\n# Argument names that match this expression will be ignored. Default to name\n# with leading underscore\nignored-argument-names=_.*\n\n# Maximum number of locals for function / method body\nmax-locals=15\n\n# Maximum number of return / yield for function / method body\nmax-returns=6\n\n# Maximum number of branch for function / method body\nmax-branches=12\n\n# Maximum number of statements in function / method body\nmax-statements=30\n\n# Maximum number of parents for a class (see R0901).\nmax-parents=5\n\n# Maximum number of attributes for a class (see R0902).\nmax-attributes=7\n\n# Minimum number of public methods for a class (see R0903).\nmin-public-methods=0\n\n# Maximum number of public methods for a class (see R0904).\nmax-public-methods=20\n\n\n[IMPORTS]\n\n# Deprecated modules which should not be used, separated by a comma\ndeprecated-modules=regsub,string,TERMIOS,Bastion,rexec,UserDict\n\n# Create a graph of every (i.e. internal and external) dependencies in the\n# given file (report RP0402 must not be disabled)\nimport-graph=\n\n# Create a graph of external dependencies in the given file (report RP0402 must\n# not be disabled)\next-import-graph=\n\n# Create a graph of internal dependencies in the given file (report RP0402 must\n# not be disabled)\nint-import-graph=\n\n\n[EXCEPTIONS]\n\n# Exceptions that will emit a warning when being caught. Defaults to\n# \"Exception\"\novergeneral-exceptions=Exception\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: python\n\nmatrix:\n    include:\n        - python: 2.7\n          env: TEST_TYPE=test\n        - python: 2.7\n          env: TEST_TYPE=check\n        - python: 3.4\n          env: TEST_TYPE=test\n        - python: 3.5\n          env: TEST_TYPE=test\n        - python: 3.6\n          env: TEST_TYPE=test\n\nsudo: false\ninstall:\n  - if [[ $TEST_TYPE == 'check' ]]; then pip install -r requirements-dev.txt; fi\n  - python scripts/ci/install\nscript:\n  - make $TEST_TYPE\n"
  },
  {
    "path": "CHANGELOG.rst",
    "content": "=========\nCHANGELOG\n=========\n\n0.2.2\n=====\n\n* bugfix:Dependency: Fix bcdoc import errors. Fixes `#247 <https://github.com/awslabs/aws-shell/issues/247>`__.\n\n0.2.1\n=====\n\n* bugfix:AWS CLI: Fixes `#208 <https://github.com/awslabs/aws-shell/issues/208>`__. Update the AWS Shell to support the latest version of the AWS CLI.\n\n0.2.0\n=====\n\n* bugfix:Command History: Ensure aws prefix is not prepended twice in command history.\n  Fixes `#157 <https://github.com/awslabs/aws-shell/issues/157>`__\n* bugfix:Keybinding: Switching between emacs/vi keybindings now functions properly\n* enhancement:Documentation: The documentation pane can now be focused and navigated.\n  Fixes `#74 <https://github.com/awslabs/aws-shell/issues/74>`__, `#159 <https://github.com/awslabs/aws-shell/issues/159>`__\n\n0.1.1\n=====\n\n* bugfix:AWS CLI: Fix issue with latest version of the AWS CLI\n  that would cause the AWS Shell to raise an exception on startup.\n  The minimum version of the AWS CLI has been bumped to 1.10.30.\n  (`issue 118 <https://github.com/awslabs/aws-shell/issues/118>`__)\n\n0.1.0\n=====\n\n* feature:Dot Commands: Add ``.exit/.quit`` dot commands\n  (`issue 97 <https://github.com/awslabs/aws-shell/pull/97>`__)\n* feature:Documentation: Show documentation for global arguments\n  (`issue 51 <https://github.com/awslabs/aws-shell/issues/51>`__)\n* feature:Dot Commands: Add ``.cd`` dot command\n  (`issue 97 <https://github.com/awslabs/aws-shell/issues/76>`__)\n* feature:Dot Commands: Add ``.profile`` dot command\n  (`issue 97 <https://github.com/awslabs/aws-shell/issues/9>`__)\n* feature:Command Line Arguments: Add ``--profile`` command line\n  option (`issue 89 <https://github.com/awslabs/aws-shell/issues/89>`__)\n* bugfix:Completer: Fix crash when attempting server side completion\n  with no region configured option\n  (`issue 84 <https://github.com/awslabs/aws-shell/issues/84>`__)\n* feature:Lexer: Add lexer/syntax highlighting\n  (`issue 27 <https://github.com/awslabs/aws-shell/issues/27>`__)\n* feature:Server Side Completion: Add server side completion for\n  Elastic Load Balancing\n  (`issue 79 <https://github.com/awslabs/aws-shell/pull/79>`__)\n* feature:Server Side Completion: Add server side completion for\n  Amazon Kinesis\n  (`issue 73 <https://github.com/awslabs/aws-shell/pull/73>`__)\n* bugfix:Windows: Fix crash when using ``.edit`` on Windows\n  (`issue 55 <https://github.com/awslabs/aws-shell/pull/55>`__)\n"
  },
  {
    "path": "LICENSE.txt",
    "content": "\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "MANIFEST.in",
    "content": "include README.rst\ninclude LICENSE.txt\ninclude NOTICE.txt\ninclude awsshell/awsshellrc\nrecursive-include awsshell/data *.json\ngraft tests\n"
  },
  {
    "path": "Makefile",
    "content": "# Eventually I'll add:\n# py.test --cov awsshell --cov-report term-missing --cov-fail-under 95 tests/\n# which will fail if tests are under 95%\n\ncheck:\n\t###### FLAKE8 #####\n\t# No unused imports, no undefined vars,\n\t# follow pep8, and max cyclomatic complexity of 15.\n\t# I'd eventually like to lower this down to < 10.\n\tflake8 --ignore=E731,W503 --exclude awsshell/compat.py --max-complexity 15 awsshell/*.py\n\t#\n\t#\n\t##### DOC8 ######\n\t# Correct rst formatting for docstrings\n\t#\n\t##\n\tdoc8 awsshell/*.py\n\t#\n\t#\n\t#\n\t# Proper docstring conventions according to pep257\n\t#\n\t#\n\tpep257 --add-ignore=D100,D101,D102,D103,D104,D105,D204\n\t#\n\t#\n\t#\n\t###### PYLINT ERRORS ONLY ######\n\t#\n\t#\n\t#\n\tpylint --rcfile .pylintrc -E awsshell\n\ntest:\n\tpython scripts/ci/run-tests\n\npylint:\n\t###### PYLINT ######\n\t# Python linter.  This will generally not have clean output.\n\t# So you'll need to manually verify this output.\n\t#\n\t#\n\tpylint --rcfile .pylintrc awsshell\n\ncoverage:\n\tpy.test --cov awsshell --cov-report term-missing tests/\n"
  },
  {
    "path": "NOTICE.txt",
    "content": "AWS Shell\nCopyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n"
  },
  {
    "path": "README.rst",
    "content": "aws-shell - The interactive productivity booster for the AWS CLI\n================================================================\n\n.. image:: https://aws-developer-blog-media.s3-us-west-2.amazonaws.com/cli/Super-Charge-Your-AWS-Command-Line-Experience-with-aws-shell/aws-shell-final.gif\n\n\nInstallation\n============\n\nThe aws-shell requires python and `pip`_ to install.\nYou can install the aws-shell using `pip`_::\n\n    $ pip install aws-shell\n\nIf you are not installing into a virtualenv you can run::\n\n    $ sudo pip install aws-shell\n\n**Mac OS X (10.11 El Capitan) users**: There is a known issue with Apple and\nits included python package dependencies (more info at\nhttps://github.com/pypa/pip/issues/3165).\nWe are investigating ways to fix this issue but in the meantime,\nto install the aws-shell, you can run:\n``sudo pip install aws-shell --upgrade --ignore-installed six``\n\nOnce you've installed the aws-shell, you can now run::\n\n    $ aws-shell\n\nTo exit the shell, press ``Ctrl-D``.\n\nUpgrading the aws-shell\n-----------------------\n\nIf you want to upgrade to the latest version of the aws-shell,\nyou can run::\n\n    $ pip install --upgrade aws-shell\n\nYou can also use this upgrade command whenever a new version of the AWS CLI is\nreleased that includes new services and API updates.  You will then be\nable to use these new services and API updates in the aws-shell.\n\nSupported Python Versions\n-------------------------\n\nThe aws-shell works on the same python versions supported by the AWS CLI:\n\n* 2.6.5 and greater\n* 2.7.x and greater\n* 3.3.x and greater\n* 3.4.x and greater\n\n\nConfiguration\n=============\n\nThe aws-shell uses the same configuration settings as the AWS CLI.\nIf you've never used the AWS CLI before, the easiest way to get\nstarted is to run the ``configure`` command::\n\n    $ aws-shell\n    aws> configure\n    AWS Access Key ID [None]: your-access-key-id\n    AWS Secret Access Key [None]: your-secret-access-key\n    Default region name [None]: region-to-use (e.g us-west-2, us-west-1, etc).\n    Default output format [None]:\n    aws>\n\nFor more information about configure settings, see the\n`AWS CLI Getting Started Guide`_.\n\nBasic Usage\n===========\n\nThe aws-shell accepts the same commands as the AWS CLI, except you don't\nneed to provide the ``aws`` prefix.  For example, here are a few commands\nyou can try::\n\n\n    $ aws-shell\n    aws> ec2 describe-regions\n    {\n        \"Regions\": [\n            {\n                \"Endpoint\": \"ec2.eu-west-1.amazonaws.com\",\n                \"RegionName\": \"eu-west-1\"\n            },\n            ...\n    aws> s3 ls\n    2015-12-07 15:03:34 bucket1\n    2015-12-07 15:03:34 bucket2\n    aws> dynamodb list-tables --output text\n    TABLENAMES     First\n    TABLENAMES     Second\n    TABLENAMES     Third\n\nProfiles\n--------\n\nThe aws-shell supports AWS CLI profiles.  You have two options to use\nprofiles.  First, you can provide a profile when you start the aws-shell::\n\n    $ aws-shell --profile prod\n    aws>\n\nWhen you do this all the server side completion as well as CLI commands\nyou run will automatically use the ``prod`` profile.\n\nYou can also change the current profile while you're in the aws-shell::\n\n    $ aws-shell\n    aws> .profile demo\n    Current shell profile changed to: demo\n\nYou can also check what profile you've configured in the aws-shell using::\n\n    aws> .profile\n    Current shell profile: demo\n\nAfter changing your profile using the ``.profile`` dot command, all\nserver side completion as well as CLI commands will automatically use\nthe new profile you've configured.\n\n\nFeatures\n========\n\nAuto Completion of Commands and Options\n---------------------------------------\n\nThe aws-shell provides auto completion of commands and\noptions as you type.\n\n\n.. image:: https://cloud.githubusercontent.com/assets/368057/11824078/784a613e-a32c-11e5-8ac5-f1d1873cc643.png\n\n\nShorthand Auto Completion\n-------------------------\n\nThe aws-shell can also fill in an example of the\nshorthand syntax used for various AWS CLI options:\n\n.. image:: https://cloud.githubusercontent.com/assets/368057/11823453/e95d85da-a328-11e5-8b8d-67566eccf9e3.png\n\n\nServer Side Auto Completion\n---------------------------\n\nThe aws-shell also leverages `boto3`_, the AWS SDK for Python, to auto complete\nserver side resources such as Amazon EC2 instance Ids, Amazon Dynamodb table\nnames, AWS IAM user names, Amazon S3 bucket names, etc.\n\nThis feature is under active development.  The list of supported resources\ncontinues to grow.\n\n.. image:: https://cloud.githubusercontent.com/assets/368057/11824022/3648b4fc-a32c-11e5-8e18-92f028eb1cee.png\n\n\nFuzzy Searching\n---------------\n\nEvery auto completion value supports fuzzy searching.  This enables you to\nspecify the commands, options, and values you want to run with even less\ntyping.  You can try typing:\n\n* The first letter of each sub word: ``ec2 describe-reserved-instances-offerings``\n  -> ``ec2 drio``\n* A little bit of each word: ``ec2 describe-instances`` -> ``ec2 descinst``\n* Any part of the command: ``dynamodb table`` -> Offers all commands that\n  contain the subsequence ``table``.\n\n\n.. image:: https://cloud.githubusercontent.com/assets/368057/11823996/18e69d16-a32c-11e5-80a2-defbaa6a8a80.png\n\nInline Documentation\n--------------------\n\nThe aws-shell will automatically pull up documentation as you type commands.\nIt will show inline documentation for CLI options.  There is also a separate\ndocumentation panel that will show documentation for the current command or\noption you are typing. Pressing F9 will toggle focus to the documentation panel\nallowing you to navigate it using your selected keybindings.\n\n\n.. image:: https://cloud.githubusercontent.com/assets/368057/11823320/36ae9b04-a328-11e5-9661-81abfc0afe5a.png\n\n\nFish-Style Auto Suggestions\n---------------------------\n\nThe aws-shell supports Fish-style auto-suggestions. Use the right arrow key to\ncomplete a suggestion.\n\n.. image:: https://cloud.githubusercontent.com/assets/368057/11822961/4bceff94-a326-11e5-87fa-c664e1e82be4.png\n\nCommand History\n---------------\n\nThe aws-shell records the commands you run and writes them to\n``~/.aws/shell/history``.  You can use the up and down arrow keys to scroll\nthrough your history.\n\n.. image:: https://cloud.githubusercontent.com/assets/368057/11823211/b5851e9a-a327-11e5-877f-687dc1f90e27.png\n\nToolbar Options\n---------------\n\nThe aws-shell has a bottom toolbar that provides several options:\n\n* ``F2`` toggles between fuzzy and substring matching\n* ``F3`` toggles between VI and Emacs key bindings\n* ``F4`` toggles between single and multi column auto completions\n* ``F5`` shows and hides the help documentation pane\n* ``F9`` toggles focus between the cli and documentation pane\n* ``F10`` or ``Ctrl-D`` exits the aws-shell\n\nAs you toggle options in the toolbar, your preferences are persisted\nto the ``~/.aws/shell/awsshellrc`` file so that the next time you run\nthe aws-shell, your preferences will be restored.\n\n.. image:: https://cloud.githubusercontent.com/assets/368057/11823907/8c3f1e60-a32b-11e5-9f99-fe504ea0a5dc.png\n\nDot Commands\n------------\n\nThe aws-shell provides additional commands specific to the aws-shell.\nThe commands are available by adding the ``.`` prefix before a command.\n\nExiting the Shell\n~~~~~~~~~~~~~~~~~\nYou can run the ``.exit`` or ``.quit`` commands to exit the shell.\n\nCreating Shell Scripts with .edit\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThere are times when you may want to take a sequence of commands\nyou've run in the aws-shell and combine them into a shell script.\nIn addition to the command history that's persisted to the\nhistory file, the aws-shell also keeps track of all the commands\nyou've run since you first started your aws-shell session.\n\nYou can run the ``.edit`` command to open all these commands in\nan editor.  The aws-shell will use the ``EDITOR`` environment\nvariable before defaulting to ``notepad`` on Windows and\n``vi`` on other platforms.\n\n::\n\n    aws> ec2 describe-instances\n    aws> dynamodb list-tables\n    aws> .edit\n\nChanging Profiles with .profile\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nYou can change the current AWS CLI profile used by the aws-shell\nby using the ``.profile`` dot command.  If you run the ``.profile``\ncommand with no arguments, the currently configured shell profile\nwill be printed.\n\n::\n\n    aws> .profile demo\n    Current shell profile changed to: demo\n    aws> .profile\n    Current shell profile: demo\n\n\n.cd\n~~~\n\nYou can change the current working directory of the aws-shell by using\nthe ``.cd`` command::\n\n    aws> !pwd\n    /usr\n    aws> .cd /tmp\n    aws> !pwd\n    /tmp\n\n\nExecuting Shell Commands\n------------------------\n\nThe aws-shell integrates with other commands in several ways.\nFirst, you can pipe AWS CLI commands to other processes as well\nas redirect output to a file::\n\n    aws> dynamodb list-tables --output text | head -n 1\n    TABLENAMES     First\n    aws> dynamodb list-tables --output text > /tmp/foo.txt\n\nSecond, if you want to run a shell command rather than an AWS CLI\ncommand, you can add the ``!`` prefix to your command::\n\n    aws> !ls /tmp/\n    foo.txt                                    bar.txt\n\nDeveloper Preview Status\n========================\n\nThe aws-shell is currently in developer preview.\nWe welcome feedback, feature requests, and bug reports.\nThere may be backwards incompatible changes made in order\nto respond to customer feedback as we continue to iterate\non the aws-shell.\n\n\nMore Information\n================\n\nBelow are miscellaneous links for more information:\n\n* `AWS CLI Reference Docs`_\n* `AWS CLI User Guide`_\n* `AWS CLI Blog`_\n* `AWS CLI Github Repo`_\n\n.. _pip: http://www.pip-installer.org/en/latest/\n.. _AWS CLI Getting Started Guide: http://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html\n.. _boto3: https://github.com/boto/boto3\n.. _AWS CLI Reference Docs: http://docs.aws.amazon.com/cli/latest/reference/\n.. _AWS CLI User Guide: http://docs.aws.amazon.com/cli/latest/userguide/\n.. _AWS CLI Blog: https://blogs.aws.amazon.com/cli/\n.. _AWS CLI Github Repo: https://github.com/aws/aws-cli\n"
  },
  {
    "path": "awsshell/__init__.py",
    "content": "from __future__ import unicode_literals, print_function\n\nimport json\nimport argparse\nimport threading\n\nfrom awsshell import shellcomplete\nfrom awsshell import autocomplete\nfrom awsshell import app\nfrom awsshell import docs\nfrom awsshell import loaders\nfrom awsshell.index import completion\nfrom awsshell import utils\n\n\n__version__ = '0.2.2'\n\n\ndef determine_doc_index_filename():\n    import awscli\n    base = loaders.JSONIndexLoader.index_filename(\n        awscli.__version__)\n    return base + '.docs'\n\n\ndef load_index(filename):\n    load = loaders.JSONIndexLoader()\n    return load.load_index(filename)\n\n\ndef main():\n    parser = argparse.ArgumentParser()\n    parser.add_argument('-p', '--profile', help='The profile name to use '\n                        'when starting the AWS Shell.')\n    args = parser.parse_args()\n\n    indexer = completion.CompletionIndex()\n    try:\n        index_str = indexer.load_index(utils.AWSCLI_VERSION)\n        index_data = json.loads(index_str)\n    except completion.IndexLoadError:\n        print(\"First run, creating autocomplete index...\")\n        from awsshell.makeindex import write_index\n        # TODO: Using internal method, but this will eventually\n        # be moved into the CompletionIndex class anyways.\n        index_file = indexer._filename_for_version(utils.AWSCLI_VERSION)\n        write_index(index_file)\n        index_str = indexer.load_index(utils.AWSCLI_VERSION)\n        index_data = json.loads(index_str)\n    doc_index_file = determine_doc_index_filename()\n    from awsshell.makeindex import write_doc_index\n    doc_data = docs.load_lazy_doc_index(doc_index_file)\n    # There's room for improvement here.  If the docs didn't finish\n    # generating, we regen the whole doc index.  Ideally we pick up\n    # from where we left off.\n    try:\n        docs.load_doc_db(doc_index_file)['__complete__']\n    except KeyError:\n        print(\"Creating doc index in the background. \"\n              \"It will be a few minutes before all documentation is \"\n              \"available.\")\n        t = threading.Thread(target=write_doc_index, args=(doc_index_file,))\n        t.daemon = True\n        t.start()\n    model_completer = autocomplete.AWSCLIModelCompleter(index_data)\n    completer = shellcomplete.AWSShellCompleter(model_completer)\n    shell = app.create_aws_shell(completer, model_completer, doc_data)\n    if args.profile:\n        shell.profile = args.profile\n    shell.run()\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "awsshell/app.py",
    "content": "\"\"\"AWS Shell application.\n\nMain entry point to the AWS Shell.\n\n\"\"\"\nfrom __future__ import unicode_literals\nimport os\nimport subprocess\nimport logging\nimport sys\n\nfrom prompt_toolkit.document import Document\nfrom prompt_toolkit.shortcuts import create_eventloop\nfrom prompt_toolkit.buffer import Buffer\nfrom prompt_toolkit.filters import Always\nfrom prompt_toolkit.interface import CommandLineInterface, Application\nfrom prompt_toolkit.interface import AbortAction, AcceptAction\nfrom prompt_toolkit.auto_suggest import AutoSuggestFromHistory\nfrom prompt_toolkit.history import InMemoryHistory, FileHistory\nfrom prompt_toolkit.enums import EditingMode\n\nfrom awsshell.ui import create_default_layout\nfrom awsshell.config import Config\nfrom awsshell.keys import KeyManager\nfrom awsshell.style import StyleFactory\nfrom awsshell.toolbar import Toolbar\nfrom awsshell.utils import build_config_file_path, temporary_file\nfrom awsshell import compat\n\n\nLOG = logging.getLogger(__name__)\nEXIT_REQUESTED = object()\n\n\ndef create_aws_shell(completer, model_completer, docs):\n    return AWSShell(completer, model_completer, docs)\n\n\nclass InputInterrupt(Exception):\n    \"\"\"Stops the input of commands.\n\n    Raising `InputInterrupt` is useful to force a cli rebuild, which is\n    sometimes necessary in order for config changes to take effect.\n\n    \"\"\"\n    pass\n\n\nclass ChangeDirHandler(object):\n    def __init__(self, output=sys.stdout, err=sys.stderr, chdir=os.chdir):\n        self._output = output\n        self._err = err\n        self._chdir = chdir\n\n    def run(self, command, application):\n        # command is a list of parsed commands\n        if len(command) != 2:\n            self._err.write(\"invalid syntax, must be: .cd dirname\\n\")\n            return\n        dirname = os.path.expandvars(os.path.expanduser(command[1]))\n        try:\n            self._chdir(dirname)\n        except OSError as e:\n            self._err.write(\"cd: %s\\n\" % e)\n\n\nclass EditHandler(object):\n    def __init__(self, popen_cls=None, env=None, err=sys.stderr):\n        if popen_cls is None:\n            popen_cls = subprocess.Popen\n        self._popen_cls = popen_cls\n        if env is None:\n            env = os.environ\n        self._env = env\n        self._err = err\n\n    def _get_editor_command(self):\n        if 'EDITOR' in self._env:\n            return self._env['EDITOR']\n        else:\n            return compat.default_editor()\n\n    def _generate_edit_history(self, application):\n        history = list(application.history)\n        commands = [h for h in history if not h.startswith(('.', '!'))]\n        return '\\n'.join(commands)\n\n    def run(self, command, application):\n        \"\"\"Open application's history buffer in an editor.\n\n        :type command: list\n        :param command: The dot command as a list split\n            on whitespace, e.g ``['.foo', 'arg1', 'arg2']``\n\n        :type application: AWSShell\n        :param application: The application object.\n\n        \"\"\"\n        with temporary_file('w') as f:\n            all_commands = self._generate_edit_history(application)\n            f.write(all_commands)\n            f.flush()\n            editor = self._get_editor_command()\n            try:\n                p = self._popen_cls([editor, f.name])\n                p.communicate()\n            except OSError:\n                self._err.write(\"Unable to launch editor: %s\\n\"\n                                \"You can configure which editor to use by \"\n                                \"exporting the EDITOR environment variable.\\n\"\n                                % editor)\n\n\nclass ProfileHandler(object):\n    USAGE = (\n        '.profile           # Print the current profile\\n'\n        '.profile <name>    # Change the current profile\\n'\n    )\n\n    def __init__(self, output=sys.stdout, err=sys.stderr):\n        self._output = output\n        self._err = err\n\n    def run(self, command, application):\n        \"\"\"Get or set the profile.\n\n        If .profile is called with no args, the current profile\n        is displayed.  If the .profile command is called with a\n        single arg, then the current profile for the application\n        will be set to the new value.\n        \"\"\"\n        if len(command) == 1:\n            profile = application.profile\n            if profile is None:\n                self._output.write(\n                    \"Current shell profile: no profile configured\\n\"\n                    \"You can change profiles using: .profile profile-name\\n\")\n            else:\n                self._output.write(\"Current shell profile: %s\\n\" % profile)\n        elif len(command) == 2:\n            new_profile_name = command[1]\n            application.profile = new_profile_name\n            self._output.write(\"Current shell profile changed to: %s\\n\" %\n                               new_profile_name)\n        else:\n            self._err.write(\"Usage:\\n%s\\n\" % self.USAGE)\n\n\nclass ExitHandler(object):\n    def run(self, command, application):\n        return EXIT_REQUESTED\n\n\nclass DotCommandHandler(object):\n    HANDLER_CLASSES = {\n        'edit': EditHandler,\n        'profile': ProfileHandler,\n        'cd': ChangeDirHandler,\n        'exit': ExitHandler,\n        'quit': ExitHandler,\n    }\n\n    def __init__(self, output=sys.stdout, err=sys.stderr):\n        self._output = output\n        self._err = err\n\n    def handle_cmd(self, command, application):\n        \"\"\"Handle running a given dot command from a user.\n\n        :type command: str\n        :param command: The full dot command string, e.g. ``.edit``,\n            of ``.profile prod``.\n\n        :type application: AWSShell\n        :param application: The application object.\n\n        \"\"\"\n        parts = command.split()\n        cmd_name = parts[0][1:]\n        if cmd_name not in self.HANDLER_CLASSES:\n            self._unknown_cmd(parts, application)\n        else:\n            # Note we expect the class to support no-arg\n            # instantiation.\n            return self.HANDLER_CLASSES[cmd_name]().run(parts, application)\n\n    def _unknown_cmd(self, cmd_parts, application):\n        self._err.write(\"Unknown dot command: %s\\n\" % cmd_parts[0])\n\n\nclass AWSShell(object):\n    \"\"\"Encapsulate the ui, completer, command history, docs, and config.\n\n    Runs the input event loop and delegates the command execution to either\n    the `awscli` or the underlying shell.\n\n    :type refresh_cli: bool\n    :param refresh_cli: Flag to refresh the cli.\n\n    :type config_obj: :class:`configobj.ConfigObj`\n    :param config_obj: Contains the config information for reading and writing.\n\n    :type config_section: :class:`configobj.Section`\n    :param config_section: Convenience attribute to access the main section\n        of the config.\n\n    :type model_completer: :class:`AWSCLIModelCompleter`\n    :param model_completer: Matches input with completions.  `AWSShell` sets\n        and gets the attribute `AWSCLIModelCompleter.match_fuzzy`.\n\n    :type enable_vi_bindings: bool\n    :param enable_vi_bindings: If True, enables Vi key bindings. Else, Emacs\n        key bindings are enabled.\n\n    :type show_completion_columns: bool\n    param show_completion_columns: If True, completions are shown in multiple\n        columns.  Else, completions are shown in a single scrollable column.\n\n    :type show_help: bool\n    :param show_help: If True, shows the help pane.  Else, hides the help pane.\n\n    :type theme: str\n    :param theme: The pygments theme.\n\n    \"\"\"\n\n    def __init__(self, completer, model_completer, docs,\n                 input=None, output=None, popen_cls=None):\n        self.completer = completer\n        self.model_completer = model_completer\n        self.history = InMemoryHistory()\n        self.file_history = FileHistory(build_config_file_path('history'))\n        self._cli = None\n        self._docs = docs\n        self.current_docs = u''\n        self.refresh_cli = False\n        self.key_manager = None\n        self._dot_cmd = DotCommandHandler()\n        self._env = os.environ.copy()\n        self._profile = None\n        self._input = input\n        self._output = output\n\n        if popen_cls is None:\n            popen_cls = subprocess.Popen\n        self._popen_cls = popen_cls\n\n        # These attrs come from the config file.\n        self.config_obj = None\n        self.config_section = None\n        self.enable_vi_bindings = None\n        self.show_completion_columns = None\n        self.show_help = None\n        self.theme = None\n\n        self.load_config()\n\n    def load_config(self):\n        \"\"\"Load the config from the config file or template.\"\"\"\n        config = Config()\n        self.config_obj = config.load('awsshellrc')\n        self.config_section = self.config_obj['aws-shell']\n        self.model_completer.match_fuzzy = self.config_section.as_bool(\n            'match_fuzzy')\n        self.enable_vi_bindings = self.config_section.as_bool(\n            'enable_vi_bindings')\n        self.show_completion_columns = self.config_section.as_bool(\n            'show_completion_columns')\n        self.show_help = self.config_section.as_bool('show_help')\n        self.theme = self.config_section['theme']\n\n    def save_config(self):\n        \"\"\"Save the config to the config file.\"\"\"\n        self.config_section['match_fuzzy'] = self.model_completer.match_fuzzy\n        self.config_section['enable_vi_bindings'] = self.enable_vi_bindings\n        self.config_section['show_completion_columns'] = \\\n            self.show_completion_columns\n        self.config_section['show_help'] = self.show_help\n        self.config_section['theme'] = self.theme\n        self.config_obj.write()\n\n    @property\n    def cli(self):\n        if self._cli is None or self.refresh_cli:\n            self._cli = self.create_cli_interface(self.show_completion_columns)\n            self.refresh_cli = False\n        return self._cli\n\n    def run(self):\n        while True:\n            try:\n                document = self.cli.run(reset_current_buffer=True)\n                text = document.text\n            except InputInterrupt:\n                pass\n            except (KeyboardInterrupt, EOFError):\n                self.save_config()\n                break\n            else:\n                if text.startswith('.'):\n                    # These are special commands (dot commands) that are\n                    # interpreted by the aws-shell directly and typically used\n                    # to modify some type of behavior in the aws-shell.\n                    result = self._dot_cmd.handle_cmd(text, application=self)\n                    if result is EXIT_REQUESTED:\n                        break\n                else:\n                    if text.startswith('!'):\n                        # Then run the rest as a normally shell command.\n                        full_cmd = text[1:]\n                    else:\n                        full_cmd = 'aws ' + text\n                        self.history.append(full_cmd)\n                    self.current_docs = u''\n                    self.cli.buffers['clidocs'].reset(\n                        initial_document=Document(self.current_docs,\n                                                  cursor_position=0))\n                    self.cli.request_redraw()\n                    p = self._popen_cls(full_cmd, shell=True, env=self._env)\n                    p.communicate()\n\n    def stop_input_and_refresh_cli(self):\n        \"\"\"Stop input by raising an `InputInterrupt`, forces a cli refresh.\n\n        The cli refresh is necessary because changing options such as key\n        bindings, single vs multi column menu completions, and the help pane\n        all require a rebuild.\n\n        :raises: :class:`InputInterrupt <exceptions.InputInterrupt>`.\n\n        \"\"\"\n        self.refresh_cli = True\n        self.cli.request_redraw()\n        raise InputInterrupt\n\n    def create_layout(self, display_completions_in_columns, toolbar):\n        from awsshell.lexer import ShellLexer\n        lexer = ShellLexer\n        if self.config_section['theme'] == 'none':\n            lexer = None\n        return create_default_layout(\n            self, u'aws> ', lexer=lexer, reserve_space_for_menu=True,\n            display_completions_in_columns=display_completions_in_columns,\n            get_bottom_toolbar_tokens=toolbar.handler)\n\n    def create_buffer(self, completer, history):\n        return Buffer(\n            history=history,\n            auto_suggest=AutoSuggestFromHistory(),\n            enable_history_search=True,\n            completer=completer,\n            complete_while_typing=Always(),\n            accept_action=AcceptAction.RETURN_DOCUMENT)\n\n    def create_key_manager(self):\n        \"\"\"Create the :class:`KeyManager`.\n\n        The inputs to KeyManager are expected to be callable, so we can't\n        use the standard @property and @attrib.setter for these attributes.\n        Lambdas cannot contain assignments so we're forced to define setters.\n\n        :rtype: :class:`KeyManager`\n        :return: A KeyManager with callables to set the toolbar options.  Also\n            includes the method stop_input_and_refresh_cli to ensure certain\n            options take effect within the current session.\n\n        \"\"\"\n        def set_match_fuzzy(match_fuzzy):\n            \"\"\"Setter for fuzzy matching mode.\n\n            :type match_fuzzy: bool\n            :param match_fuzzy: The match fuzzy flag.\n\n            \"\"\"\n            self.model_completer.match_fuzzy = match_fuzzy\n\n        def set_enable_vi_bindings(enable_vi_bindings):\n            \"\"\"Setter for vi mode keybindings.\n\n            If vi mode is off, emacs mode is enabled by default by\n            `prompt_toolkit`.\n\n            :type enable_vi_bindings: bool\n            :param enable_vi_bindings: The enable Vi bindings flag.\n\n            \"\"\"\n            self.enable_vi_bindings = enable_vi_bindings\n\n        def set_show_completion_columns(show_completion_columns):\n            \"\"\"Setter for showing the completions in columns flag.\n\n            :type show_completion_columns: bool\n            :param show_completion_columns: The show completions in\n                multiple columns flag.\n\n            \"\"\"\n            self.show_completion_columns = show_completion_columns\n\n        def set_show_help(show_help):\n            \"\"\"Setter for showing the help container flag.\n\n            :type show_help: bool\n            :param show_help: The show help flag.\n\n            \"\"\"\n            self.show_help = show_help\n\n        return KeyManager(\n            lambda: self.model_completer.match_fuzzy, set_match_fuzzy,\n            lambda: self.enable_vi_bindings, set_enable_vi_bindings,\n            lambda: self.show_completion_columns, set_show_completion_columns,\n            lambda: self.show_help, set_show_help,\n            self.stop_input_and_refresh_cli)\n\n    def create_application(self, completer, history,\n                           display_completions_in_columns):\n        self.key_manager = self.create_key_manager()\n        toolbar = Toolbar(\n            lambda: self.model_completer.match_fuzzy,\n            lambda: self.enable_vi_bindings,\n            lambda: self.show_completion_columns,\n            lambda: self.show_help)\n        style_factory = StyleFactory(self.theme)\n        buffers = {\n            'clidocs': Buffer(read_only=True)\n        }\n\n        if self.enable_vi_bindings:\n            editing_mode = EditingMode.VI\n        else:\n            editing_mode = EditingMode.EMACS\n\n        return Application(\n            editing_mode=editing_mode,\n            layout=self.create_layout(display_completions_in_columns, toolbar),\n            mouse_support=False,\n            style=style_factory.style,\n            buffers=buffers,\n            buffer=self.create_buffer(completer, history),\n            on_abort=AbortAction.RETRY,\n            on_exit=AbortAction.RAISE_EXCEPTION,\n            on_input_timeout=self.on_input_timeout,\n            key_bindings_registry=self.key_manager.manager.registry,\n        )\n\n    def on_input_timeout(self, cli):\n        if not self.show_help:\n            return\n        document = cli.current_buffer.document\n        text = document.text\n        LOG.debug(\"document.text = %s\", text)\n        LOG.debug(\"current_command = %s\", self.completer.current_command)\n        if text.strip():\n            command = self.completer.current_command\n            key_name = '.'.join(command.split()).encode('utf-8')\n            last_option = self.completer.last_option\n            if last_option:\n                self.current_docs = self._docs.extract_param(\n                    key_name, last_option)\n            else:\n                self.current_docs = self._docs.extract_description(key_name)\n        else:\n            self.current_docs = u''\n\n        position = cli.buffers['clidocs'].document.cursor_position\n        # if the docs to be displayed have changed, reset position to 0\n        if cli.buffers['clidocs'].text != self.current_docs:\n            position = 0\n\n        cli.buffers['clidocs'].reset(\n            initial_document=Document(\n                self.current_docs,\n                cursor_position=position\n            )\n        )\n        cli.request_redraw()\n\n    def create_cli_interface(self, display_completions_in_columns):\n        # A CommandLineInterface from prompt_toolkit\n        # accepts two things: an application and an\n        # event loop.\n        loop = create_eventloop()\n        app = self.create_application(self.completer,\n                                      self.file_history,\n                                      display_completions_in_columns)\n        cli = CommandLineInterface(application=app, eventloop=loop,\n                                   input=self._input, output=self._output)\n        return cli\n\n    @property\n    def profile(self):\n        return self._profile\n\n    @profile.setter\n    def profile(self, new_profile_name):\n        # There's only two things that need to know about new profile\n        # changes.\n        #\n        # First, the actual command runner.  If we want\n        # to use a different profile, it should ensure that the CLI\n        # commands that get run use the new profile (via the\n        # AWS_DEFAULT_PROFILE env var).\n        #\n        # Second, we also need to let the server side autocompleter know.\n        #\n        # Given this is easy to manage by hand, I don't think\n        # it's worth adding an event system or observers just yet.\n        # If this gets hard to manage, the complexity of those systems\n        # would be worth it.\n        self._env['AWS_DEFAULT_PROFILE'] = new_profile_name\n        self.completer.change_profile(new_profile_name)\n        self._profile = new_profile_name\n"
  },
  {
    "path": "awsshell/autocomplete.py",
    "content": "from __future__ import print_function\nfrom awsshell.fuzzy import fuzzy_search\nfrom awsshell.substring import substring_search\n\n\nclass AWSCLIModelCompleter(object):\n    \"\"\"Autocompletion based on the JSON models for AWS services.\n\n    This class consumes indexed data based on the JSON models from\n    AWS service (which we pull through botocore's data loaders).\n\n    \"\"\"\n    def __init__(self, index_data, match_fuzzy=True):\n        self._index = index_data\n        self._root_name = 'aws'\n        self._global_options = index_data[self._root_name]['arguments']\n        # These values mutate as autocompletions occur.\n        # They track state to improve the autocompletion speed.\n        self._current_name = 'aws'\n        self._current = index_data[self._root_name]\n        self._last_position = 0\n        self._current_line = ''\n        self.last_option = ''\n        # This will get populated as a command is completed.\n        self.cmd_path = [self._current_name]\n        self.match_fuzzy = match_fuzzy\n\n    @property\n    def global_arg_metadata(self):\n        return self._index[self._root_name]['argument_metadata']\n\n    @property\n    def arg_metadata(self):\n        # Returns the required arguments for the current level.\n        return self._current.get('argument_metadata', {})\n\n    def reset(self):\n        # Resets all the state.  Called after a user runs\n        # a command.\n        self._current_name = self._root_name\n        self._current = self._index[self._root_name]\n        self._last_position = 0\n        self.last_option = ''\n        self.cmd_path = [self._current_name]\n\n    def autocomplete(self, line):\n        \"\"\"Given a line, return a list of suggestions.\"\"\"\n        current_length = len(line)\n        self._current_line = line\n        if current_length == 1 and self._last_position > 1:\n            # Reset state.  This is likely from a user completing\n            # a previous command.\n            self.reset()\n        elif current_length < self._last_position:\n            # The user has hit backspace.  We'll need to check\n            # the current words.\n            return self._handle_backspace()\n        elif not line:\n            return []\n        elif current_length != self._last_position + 1:\n            return self._complete_from_full_parse()\n\n        # This position is important.  We only update the _last_position\n        # after we've checked the special cases above where that value\n        # matters.\n        self._last_position = len(line)\n        if line and not line.strip():\n            # Special case, the user hits a space on a new line so\n            # we autocomplete all the top level commands.\n            return self._current['commands']\n\n        last_word = line.split()[-1]\n        if last_word in self.arg_metadata or last_word in self._global_options:\n            # The last thing we completed was an argument, record\n            # this as self.last_arg\n            self.last_option = last_word\n        if line[-1] == ' ':\n            # At this point the user has autocompleted a command\n            # or an argument and has hit space.  If they've\n            # just completed a command, we need to change the\n            # current context and traverse into the subcommand.\n            # \"ec2 \"\n            #      ^--here, need to traverse into \"ec2\"\n            #\n            # Otherwise:\n            # \"ec2 --no-validate-ssl \"\n            #                        ^-- here, stay on \"ec2\" context.\n            if not last_word.startswith('-'):\n                next_command = self._current['children'].get(last_word)\n                if next_command is not None:\n                    self._current = next_command\n                    self._current_name = last_word\n                    self.cmd_path.append(self._current_name)\n            elif last_word in self.arg_metadata and \\\n                    self.arg_metadata[last_word]['example']:\n                # Then this is an arg with a shorthand example so we'll\n                # suggest that example.\n                return [self.arg_metadata[last_word]['example']]\n            # Even if we don't change context, we still want to\n            # autocomplete all the commands for the current context\n            # in either of the above two cases.\n            return self._current['commands'][:]\n        elif last_word.startswith('-'):\n            # TODO: cache this for the duration of the current context.\n            # We don't need to recompute this until the args are\n            # different.\n            all_args = self._get_all_args()\n            if self.match_fuzzy:\n                return fuzzy_search(last_word, all_args)\n            else:\n                return substring_search(last_word, all_args)\n        if self.match_fuzzy:\n            return fuzzy_search(last_word, self._current['commands'])\n        else:\n            return substring_search(last_word, self._current['commands'])\n\n    def _get_all_args(self):\n        if self._current['arguments'] != self._global_options:\n            all_args = self._current['arguments'] + self._global_options\n        else:\n            all_args = self._current['arguments']\n        return all_args\n\n    def _handle_backspace(self):\n        return self._complete_from_full_parse()\n\n    def _complete_from_full_parse(self):\n        # We try to avoid calling this, but this is necessary\n        # sometimes.  In this scenario, we're resetting everything\n        # and starting from the very beginning and reparsing\n        # everything.\n        # This is a naive implementation for now.  This\n        # can be optimized.\n        self.reset()\n        line = self._current_line\n        for i in range(1, len(self._current_line)):\n            self.autocomplete(line[:i])\n        return self.autocomplete(line)\n\n    def _autocomplete_options(self, last_word):\n        global_args = []\n        # Autocomplete argument names.\n        current_arg_completions = [\n            cmd for cmd in self._current['arguments']\n            if cmd.startswith(last_word)]\n        if self._current_name != self._root_name:\n            # Also autocomplete global arguments.\n            global_args = [\n                cmd for cmd in self._global_options if\n                cmd.startswith(last_word)]\n        return current_arg_completions + global_args\n"
  },
  {
    "path": "awsshell/awsshellrc",
    "content": "[aws-shell]\n\n# fuzzy or substring match.\nmatch_fuzzy = True\n\n# vi or emacs key bindings.\nenable_vi_bindings = False\n\n# multi or single column completion menu.\nshow_completion_columns = False\n\n# show or hide the help pane.\nshow_help = True\n\n# visual theme. possible values: manni, igor, xcode, vim,\n# autumn,vs, rrt, native, perldoc, borland, tango, emacs,\n# friendly, monokai, paraiso-dark, colorful, murphy, bw,\n# pastie, paraiso-light, trac, default, fruity.\n# to disable themes, set theme = none\ntheme = vim\n"
  },
  {
    "path": "awsshell/compat.py",
    "content": "from __future__ import print_function\nimport sys\nimport platform\n\n\nPY3 = sys.version_info[0] == 3\nON_WINDOWS = platform.system() == 'Windows'\n\n\nif PY3:\n    from html.parser import HTMLParser\n    text_type = str\n    from io import StringIO\n    import dbm\nelse:\n    from HTMLParser import HTMLParser\n    text_type = unicode\n    from cStringIO import StringIO\n    import anydbm as dbm\n\n\nif ON_WINDOWS:\n    def default_editor():\n        return 'notepad.exe'\nelse:\n    def default_editor():\n        return 'vi'\n"
  },
  {
    "path": "awsshell/config.py",
    "content": "# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\"). You\n# may not use this file except in compliance with the License. A copy of\n# the License is located at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# or in the \"license\" file accompanying this file. This file is\n# distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF\n# ANY KIND, either express or implied. See the License for the specific\n# language governing permissions and limitations under the License.\nimport os\nimport shutil\n\nfrom configobj import ConfigObj\n\nfrom awsshell.utils import build_config_file_path\n\n\nclass Config(object):\n    \"\"\"Reads and writes the config template and user config file.\"\"\"\n\n    def load(self, config_template, config_file=None):\n        \"\"\"Read the config file if it exists, else read the default config.\n\n        Creates the user config file if it doesn't exist using the template.\n\n        :type config_template: str\n        :param config_template: The config template file name.\n\n        :type config_file: str\n        :param config_file: (Optional) The config file name.\n            If None, the config_file name will be set to the config_template.\n\n        :rtype: :class:`configobj.ConfigObj`\n        :return: The config information for reading and writing.\n        \"\"\"\n        if config_file is None:\n            config_file = config_template\n        config_path = build_config_file_path(config_file)\n        template_path = os.path.join(os.path.dirname(__file__),\n                                     config_template)\n        self._copy_template_to_config(template_path, config_path)\n        return self._load_template_or_config(template_path, config_path)\n\n    def _load_template_or_config(self, template_path, config_path):\n        \"\"\"Load the config file if it exists, else read the default config.\n\n        :type template_path: str\n        :param template_path: The template config file path.\n\n        :type config_path: str\n        :param config_path: The user's config file path.\n\n        :rtype: :class:`configobj.ConfigObj`\n        :return: The config information for reading and writing.\n        \"\"\"\n        expanded_config_path = os.path.expanduser(config_path)\n        cfg = ConfigObj()\n        cfg.filename = expanded_config_path\n        cfg.merge(ConfigObj(template_path, interpolation=False))\n        cfg.merge(ConfigObj(expanded_config_path, interpolation=False))\n        return cfg\n\n    def _copy_template_to_config(self, template_path,\n                                 config_path, overwrite=False):\n        \"\"\"Write the default config from a template.\n\n        :type template_path: str\n        :param template_path: The template config file path.\n\n        :type config_path: str\n        :param config_path: The user's config file path.\n\n        :type overwrite: bool\n        :param overwrite: (Optional) Determines whether to overwrite the\n            existing config file, if it exists.\n\n        :raises: :class:`OSError <exceptions.OSError>`\n        \"\"\"\n        config_path = os.path.expanduser(config_path)\n        if not overwrite and os.path.isfile(config_path):\n            return\n        else:\n            try:\n                config_path_dir_name = os.path.dirname(config_path)\n                os.makedirs(config_path_dir_name)\n            except OSError:\n                if not os.path.isdir(config_path_dir_name):\n                    raise\n            shutil.copyfile(template_path, config_path)\n"
  },
  {
    "path": "awsshell/data/cloudformation/2010-05-15/completions-1.json",
    "content": "{\n  \"operations\": {\n    \"UpdateStack\": {\n      \"StackName\": {\n        \"resourceName\": \"Stack\", \n        \"resourceIdentifier\": \"Name\"\n      }\n    }, \n    \"DeleteStack\": {\n      \"StackName\": {\n        \"resourceName\": \"Stack\", \n        \"resourceIdentifier\": \"Name\"\n      }\n    }, \n    \"CancelUpdateStack\": {\n      \"StackName\": {\n        \"resourceName\": \"Stack\", \n        \"resourceIdentifier\": \"Name\"\n      }\n    }\n  }, \n  \"resources\": {\n    \"Stack\": {\n      \"operation\": \"DescribeStacks\", \n      \"resourceIdentifier\": {\n        \"Name\": \"Stacks[].StackName\"\n      }\n    }\n  }\n}"
  },
  {
    "path": "awsshell/data/dynamodb/2012-08-10/completions-1.json",
    "content": "{\n  \"operations\": {\n    \"UpdateTable\": {\n      \"TableName\": {\n        \"resourceName\": \"Table\", \n        \"resourceIdentifier\": \"Name\"\n      }\n    }, \n    \"DeleteTable\": {\n      \"TableName\": {\n        \"resourceName\": \"Table\", \n        \"resourceIdentifier\": \"Name\"\n      }\n    }, \n    \"Scan\": {\n      \"TableName\": {\n        \"resourceName\": \"Table\", \n        \"resourceIdentifier\": \"Name\"\n      }\n    }, \n    \"GetItem\": {\n      \"TableName\": {\n        \"resourceName\": \"Table\", \n        \"resourceIdentifier\": \"Name\"\n      }\n    }, \n    \"Query\": {\n      \"TableName\": {\n        \"resourceName\": \"Table\", \n        \"resourceIdentifier\": \"Name\"\n      }\n    }, \n    \"PutItem\": {\n      \"TableName\": {\n        \"resourceName\": \"Table\", \n        \"resourceIdentifier\": \"Name\"\n      }\n    }, \n    \"UpdateItem\": {\n      \"TableName\": {\n        \"resourceName\": \"Table\", \n        \"resourceIdentifier\": \"Name\"\n      }\n    }, \n    \"DeleteItem\": {\n      \"TableName\": {\n        \"resourceName\": \"Table\", \n        \"resourceIdentifier\": \"Name\"\n      }\n    }\n  }, \n  \"resources\": {\n    \"Table\": {\n      \"operation\": \"ListTables\", \n      \"resourceIdentifier\": {\n        \"Name\": \"TableNames[]\"\n      }\n    }\n  }\n}"
  },
  {
    "path": "awsshell/data/ec2/2015-04-15/completions-1.json",
    "content": "{\n  \"operations\": {\n    \"ResetNetworkInterfaceAttribute\": {\n      \"NetworkInterfaceId\": {\n        \"resourceName\": \"NetworkInterface\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"CreateSubnet\": {\n      \"VpcId\": {\n        \"resourceName\": \"Vpc\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"CopySnapshot\": {\n      \"SourceSnapshotId\": {\n        \"resourceName\": \"Snapshot\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"AttachNetworkInterface\": {\n      \"NetworkInterfaceId\": {\n        \"resourceName\": \"NetworkInterface\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"UnassignPrivateIpAddresses\": {\n      \"NetworkInterfaceId\": {\n        \"resourceName\": \"NetworkInterface\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"DescribeImageAttribute\": {\n      \"ImageId\": {\n        \"resourceName\": \"Image\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"DescribeInstances\": {\n      \"InstanceIds\": {\n        \"resourceName\": \"Instance\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"ResetSnapshotAttribute\": {\n      \"SnapshotId\": {\n        \"resourceName\": \"Snapshot\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"StartInstances\": {\n      \"InstanceIds\": {\n        \"resourceName\": \"Instance\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"DeleteDhcpOptions\": {\n      \"DhcpOptionsId\": {\n        \"resourceName\": \"DhcpOptions\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"RejectVpcPeeringConnection\": {\n      \"VpcPeeringConnectionId\": {\n        \"resourceName\": \"VpcPeeringConnection\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"DeleteInternetGateway\": {\n      \"InternetGatewayId\": {\n        \"resourceName\": \"InternetGateway\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"ResetImageAttribute\": {\n      \"ImageId\": {\n        \"resourceName\": \"Image\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"ModifyNetworkInterfaceAttribute\": {\n      \"NetworkInterfaceId\": {\n        \"resourceName\": \"NetworkInterface\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"MonitorInstances\": {\n      \"InstanceIds\": {\n        \"resourceName\": \"Instance\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"DeleteNetworkAcl\": {\n      \"NetworkAclId\": {\n        \"resourceName\": \"NetworkAcl\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"DisableVpcClassicLink\": {\n      \"VpcId\": {\n        \"resourceName\": \"Vpc\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"ModifyInstanceAttribute\": {\n      \"InstanceId\": {\n        \"resourceName\": \"Instance\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"EnableVpcClassicLink\": {\n      \"VpcId\": {\n        \"resourceName\": \"Vpc\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"AssociateDhcpOptions\": {\n      \"VpcId\": {\n        \"resourceName\": \"Vpc\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"AssociateRouteTable\": {\n      \"RouteTableId\": {\n        \"resourceName\": \"RouteTable\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"DescribeNetworkInterfaceAttribute\": {\n      \"NetworkInterfaceId\": {\n        \"resourceName\": \"NetworkInterface\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"DeleteVpc\": {\n      \"VpcId\": {\n        \"resourceName\": \"Vpc\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"GetPasswordData\": {\n      \"InstanceId\": {\n        \"resourceName\": \"Instance\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"UnmonitorInstances\": {\n      \"InstanceIds\": {\n        \"resourceName\": \"Instance\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"AcceptVpcPeeringConnection\": {\n      \"VpcPeeringConnectionId\": {\n        \"resourceName\": \"VpcPeeringConnection\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"DeleteSnapshot\": {\n      \"SnapshotId\": {\n        \"resourceName\": \"Snapshot\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"CreateRoute\": {\n      \"RouteTableId\": {\n        \"resourceName\": \"RouteTable\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"CreateNetworkAclEntry\": {\n      \"NetworkAclId\": {\n        \"resourceName\": \"NetworkAcl\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"RevokeSecurityGroupEgress\": {\n      \"GroupId\": {\n        \"resourceName\": \"SecurityGroup\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"DeregisterImage\": {\n      \"ImageId\": {\n        \"resourceName\": \"Image\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"AssignPrivateIpAddresses\": {\n      \"NetworkInterfaceId\": {\n        \"resourceName\": \"NetworkInterface\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"ReportInstanceStatus\": {\n      \"Instances\": {\n        \"resourceName\": \"Instance\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"DescribeSnapshotAttribute\": {\n      \"SnapshotId\": {\n        \"resourceName\": \"Snapshot\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"DeleteSubnet\": {\n      \"SubnetId\": {\n        \"resourceName\": \"Subnet\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"DeleteVolume\": {\n      \"VolumeId\": {\n        \"resourceName\": \"Volume\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"CreateNetworkAcl\": {\n      \"VpcId\": {\n        \"resourceName\": \"Vpc\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"AttachClassicLinkVpc\": {\n      \"VpcId\": {\n        \"resourceName\": \"Vpc\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"DeleteSecurityGroup\": {\n      \"GroupId\": {\n        \"resourceName\": \"SecurityGroup\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"CreateImage\": {\n      \"InstanceId\": {\n        \"resourceName\": \"Instance\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"DeleteVpcPeeringConnection\": {\n      \"VpcPeeringConnectionId\": {\n        \"resourceName\": \"VpcPeeringConnection\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"ModifyVolumeAttribute\": {\n      \"VolumeId\": {\n        \"resourceName\": \"Volume\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"ModifyImageAttribute\": {\n      \"ImageId\": {\n        \"resourceName\": \"Image\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"DeleteNetworkAclEntry\": {\n      \"NetworkAclId\": {\n        \"resourceName\": \"NetworkAcl\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"RunInstances\": {\n      \"SubnetId\": {\n        \"resourceName\": \"Subnet\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"GetConsoleOutput\": {\n      \"InstanceId\": {\n        \"resourceName\": \"Instance\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"StopInstances\": {\n      \"InstanceIds\": {\n        \"resourceName\": \"Instance\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"ModifySnapshotAttribute\": {\n      \"SnapshotId\": {\n        \"resourceName\": \"Snapshot\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"DescribeVpcAttribute\": {\n      \"VpcId\": {\n        \"resourceName\": \"Vpc\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"AttachInternetGateway\": {\n      \"VpcId\": {\n        \"resourceName\": \"Vpc\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"DescribeVolumeAttribute\": {\n      \"VolumeId\": {\n        \"resourceName\": \"Volume\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"DetachVolume\": {\n      \"VolumeId\": {\n        \"resourceName\": \"Volume\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"ReplaceNetworkAclEntry\": {\n      \"NetworkAclId\": {\n        \"resourceName\": \"NetworkAcl\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"AttachVolume\": {\n      \"VolumeId\": {\n        \"resourceName\": \"Volume\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"DetachNetworkInterface\": {}, \n    \"TerminateInstances\": {\n      \"InstanceIds\": {\n        \"resourceName\": \"Instance\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"DetachInternetGateway\": {\n      \"VpcId\": {\n        \"resourceName\": \"Vpc\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"ResetInstanceAttribute\": {\n      \"InstanceId\": {\n        \"resourceName\": \"Instance\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"DeletePlacementGroup\": {\n      \"GroupName\": {\n        \"resourceName\": \"PlacementGroup\", \n        \"resourceIdentifier\": \"Name\"\n      }\n    }, \n    \"DeleteRouteTable\": {\n      \"RouteTableId\": {\n        \"resourceName\": \"RouteTable\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"CreateTags\": {\n      \"Resources\": {\n        \"resourceName\": \"Vpc\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"EnableVolumeIO\": {\n      \"VolumeId\": {\n        \"resourceName\": \"Volume\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"CreateRouteTable\": {\n      \"VpcId\": {\n        \"resourceName\": \"Vpc\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"CreateNetworkInterface\": {\n      \"SubnetId\": {\n        \"resourceName\": \"Subnet\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"ReplaceNetworkAclAssociation\": {\n      \"NetworkAclId\": {\n        \"resourceName\": \"NetworkAcl\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"ModifyVpcAttribute\": {\n      \"VpcId\": {\n        \"resourceName\": \"Vpc\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"DeleteNetworkInterface\": {\n      \"NetworkInterfaceId\": {\n        \"resourceName\": \"NetworkInterface\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"RebootInstances\": {\n      \"InstanceIds\": {\n        \"resourceName\": \"Instance\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"DescribeVolumeStatus\": {\n      \"VolumeIds\": {\n        \"resourceName\": \"Volume\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"DetachClassicLinkVpc\": {\n      \"VpcId\": {\n        \"resourceName\": \"Vpc\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"AuthorizeSecurityGroupEgress\": {\n      \"GroupId\": {\n        \"resourceName\": \"SecurityGroup\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"CreateVpcPeeringConnection\": {\n      \"VpcId\": {\n        \"resourceName\": \"Vpc\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"RevokeSecurityGroupIngress\": {\n      \"GroupId\": {\n        \"resourceName\": \"SecurityGroup\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"CreateSecurityGroup\": {\n      \"VpcId\": {\n        \"resourceName\": \"Vpc\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"DeleteKeyPair\": {\n      \"KeyName\": {\n        \"resourceName\": \"KeyPair\", \n        \"resourceIdentifier\": \"Name\"\n      }\n    }, \n    \"AuthorizeSecurityGroupIngress\": {\n      \"GroupId\": {\n        \"resourceName\": \"SecurityGroup\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"CreateSnapshot\": {\n      \"VolumeId\": {\n        \"resourceName\": \"Volume\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"DescribeInstanceAttribute\": {\n      \"InstanceId\": {\n        \"resourceName\": \"Instance\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }\n  }, \n  \"resources\": {\n    \"Subnet\": {\n      \"operation\": \"DescribeSubnets\", \n      \"resourceIdentifier\": {\n        \"Id\": \"Subnets[].SubnetId\"\n      }\n    }, \n    \"VpcPeeringConnection\": {\n      \"operation\": \"DescribeVpcPeeringConnections\", \n      \"resourceIdentifier\": {\n        \"Id\": \"VpcPeeringConnections[].VpcPeeringConnectionId\"\n      }\n    }, \n    \"NetworkAcl\": {\n      \"operation\": \"DescribeNetworkAcls\", \n      \"resourceIdentifier\": {\n        \"Id\": \"NetworkAcls[].NetworkAclId\"\n      }\n    }, \n    \"RouteTable\": {\n      \"operation\": \"DescribeRouteTables\", \n      \"resourceIdentifier\": {\n        \"Id\": \"RouteTables[].RouteTableId\"\n      }\n    }, \n    \"Snapshot\": {\n      \"operation\": \"DescribeSnapshots\", \n      \"resourceIdentifier\": {\n        \"Id\": \"Snapshots[].SnapshotId\"\n      }\n    }, \n    \"DhcpOptions\": {\n      \"operation\": \"DescribeDhcpOptions\", \n      \"resourceIdentifier\": {\n        \"Id\": \"DhcpOptions[].DhcpOptionsId\"\n      }\n    }, \n    \"SecurityGroup\": {\n      \"operation\": \"DescribeSecurityGroups\", \n      \"resourceIdentifier\": {\n        \"Id\": \"SecurityGroups[].GroupId\"\n      }\n    }, \n    \"NetworkInterface\": {\n      \"operation\": \"DescribeNetworkInterfaces\", \n      \"resourceIdentifier\": {\n        \"Id\": \"NetworkInterfaces[].NetworkInterfaceId\"\n      }\n    }, \n    \"Volume\": {\n      \"operation\": \"DescribeVolumes\", \n      \"resourceIdentifier\": {\n        \"Id\": \"Volumes[].VolumeId\"\n      }\n    }, \n    \"Instance\": {\n      \"operation\": \"DescribeInstances\", \n      \"resourceIdentifier\": {\n        \"Id\": \"Reservations[].Instances[].InstanceId\"\n      }\n    }, \n    \"KeyPair\": {\n      \"operation\": \"DescribeKeyPairs\", \n      \"resourceIdentifier\": {\n        \"Name\": \"KeyPairs[].KeyName\"\n      }\n    }, \n    \"InternetGateway\": {\n      \"operation\": \"DescribeInternetGateways\", \n      \"resourceIdentifier\": {\n        \"Id\": \"InternetGateways[].InternetGatewayId\"\n      }\n    }, \n    \"Vpc\": {\n      \"operation\": \"DescribeVpcs\", \n      \"resourceIdentifier\": {\n        \"Id\": \"Vpcs[].VpcId\"\n      }\n    }, \n    \"PlacementGroup\": {\n      \"operation\": \"DescribePlacementGroups\", \n      \"resourceIdentifier\": {\n        \"Name\": \"PlacementGroups[].GroupName\"\n      }\n    }, \n    \"Image\": {\n      \"operation\": \"DescribeImages\", \n      \"resourceIdentifier\": {\n        \"Id\": \"Images[].ImageId\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "awsshell/data/elb/2012-06-01/completions-1.json",
    "content": "{\n  \"operations\": {\n    \"AddTags\": {\n      \"LoadBalancerNames\": {\n        \"resourceName\": \"LoadBalancer\",\n        \"resourceIdentifier\": \"Name\"\n      }\n    },\n    \"ApplySecurityGroupsToLoadBalancer\": {\n      \"LoadBalancerName\": {\n        \"resourceName\": \"LoadBalancer\",\n        \"resourceIdentifier\": \"Name\"\n      }\n    },\n    \"AttachLoadBalancerToSubnets\": {\n      \"LoadBalancerName\": {\n        \"resourceName\": \"LoadBalancer\",\n        \"resourceIdentifier\": \"Name\"\n      }\n    },\n    \"ConfigureHealthCheck\": {\n      \"LoadBalancerName\": {\n        \"resourceName\": \"LoadBalancer\",\n        \"resourceIdentifier\": \"Name\"\n      }\n    },\n    \"CreateAppCookieStickinessPolicy\": {\n      \"LoadBalancerName\": {\n        \"resourceName\": \"LoadBalancer\",\n        \"resourceIdentifier\": \"Name\"\n      }\n    },\n    \"CreateLBCookieStickinessPolicy\": {\n      \"LoadBalancerName\": {\n        \"resourceName\": \"LoadBalancer\",\n        \"resourceIdentifier\": \"Name\"\n      }\n    },\n    \"CreateLoadBalancerPolicy\": {\n      \"LoadBalancerName\": {\n        \"resourceName\": \"LoadBalancer\",\n        \"resourceIdentifier\": \"Name\"\n      }\n    },\n    \"DetachLoadBalancerFromSubnets\": {\n      \"LoadBalancerName\": {\n        \"resourceName\": \"LoadBalancer\",\n        \"resourceIdentifier\": \"Name\"\n      }\n    },\n    \"DescribeInstanceHealth\": {\n      \"LoadBalancerName\": {\n        \"resourceName\": \"LoadBalancer\",\n        \"resourceIdentifier\": \"Name\"\n      }\n    },\n    \"DeleteLoadBalancer\": {\n      \"LoadBalancerName\": {\n        \"resourceName\": \"LoadBalancer\",\n        \"resourceIdentifier\": \"Name\"\n      }\n    },\n    \"DeleteLoadBalancerListeners\": {\n      \"LoadBalancerName\": {\n        \"resourceName\": \"LoadBalancer\",\n        \"resourceIdentifier\": \"Name\"\n      }\n    },\n    \"DeleteLoadBalancerPolicy\": {\n      \"LoadBalancerName\": {\n        \"resourceName\": \"LoadBalancer\",\n        \"resourceIdentifier\": \"Name\"\n      }\n    },\n    \"DescribeTags\": {\n      \"LoadBalancerNames\": {\n        \"resourceName\": \"LoadBalancer\",\n        \"resourceIdentifier\": \"Name\"\n      }\n    },\n    \"DeregisterInstancesFromLoadBalancer\": {\n      \"LoadBalancerName\": {\n        \"resourceName\": \"LoadBalancer\",\n        \"resourceIdentifier\": \"Name\"\n      }\n    },\n    \"DescribeLoadBalancerAttributes\": {\n      \"LoadBalancerName\": {\n        \"resourceName\": \"LoadBalancer\",\n        \"resourceIdentifier\": \"Name\"\n      }\n    },\n    \"DescribeLoadBalancerPolicies\": {\n      \"LoadBalancerName\": {\n        \"resourceName\": \"LoadBalancer\",\n        \"resourceIdentifier\": \"Name\"\n      }\n    },\n    \"DescribeLoadBalancers\": {\n      \"LoadBalancerNames\": {\n        \"resourceName\": \"LoadBalancer\",\n        \"resourceIdentifier\": \"Name\"\n      }\n    },\n    \"DisableAvailabilityZonesForLoadBalancer\": {\n      \"LoadBalancerName\": {\n        \"resourceName\": \"LoadBalancer\",\n        \"resourceIdentifier\": \"Name\"\n      }\n    },\n    \"EnableAvailabilityZonesForLoadBalancer\": {\n      \"LoadBalancerName\": {\n        \"resourceName\": \"LoadBalancer\",\n        \"resourceIdentifier\": \"Name\"\n      }\n    },\n    \"ModifyLoadBalancerAttributes\": {\n      \"LoadBalancerName\": {\n        \"resourceName\": \"LoadBalancer\",\n        \"resourceIdentifier\": \"Name\"\n      }\n    },\n    \"RegisterInstancesWithLoadBalancer\": {\n      \"LoadBalancerName\": {\n        \"resourceName\": \"LoadBalancer\",\n        \"resourceIdentifier\": \"Name\"\n      }\n    },\n    \"RemoveTags\": {\n      \"LoadBalancerNames\": {\n        \"resourceName\": \"LoadBalancer\",\n        \"resourceIdentifier\": \"Name\"\n      }\n    },\n    \"SetLoadBalancerListenerSSLCertificate\": {\n      \"LoadBalancerName\": {\n        \"resourceName\": \"LoadBalancer\",\n        \"resourceIdentifier\": \"Name\"\n      }\n    },\n    \"SetLoadBalancerPoliciesForBackendServer\": {\n      \"LoadBalancerName\": {\n        \"resourceName\": \"LoadBalancer\",\n        \"resourceIdentifier\": \"Name\"\n      }\n    },\n    \"SetLoadBalancerPoliciesOfListener\": {\n      \"LoadBalancerName\": {\n        \"resourceName\": \"LoadBalancer\",\n        \"resourceIdentifier\": \"Name\"\n      }\n    }\n  },\n  \"resources\": {\n    \"LoadBalancer\": {\n      \"operation\": \"DescribeLoadBalancers\",\n      \"resourceIdentifier\": {\n        \"Name\": \"LoadBalancerDescriptions[].LoadBalancerName\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "awsshell/data/glacier/2012-06-01/completions-1.json",
    "content": "{\n  \"operations\": {\n    \"CreateVault\": {\n      \"vaultName\": {\n        \"resourceName\": \"Vault\", \n        \"resourceIdentifier\": \"Name\"\n      }, \n      \"accountId\": {\n        \"resourceName\": \"Vault\", \n        \"resourceIdentifier\": \"AccountId\"\n      }\n    }, \n    \"DeleteVault\": {\n      \"vaultName\": {\n        \"resourceName\": \"Vault\", \n        \"resourceIdentifier\": \"Name\"\n      }, \n      \"accountId\": {\n        \"resourceName\": \"Vault\", \n        \"resourceIdentifier\": \"AccountId\"\n      }\n    }, \n    \"InitiateJob\": {\n      \"vaultName\": {\n        \"resourceName\": \"Vault\", \n        \"resourceIdentifier\": \"Name\"\n      }, \n      \"accountId\": {\n        \"resourceName\": \"Vault\", \n        \"resourceIdentifier\": \"AccountId\"\n      }\n    }, \n    \"UploadArchive\": {\n      \"vaultName\": {\n        \"resourceName\": \"Vault\", \n        \"resourceIdentifier\": \"Name\"\n      }, \n      \"accountId\": {\n        \"resourceName\": \"Vault\", \n        \"resourceIdentifier\": \"AccountId\"\n      }\n    }, \n    \"InitiateMultipartUpload\": {\n      \"vaultName\": {\n        \"resourceName\": \"Vault\", \n        \"resourceIdentifier\": \"Name\"\n      }, \n      \"accountId\": {\n        \"resourceName\": \"Vault\", \n        \"resourceIdentifier\": \"AccountId\"\n      }\n    }\n  }, \n  \"resources\": {\n    \"Vault\": {\n      \"operation\": \"ListVaults\", \n      \"resourceIdentifier\": {\n        \"AccountId\": \"accountId\",\n        \"Name\": \"VaultList[].VaultName\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "awsshell/data/iam/2010-05-08/completions-1.json",
    "content": "{\n  \"operations\": {\n    \"DeleteInstanceProfile\": {\n      \"InstanceProfileName\": {\n        \"resourceName\": \"InstanceProfile\", \n        \"resourceIdentifier\": \"Name\"\n      }\n    }, \n    \"CreateLoginProfile\": {\n      \"UserName\": {\n        \"resourceName\": \"User\", \n        \"resourceIdentifier\": \"Name\"\n      }\n    }, \n    \"DeletePolicy\": {\n      \"PolicyArn\": {\n        \"resourceName\": \"Policy\", \n        \"resourceIdentifier\": \"Arn\"\n      }\n    }, \n    \"CreateGroup\": {\n      \"GroupName\": {\n        \"resourceName\": \"Group\", \n        \"resourceIdentifier\": \"Name\"\n      }\n    }, \n    \"DetachRolePolicy\": {\n      \"RoleName\": {\n        \"resourceName\": \"Role\", \n        \"resourceIdentifier\": \"Name\"\n      }\n    }, \n    \"EnableMFADevice\": {\n      \"UserName\": {\n        \"resourceName\": \"User\", \n        \"resourceIdentifier\": \"Name\"\n      }\n    }, \n    \"DeleteSAMLProvider\": {\n      \"SAMLProviderArn\": {\n        \"resourceName\": \"SamlProvider\", \n        \"resourceIdentifier\": \"Arn\"\n      }\n    }, \n    \"UpdateUser\": {\n      \"UserName\": {\n        \"resourceName\": \"User\", \n        \"resourceIdentifier\": \"Name\"\n      }\n    }, \n    \"PutGroupPolicy\": {\n      \"GroupName\": {\n        \"resourceName\": \"Group\", \n        \"resourceIdentifier\": \"Name\"\n      }\n    }, \n    \"DeleteVirtualMFADevice\": {\n      \"SerialNumber\": {\n        \"resourceName\": \"VirtualMfaDevice\", \n        \"resourceIdentifier\": \"SerialNumber\"\n      }\n    }, \n    \"DeleteGroup\": {\n      \"GroupName\": {\n        \"resourceName\": \"Group\", \n        \"resourceIdentifier\": \"Name\"\n      }\n    }, \n    \"DeleteRole\": {\n      \"RoleName\": {\n        \"resourceName\": \"Role\", \n        \"resourceIdentifier\": \"Name\"\n      }\n    }, \n    \"AttachGroupPolicy\": {\n      \"PolicyArn\": {\n        \"resourceName\": \"Policy\", \n        \"resourceIdentifier\": \"Arn\"\n      }\n    }, \n    \"CreateAccessKey\": {\n      \"UserName\": {\n        \"resourceName\": \"User\", \n        \"resourceIdentifier\": \"Name\"\n      }\n    }, \n    \"UpdateServerCertificate\": {\n      \"ServerCertificateName\": {\n        \"resourceName\": \"ServerCertificate\", \n        \"resourceIdentifier\": \"Name\"\n      }\n    }, \n    \"AddUserToGroup\": {\n      \"UserName\": {\n        \"resourceName\": \"User\", \n        \"resourceIdentifier\": \"Name\"\n      }\n    }, \n    \"UpdateSAMLProvider\": {\n      \"SAMLProviderArn\": {\n        \"resourceName\": \"SamlProvider\", \n        \"resourceIdentifier\": \"Arn\"\n      }\n    }, \n    \"RemoveRoleFromInstanceProfile\": {\n      \"InstanceProfileName\": {\n        \"resourceName\": \"InstanceProfile\", \n        \"resourceIdentifier\": \"Name\"\n      }\n    }, \n    \"DetachUserPolicy\": {\n      \"UserName\": {\n        \"resourceName\": \"User\", \n        \"resourceIdentifier\": \"Name\"\n      }\n    }, \n    \"DeleteUser\": {\n      \"UserName\": {\n        \"resourceName\": \"User\", \n        \"resourceIdentifier\": \"Name\"\n      }\n    }, \n    \"CreatePolicyVersion\": {\n      \"PolicyArn\": {\n        \"resourceName\": \"Policy\", \n        \"resourceIdentifier\": \"Arn\"\n      }\n    }, \n    \"AttachRolePolicy\": {\n      \"RoleName\": {\n        \"resourceName\": \"Role\", \n        \"resourceIdentifier\": \"Name\"\n      }\n    }, \n    \"RemoveUserFromGroup\": {\n      \"UserName\": {\n        \"resourceName\": \"User\", \n        \"resourceIdentifier\": \"Name\"\n      }\n    }, \n    \"AttachUserPolicy\": {\n      \"UserName\": {\n        \"resourceName\": \"User\", \n        \"resourceIdentifier\": \"Name\"\n      }\n    }, \n    \"PutUserPolicy\": {\n      \"UserName\": {\n        \"resourceName\": \"User\", \n        \"resourceIdentifier\": \"Name\"\n      }\n    }, \n    \"DeleteServerCertificate\": {\n      \"ServerCertificateName\": {\n        \"resourceName\": \"ServerCertificate\", \n        \"resourceIdentifier\": \"Name\"\n      }\n    }, \n    \"UpdateGroup\": {\n      \"GroupName\": {\n        \"resourceName\": \"Group\", \n        \"resourceIdentifier\": \"Name\"\n      }\n    }, \n    \"CreateUser\": {\n      \"UserName\": {\n        \"resourceName\": \"User\", \n        \"resourceIdentifier\": \"Name\"\n      }\n    }, \n    \"DetachGroupPolicy\": {\n      \"PolicyArn\": {\n        \"resourceName\": \"Policy\", \n        \"resourceIdentifier\": \"Arn\"\n      }\n    }, \n    \"AddRoleToInstanceProfile\": {\n      \"InstanceProfileName\": {\n        \"resourceName\": \"InstanceProfile\", \n        \"resourceIdentifier\": \"Name\"\n      }\n    }\n  }, \n  \"resources\": {\n    \"Group\": {\n      \"operation\": \"ListGroups\", \n      \"resourceIdentifier\": {\n        \"Name\": \"Groups[].GroupName\"\n      }\n    }, \n    \"VirtualMfaDevice\": {\n      \"operation\": \"ListVirtualMFADevices\", \n      \"resourceIdentifier\": {\n        \"SerialNumber\": \"VirtualMFADevices[].SerialNumber\"\n      }\n    }, \n    \"InstanceProfile\": {\n      \"operation\": \"ListInstanceProfiles\", \n      \"resourceIdentifier\": {\n        \"Name\": \"InstanceProfiles[].InstanceProfileName\"\n      }\n    }, \n    \"Role\": {\n      \"operation\": \"ListRoles\", \n      \"resourceIdentifier\": {\n        \"Name\": \"Roles[].RoleName\"\n      }\n    }, \n    \"User\": {\n      \"operation\": \"ListUsers\", \n      \"resourceIdentifier\": {\n        \"Name\": \"Users[].UserName\"\n      }\n    }, \n    \"Policy\": {\n      \"operation\": \"ListPolicies\", \n      \"resourceIdentifier\": {\n        \"Arn\": \"Policies[].Arn\"\n      }\n    }, \n    \"SamlProvider\": {\n      \"operation\": \"ListSAMLProviders\", \n      \"resourceIdentifier\": {\n        \"Arn\": \"SAMLProviderList[].Arn\"\n      }\n    }, \n    \"ServerCertificate\": {\n      \"operation\": \"ListServerCertificates\", \n      \"resourceIdentifier\": {\n        \"Name\": \"ServerCertificateMetadataList[].ServerCertificateName\"\n      }\n    }\n  }\n}"
  },
  {
    "path": "awsshell/data/kinesis/2013-12-02/completions-1.json",
    "content": "{\n  \"operations\": {\n    \"AddTagsToStream\": {\n      \"StreamName\": {\n        \"resourceName\": \"Stream\",\n        \"resourceIdentifier\": \"Name\"\n      }\n    },\n    \"DeleteStream\": {\n      \"StreamName\": {\n        \"resourceName\": \"Stream\",\n        \"resourceIdentifier\": \"Name\"\n      }\n    },\n    \"DecreaseStreamRetentionPeriod\": {\n      \"StreamName\": {\n        \"resourceName\": \"Stream\",\n        \"resourceIdentifier\": \"Name\"\n      }\n    },\n    \"DescribeStream\": {\n      \"StreamName\": {\n        \"resourceName\": \"Stream\",\n        \"resourceIdentifier\": \"Name\"\n      }\n    },\n    \"GetShardIterator\": {\n      \"StreamName\": {\n        \"resourceName\": \"Stream\",\n        \"resourceIdentifier\": \"Name\"\n      }\n    },\n    \"IncreaseStreamRetentionPeriod\": {\n      \"StreamName\": {\n        \"resourceName\": \"Stream\",\n        \"resourceIdentifier\": \"Name\"\n      }\n    },\n    \"ListTagsForStream\": {\n      \"StreamName\": {\n        \"resourceName\": \"Stream\",\n        \"resourceIdentifier\": \"Name\"\n      }\n    },\n    \"MergeShards\": {\n      \"StreamName\": {\n        \"resourceName\": \"Stream\",\n        \"resourceIdentifier\": \"Name\"\n      }\n    },\n    \"PutRecord\": {\n      \"StreamName\": {\n        \"resourceName\": \"Stream\",\n        \"resourceIdentifier\": \"Name\"\n      }\n    },\n    \"PutRecords\": {\n      \"StreamName\": {\n        \"resourceName\": \"Stream\",\n        \"resourceIdentifier\": \"Name\"\n      }\n    },\n    \"RemoveTagsFromStream\": {\n      \"StreamName\": {\n        \"resourceName\": \"Stream\",\n        \"resourceIdentifier\": \"Name\"\n      }\n    },\n    \"SplitShard\": {\n      \"StreamName\": {\n        \"resourceName\": \"Stream\",\n        \"resourceIdentifier\": \"Name\"\n      }\n    }\n  },\n  \"resources\": {\n    \"Stream\": {\n      \"operation\": \"ListStreams\",\n      \"resourceIdentifier\": {\n        \"Name\": \"StreamNames[]\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "awsshell/data/opsworks/2013-02-18/completions-1.json",
    "content": "{\n  \"operations\": {\n    \"CreateLayer\": {\n      \"StackId\": {\n        \"resourceName\": \"Stack\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }, \n    \"DeleteStack\": {\n      \"StackId\": {\n        \"resourceName\": \"Stack\", \n        \"resourceIdentifier\": \"Id\"\n      }\n    }\n  }, \n  \"resources\": {\n    \"Stack\": {\n      \"operation\": \"DescribeStacks\", \n      \"resourceIdentifier\": {\n        \"Id\": \"Stacks[].StackId\"\n      }\n    }\n  }\n}"
  },
  {
    "path": "awsshell/data/s3/2006-03-01/completions-1.json",
    "content": "{\n  \"operations\": {\n    \"CreateBucket\": {\n      \"Bucket\": {\n        \"resourceName\": \"Bucket\", \n        \"resourceIdentifier\": \"Name\"\n      }\n    }, \n    \"PutObject\": {\n      \"Bucket\": {\n        \"resourceName\": \"Bucket\", \n        \"resourceIdentifier\": \"Name\"\n      }\n    }, \n    \"DeleteObjects\": {\n      \"Bucket\": {\n        \"resourceName\": \"Bucket\", \n        \"resourceIdentifier\": \"Name\"\n      }\n    }, \n    \"DeleteBucket\": {\n      \"Bucket\": {\n        \"resourceName\": \"Bucket\", \n        \"resourceIdentifier\": \"Name\"\n      }\n    }\n  }, \n  \"resources\": {\n    \"Bucket\": {\n      \"operation\": \"ListBuckets\", \n      \"resourceIdentifier\": {\n        \"Name\": \"Buckets[].Name\"\n      }\n    }\n  }\n}"
  },
  {
    "path": "awsshell/data/sns/2010-03-31/completions-1.json",
    "content": "{\n  \"operations\": {\n    \"ConfirmSubscription\": {\n      \"TopicArn\": {\n        \"resourceName\": \"Topic\", \n        \"resourceIdentifier\": \"Arn\"\n      }\n    }, \n    \"SetPlatformApplicationAttributes\": {\n      \"PlatformApplicationArn\": {\n        \"resourceName\": \"PlatformApplication\", \n        \"resourceIdentifier\": \"Arn\"\n      }\n    }, \n    \"Subscribe\": {\n      \"TopicArn\": {\n        \"resourceName\": \"Topic\", \n        \"resourceIdentifier\": \"Arn\"\n      }\n    }, \n    \"SetTopicAttributes\": {\n      \"TopicArn\": {\n        \"resourceName\": \"Topic\", \n        \"resourceIdentifier\": \"Arn\"\n      }\n    }, \n    \"DeletePlatformApplication\": {\n      \"PlatformApplicationArn\": {\n        \"resourceName\": \"PlatformApplication\", \n        \"resourceIdentifier\": \"Arn\"\n      }\n    }, \n    \"SetSubscriptionAttributes\": {\n      \"SubscriptionArn\": {\n        \"resourceName\": \"Subscription\", \n        \"resourceIdentifier\": \"Arn\"\n      }\n    }, \n    \"Publish\": {\n      \"TopicArn\": {\n        \"resourceName\": \"Topic\", \n        \"resourceIdentifier\": \"Arn\"\n      }\n    }, \n    \"AddPermission\": {\n      \"TopicArn\": {\n        \"resourceName\": \"Topic\", \n        \"resourceIdentifier\": \"Arn\"\n      }\n    }, \n    \"Unsubscribe\": {\n      \"SubscriptionArn\": {\n        \"resourceName\": \"Subscription\", \n        \"resourceIdentifier\": \"Arn\"\n      }\n    }, \n    \"RemovePermission\": {\n      \"TopicArn\": {\n        \"resourceName\": \"Topic\", \n        \"resourceIdentifier\": \"Arn\"\n      }\n    }, \n    \"CreatePlatformEndpoint\": {\n      \"PlatformApplicationArn\": {\n        \"resourceName\": \"PlatformApplication\", \n        \"resourceIdentifier\": \"Arn\"\n      }\n    }, \n    \"DeleteTopic\": {\n      \"TopicArn\": {\n        \"resourceName\": \"Topic\", \n        \"resourceIdentifier\": \"Arn\"\n      }\n    }\n  }, \n  \"resources\": {\n    \"Topic\": {\n      \"operation\": \"ListTopics\", \n      \"resourceIdentifier\": {\n        \"Arn\": \"Topics[].TopicArn\"\n      }\n    }, \n    \"PlatformApplication\": {\n      \"operation\": \"ListPlatformApplications\", \n      \"resourceIdentifier\": {\n        \"Arn\": \"PlatformApplications[].PlatformApplicationArn\"\n      }\n    }, \n    \"Subscription\": {\n      \"operation\": \"ListSubscriptions\", \n      \"resourceIdentifier\": {\n        \"Arn\": \"Subscriptions[].SubscriptionArn\"\n      }\n    }\n  }\n}"
  },
  {
    "path": "awsshell/data/sqs/2012-11-05/completions-1.json",
    "content": "{\n  \"operations\": {\n    \"SetQueueAttributes\": {\n      \"QueueUrl\": {\n        \"resourceName\": \"Queue\", \n        \"resourceIdentifier\": \"Url\"\n      }\n    }, \n    \"SendMessageBatch\": {\n      \"QueueUrl\": {\n        \"resourceName\": \"Queue\", \n        \"resourceIdentifier\": \"Url\"\n      }\n    }, \n    \"DeleteMessageBatch\": {\n      \"QueueUrl\": {\n        \"resourceName\": \"Queue\", \n        \"resourceIdentifier\": \"Url\"\n      }\n    }, \n    \"AddPermission\": {\n      \"QueueUrl\": {\n        \"resourceName\": \"Queue\", \n        \"resourceIdentifier\": \"Url\"\n      }\n    }, \n    \"ChangeMessageVisibilityBatch\": {\n      \"QueueUrl\": {\n        \"resourceName\": \"Queue\", \n        \"resourceIdentifier\": \"Url\"\n      }\n    }, \n    \"SendMessage\": {\n      \"QueueUrl\": {\n        \"resourceName\": \"Queue\", \n        \"resourceIdentifier\": \"Url\"\n      }\n    }, \n    \"DeleteQueue\": {\n      \"QueueUrl\": {\n        \"resourceName\": \"Queue\", \n        \"resourceIdentifier\": \"Url\"\n      }\n    }, \n    \"PurgeQueue\": {\n      \"QueueUrl\": {\n        \"resourceName\": \"Queue\", \n        \"resourceIdentifier\": \"Url\"\n      }\n    }, \n    \"ReceiveMessage\": {\n      \"QueueUrl\": {\n        \"resourceName\": \"Queue\", \n        \"resourceIdentifier\": \"Url\"\n      }\n    }, \n    \"RemovePermission\": {\n      \"QueueUrl\": {\n        \"resourceName\": \"Queue\", \n        \"resourceIdentifier\": \"Url\"\n      }\n    }\n  }, \n  \"resources\": {\n    \"Queue\": {\n      \"operation\": \"ListQueues\", \n      \"resourceIdentifier\": {\n        \"Url\": \"QueueUrls[]\"\n      }\n    }\n  }\n}"
  },
  {
    "path": "awsshell/db.py",
    "content": "from __future__ import unicode_literals\nimport os\nimport sqlite3\n\n\nclass ConcurrentDBM(object):\n\n    @classmethod\n    def open(cls, filename, create=False):\n        if create and not os.path.isfile(filename):\n            return cls.create(filename)\n        else:\n            db = sqlite3.connect(filename)\n            return cls(db)\n\n    @classmethod\n    def create(cls, filename):\n        db = sqlite3.connect(filename)\n        with db:\n            db.execute(\n                'CREATE TABLE docindex (key TEXT PRIMARY KEY, value TEXT)')\n        return cls(db)\n\n    def __init__(self, db):\n        self._db = db\n\n    def __getitem__(self, key):\n        if isinstance(key, bytes):\n            key = key.decode('utf-8')\n        cursor = self._db.cursor()\n        cursor.execute(\n            'SELECT value FROM docindex WHERE key = :key', {'key': key})\n        result = cursor.fetchone()\n        if result is not None:\n            return result[0]\n        raise KeyError(key)\n\n    def __setitem__(self, key, value):\n        with self._db:\n            self._db.execute(\n                'INSERT OR REPLACE INTO docindex (key, value) '\n                'VALUES (:key, :value)',\n                {'key': key, 'value': value})\n\n    def close(self):\n        self._db.close()\n"
  },
  {
    "path": "awsshell/docs.py",
    "content": "from __future__ import unicode_literals\nfrom awsshell import db\n\n\ndef load_lazy_doc_index(filename):\n    d = load_doc_db(filename)\n    return DocRetriever(d)\n\n\ndef load_doc_db(filename):\n    d = db.ConcurrentDBM.open(filename, create=True)\n    return d\n\n\nclass DocRetriever(object):\n    \"\"\"Retrieve documentation for the AWS CLI.\"\"\"\n    def __init__(self, doc_index):\n        # Internally, most of the speedup comes from\n        # the fact that this data is pre-rendered and\n        # indexed.\n        self._doc_index = doc_index\n        self._cache = {}\n\n    def extract_description(self, dot_cmd):\n        try:\n            docs = self._doc_index[dot_cmd]\n        except KeyError:\n            return u''\n        index = docs.find('SYNOPSIS')\n        if index > 0:\n            docs = docs[:index]\n        return docs\n\n    def extract_param(self, dot_cmd, param_name):\n        try:\n            docs = self._doc_index[dot_cmd]\n        except KeyError:\n            return u''\n        index = docs.find('OPTIONS')\n        param_start_index = docs.find(param_name, index)\n        param_end_index = docs.find('--', param_start_index + 1)\n        return docs[param_start_index:param_end_index]\n"
  },
  {
    "path": "awsshell/fuzzy.py",
    "content": "\"\"\"Fuzzy finder for AWS Shell.\n\nThis is a fuzzy finder used for the autocompleter\nthat I've tried to optimize for the AWS CLI set\nof commands.\n\nThere doesn't seem to be a generic algorithm that\ndoes exactly what I want.  Changing any of these\nheuristics so far means that you have to give up\nsome other component of the idealized matching.\n\nThe main things I care about:\n\n* Special weight is given to subsequences on a\n  word boundary.  So \"drio\" scores higher for\n  \"describe-reserved-instances_offering\" than\n  \"create-spot-datafeed-subscription\".\n* The more of the word you complete, the higher\n  score it has, so given \"describe-instance\", then\n  \"describe-instances\" should match higher than\n  \"describe-instance-attribute\" because the former\n  matches every single character except for one.\n* Similar to one, \"rinstance\" should rank\n  \"run-instances\" higher than \"describe-instances\"\n  because the \"r\" falls on a word boundary.\n\nHigh Level Idea\n===============\n\nThe basic idea is to try to calculate a numeric\nscore between 0 and 1 given the users's search\nstring and a possible word in the corpus of words.\n\nYou calculate the score for each word and then sort\nthem appropriately and return the results in order\nback to the user.  A score of 1 is the highest\npossible score, it would represent an exact match,\nand 0 is the lowest meaning these is no possible chance\nfor the word to be a match.\n\n\"\"\"\nfrom __future__ import print_function\n\n\ndef fuzzy_search(user_input, corpus):\n    candidates = []\n    for word in corpus:\n        current_score = calculate_score(user_input, word)\n        if current_score > 0:\n            candidates.append((word, current_score))\n    return [c[0] for c in sorted(candidates, key=lambda x: x[1], reverse=True)]\n\n\ndef calculate_score(search_string, word):\n    \"\"\"Calculate how well the search string matches the word.\"\"\"\n    # See the module docstring for a high level description\n    # of what we're trying to do.\n    # * If the search string is larger than the word, we know\n    #   immediately that this can't be a match.\n    if len(search_string) > len(word):\n        return 0\n    original_word = word\n    score = 1\n    search_index = 0\n    while True:\n        scale = 1.0\n        search_char = search_string[search_index]\n        i = word.find(search_char)\n        if i < 0:\n            return 0\n        if i > 0 and word[i - 1] == '-':\n            scale = 0.95\n        else:\n            scale = 1 - (i / float(len(word)))\n        score *= scale\n        word = word[i + 1:]\n        search_index += 1\n        if search_index >= len(search_string):\n            break\n    # The more characters that matched the word, the better\n    # so prefer more complete matches.\n    completion_scale = 1 - (len(word) / float(len(original_word)))\n    score *= completion_scale\n    return score\n"
  },
  {
    "path": "awsshell/index/__init__.py",
    "content": "\"\"\"Subpackage for indexing data.\n\nNote that, while there is documentation for all the code in this\npackage, these modules/classes are considered internal and not\nintended for use outside of the awsshell.  It's very likely that\nthese interfaces will change over time and may have backwards incompatible\nchanges as improvements are made.\n\nThis package contains modules for working with the index data that the AWS\nShell uses.  It manages the creation and loading of the AWS Shell index files.\n\nIn this context, an index file is specifically used to speed up various\noperations performed by the AWS CLI: autocompletion, pulling up docs,\nserver side resource completion, etc.  In typically consists of data constructed\nin such a way to make it quick and easy to answer questions such as\n\"what are the available subcommands?  what are the docs for this operation?\nwhat arguments does this command take?\".\n\nThe AWS Shell supports has several types of index files:\n\n* The local command completion index.\n* The documentation index.\n* The server side resource completion index.\n\nThey're split into separate files because they're used in different scenarios,\nand generated by different means (command completion is driven by botocore\nJSON models, and server side completion is driven by boto3 resource models).\n\nCouple of things to know about the index files:\n\n* The command completion and doc index are indexed by CLI version.  Each version\nof the CLI can potentially add new commands/services, so we want to ensure that\nwe have an index per version so we can offer the correct autocompletion and\ndocumentation based on what CLI version you have installed.\n* Command completion and documentation are automatically generated based\non demand. If we notice you don't have an index available for the particular\nCLI version, we automatically generate the data.\n* The total amount of documentation for every command for every service is\nlarge.  We want to avoid loading the entire docset into memory, so we're not\nusing JSON.  Instead we're using a DBM interface (via ``shelve``).  May\nalso consider sqlite.\n* Server side resource completion is *not* generated on demand.  This package\ncontains code for generating the a \"reverse index\" derived from boto3 docs.\nThis is primarily done while we explore to what extent the boto3 resource\nmodels have the information we need.  To be fair, the resource models were\nnever intended to be used in the mechanism in which we're using them, so it's\nvery possible we may need to enhance the models by hand.  This is why we\npre-generate the index for server side completion up front so we can add hand\nmodifications if needed.  It's very possible that we may be able to come up\nwith something sufficiently sophisticated enough where we can change this index\nto by autogenerated on demand like the other two.\n* While the indexes are grouped in a single subpackage for discoverability,\nthey (purposefully) do not share the same interface for usage.  Each\nindex needs to answer different questions, so it does not make sense\nfor them to share the same interface.\n\nUsage\n=====\n\nLoading the completion index\n\n.. code-block:: python\n\n  # Default usage:\n\n  loader = CompletionIndex()\n  # Load based on the current CLI version you have installed\n  # (i.e awscli.__version__)\n  index = loader.load_index()\n\n  # Load from a specific directory.\n  loader = CompletionIndex(cache_dir='/tmp/mycache')\n  index = loader.load_index()\n\n  # Load a specific version.\n\n  loader = CompletionIndex()\n  index = loader.load_index(version='1.9.1')\n\n  # Generating an index.\n\n  loader = CompletionIndex()\n  # You don't give it a version because we can only\n  # generate an index based on the version we can import.\n  completion_index = loader.generate_index()\n  # You don't need to give a filename, it will write it out\n  # based on the current version.\n  loader.write_index_to_file(completion_index)\n\n  # Generate and write the index in one step.  It will also\n  # return the generated index in case you need it.\n  loader.generate_and_write_index()\n\n  # This is some autocompleter could use the index loader:\n\n  loader = CompletionIndex()\n  if not loader.index_exists():\n      index = loader.generate_and_write_index()\n  else:\n      index = loader.load_index()\n\n  autocompleter = MyAutoCompleter(index)\n\n\"\"\"\n"
  },
  {
    "path": "awsshell/index/completion.py",
    "content": "\"\"\"Module for completion index.\n\nGenerates, loads, and writes out completion index.\nAlso provides an interface for working with the\nindexed data.\n\nThe the subpackage docstring of awsshell.index for\na higher level overview.\n\n\"\"\"\nimport os\nimport json\n\nfrom awsshell.utils import FSLayer, FileReadError, build_config_file_path\nfrom awsshell import utils\n\n\nclass IndexLoadError(Exception):\n    \"\"\"Raised when an index could not be loaded.\"\"\"\n\n\nclass CompletionIndex(object):\n    \"\"\"Handles working with the local commmand completion index.\n\n    :type commands: list\n    :param commands: ec2, s3, elb...\n\n    :type subcommands: list\n    :param subcommands: start-instances, stop-instances, terminate-instances...\n\n    :type global_opts: list\n    :param global_opts: --profile, --region, --output...\n\n    :type args_opts: set, to filter out duplicates\n    :param args_opts: ec2 start-instances: --instance-ids, --dry-run...\n    \"\"\"\n\n    # The completion index can read/write to a cache dir\n    # so that it doesn't have to recompute the completion cache\n    # every time the CLI starts up.\n    DEFAULT_CACHE_DIR = build_config_file_path('cache')\n\n    def __init__(self, cache_dir=DEFAULT_CACHE_DIR, fslayer=None):\n        self._cache_dir = cache_dir\n        if fslayer is None:\n            fslayer = FSLayer()\n        self._fslayer = fslayer\n        self.commands = []\n        self.subcommands = []\n        self.global_opts = []\n        self.args_opts = set()\n\n    def load_index(self, version_string):\n        \"\"\"Load the completion index for a given CLI version.\n\n        :type version_string: str\n        :param version_string: The AWS CLI version, e.g \"1.9.2\".\n\n        :raises: :class:`IndexLoadError <exceptions.IndexLoadError>`\n        \"\"\"\n        filename = self._filename_for_version(version_string)\n        try:\n            contents = self._fslayer.file_contents(filename)\n        except FileReadError as e:\n            raise IndexLoadError(str(e))\n        return contents\n\n    def _filename_for_version(self, version_string):\n        return os.path.join(\n            self._cache_dir, 'completions-%s.json' % version_string)\n\n    def load_completions(self):\n        \"\"\"Load completions from the completion index.\n\n        Updates the following attributes:\n            * commands\n            * subcommands\n            * global_opts\n            * args_opts\n        \"\"\"\n        try:\n            index_str = self.load_index(utils.AWSCLI_VERSION)\n        except IndexLoadError:\n            return\n        index_str = self.load_index(utils.AWSCLI_VERSION)\n        index_data = json.loads(index_str)\n        index_root = index_data['aws']\n        # ec2, s3, elb...\n        self.commands = index_root['commands']\n        # --profile, --region, --output...\n        self.global_opts = index_root['arguments']\n        for command in self.commands:\n            # ec2: start-instances, stop-instances, terminate-instances...\n            subcommands_current = index_root['children'] \\\n                .get(command)['commands']\n            self.subcommands.extend(subcommands_current)\n            for subcommand_current in subcommands_current:\n                # start-instances: --instance-ids, --dry-run...\n                args_opts_current = index_root['children'] \\\n                    .get(command)['children'] \\\n                    .get(subcommand_current)['arguments']\n                self.args_opts.update(args_opts_current)\n"
  },
  {
    "path": "awsshell/keys.py",
    "content": "# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\"). You\n# may not use this file except in compliance with the License. A copy of\n# the License is located at\n#\n#     http://aws.amazon.com/apache2.0/\n#\n# or in the \"license\" file accompanying this file. This file is\n# distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF\n# ANY KIND, either express or implied. See the License for the specific\n# language governing permissions and limitations under the License.\nfrom prompt_toolkit.key_binding.manager import KeyBindingManager\nfrom prompt_toolkit.keys import Keys\n\n\nclass KeyManager(object):\n    \"\"\"A custom :class:`prompt_toolkit.KeyBindingManager`.\n\n    Handles togging of:\n        * Fuzzy or substring matching.\n        * Vi or Emacs key bindings.\n        * Multi or single columns in the autocompletion menu.\n        * Showing or hiding the help pane.\n\n    :type manager: :class:`prompt_toolkit.KeyBindingManager`\n    :param manager: A custom `KeyBindingManager`.\n    \"\"\"\n\n    def __init__(self, get_match_fuzzy, set_match_fuzzy,\n                 get_enable_vi_bindings, set_enable_vi_bindings,\n                 get_show_completion_columns, set_show_completion_columns,\n                 get_show_help, set_show_help, stop_input_and_refresh_cli):\n        self.manager = None\n        self._create_key_manager(\n            get_match_fuzzy, set_match_fuzzy,\n            get_enable_vi_bindings, set_enable_vi_bindings,\n            get_show_completion_columns, set_show_completion_columns,\n            get_show_help, set_show_help, stop_input_and_refresh_cli)\n\n    def _create_key_manager(self, get_match_fuzzy, set_match_fuzzy,\n                            get_enable_vi_bindings, set_enable_vi_bindings,\n                            get_show_completion_columns,\n                            set_show_completion_columns,\n                            get_show_help, set_show_help,\n                            stop_input_and_refresh_cli):\n        \"\"\"Create and initialize the keybinding manager.\n\n        :type get_fuzzy_match: callable\n        :param get_fuzzy_match: Gets the fuzzy matching config.\n\n        :type set_fuzzy_match: callable\n        :param set_fuzzy_match: Sets the fuzzy matching config.\n\n        :type get_enable_vi_bindings: callable\n        :param get_enable_vi_bindings: Gets the vi (or emacs) key bindings\n            config.\n\n        :type set_enable_vi_bindings: callable\n        :param set_enable_vi_bindings: Sets the vi (or emacs) key bindings\n            config.\n\n        :type get_show_completion_columns: callable\n        :param get_show_completion_columns: Gets the show completions in\n            multiple or single columns config.\n\n        type set_show_completion_columns: callable\n        :param set_show_completion_columns: Sets the show completions in\n            multiple or single columns config.\n\n        :type get_show_help: callable\n        :param get_show_help: Gets the show help pane config.\n\n        :type set_show_help: callable\n        :param set_show_help: Sets the show help pane config.\n\n        :type stop_input_and_refresh_cli: callable\n        param stop_input_and_refresh_cli: Stops input by raising an\n            `InputInterrupt`, forces a cli refresh to ensure certain\n            options take effect within the current session.\n\n        :rtype: :class:`prompt_toolkit.KeyBindingManager`\n        :return: A custom `KeyBindingManager`.\n\n        \"\"\"\n        assert callable(get_match_fuzzy)\n        assert callable(set_match_fuzzy)\n        assert callable(get_enable_vi_bindings)\n        assert callable(set_enable_vi_bindings)\n        assert callable(get_show_completion_columns)\n        assert callable(set_show_completion_columns)\n        assert callable(get_show_help)\n        assert callable(set_show_help)\n        assert callable(stop_input_and_refresh_cli)\n        self.manager = KeyBindingManager(\n            enable_search=True,\n            enable_abort_and_exit_bindings=True,\n            enable_system_bindings=True,\n            enable_auto_suggest_bindings=True,\n            enable_open_in_editor=False)\n\n        @self.manager.registry.add_binding(Keys.F2)\n        def handle_f2(_):\n            \"\"\"Toggle fuzzy matching.\n\n            :type _: :class:`prompt_toolkit.Event`\n            :param _: (Unused)\n\n            \"\"\"\n            set_match_fuzzy(not get_match_fuzzy())\n\n        @self.manager.registry.add_binding(Keys.F3)\n        def handle_f3(_):\n            \"\"\"Toggle Vi mode keybindings matching.\n\n            Disabling Vi keybindings will enable Emacs keybindings.\n\n            :type _: :class:`prompt_toolkit.Event`\n            :param _: (Unused)\n\n            \"\"\"\n            set_enable_vi_bindings(not get_enable_vi_bindings())\n            stop_input_and_refresh_cli()\n\n        @self.manager.registry.add_binding(Keys.F4)\n        def handle_f4(_):\n            \"\"\"Toggle multiple column completions.\n\n            :type _: :class:`prompt_toolkit.Event`\n            :param _: (Unused)\n\n            \"\"\"\n            set_show_completion_columns(not get_show_completion_columns())\n            stop_input_and_refresh_cli()\n\n        @self.manager.registry.add_binding(Keys.F5)\n        def handle_f5(_):\n            \"\"\"Toggle the help container.\n\n            :type _: :class:`prompt_toolkit.Event`\n            :param _: (Unused)\n\n            \"\"\"\n            set_show_help(not get_show_help())\n            stop_input_and_refresh_cli()\n\n        @self.manager.registry.add_binding(Keys.F9)\n        def handle_f9(event):\n            \"\"\"Switch between the default and docs buffers.\n\n            :type event: :class:`prompt_toolkit.Event`\n            :param event: Contains info about the event, namely the cli\n                which is used to changing which buffer is focused.\n\n            \"\"\"\n            if event.cli.current_buffer_name == u'clidocs':\n                event.cli.focus(u'DEFAULT_BUFFER')\n            else:\n                event.cli.focus(u'clidocs')\n\n        @self.manager.registry.add_binding(Keys.F10)\n        def handle_f10(event):\n            \"\"\"Quit when the `F10` key is pressed.\n\n            :type event: :class:`prompt_toolkit.Event`\n            :param event: Contains info about the event, namely the cli\n                which is used for exiting the app.\n\n            \"\"\"\n            event.cli.set_exit()\n"
  },
  {
    "path": "awsshell/lexer.py",
    "content": "# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\"). You\n# may not use this file except in compliance with the License. A copy of\n# the License is located at\n#\n#     http://aws.amazon.com/apache2.0/\n#\n# or in the \"license\" file accompanying this file. This file is\n# distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF\n# ANY KIND, either express or implied. See the License for the specific\n# language governing permissions and limitations under the License.\nfrom pygments.lexer import RegexLexer\nfrom pygments.lexer import words\nfrom pygments.token import Keyword, Literal, Name, Operator, Text\n\nfrom awsshell.index.completion import CompletionIndex\n\n\nclass ShellLexer(RegexLexer):\n    \"\"\"Provides highlighting for commands, subcommands, arguments, and options.\n\n    :type completion_index: :class:`CompletionIndex`\n    :param completion_index: Completion index used to determine commands,\n        subcommands, arguments, and options for highlighting.\n\n    :type tokens: dict\n    :param tokens: A dict of (`pygments.lexer`, `pygments.token`) used for\n        pygments highlighting.\n    \"\"\"\n    completion_index = CompletionIndex()\n    completion_index.load_completions()\n    tokens = {\n        'root': [\n            # ec2, s3, elb...\n            (words(\n                tuple(completion_index.commands),\n                prefix=r'\\b',\n                suffix=r'\\b'),\n             Literal.String),\n            # describe-instances\n            (words(\n                tuple(completion_index.subcommands),\n                prefix=r'\\b',\n                suffix=r'\\b'),\n             Name.Class),\n            # --instance-ids\n            (words(\n                tuple(list(completion_index.args_opts)),\n                prefix=r'',\n                suffix=r'\\b'),\n             Keyword.Declaration),\n            # --profile\n            (words(\n                tuple(completion_index.global_opts),\n                prefix=r'',\n                suffix=r'\\b'),\n             Operator.Word),\n            # Everything else\n            (r'.*\\n', Text),\n        ]\n    }\n"
  },
  {
    "path": "awsshell/loaders.py",
    "content": "import json\n\nfrom awsshell.utils import build_config_file_path\n\n\nclass JSONIndexLoader(object):\n    def __init__(self):\n        pass\n\n    @staticmethod\n    def index_filename(version_string, type_name='completions'):\n        return build_config_file_path(\n            '%s-%s.json' % (version_string, type_name))\n\n    def load_index(self, filename):\n        with open(filename, 'r') as f:\n            return json.load(f)\n"
  },
  {
    "path": "awsshell/makeindex.py",
    "content": "\"\"\"Module for building the autocompletion indices.\"\"\"\nfrom __future__ import print_function\nimport os\nimport json\n\nfrom six import BytesIO\nfrom docutils.core import publish_string\nimport awscli.clidriver\nfrom awscli.argprocess import ParamShorthandDocGen\ntry:\n    from botocore.docs.bcdoc import textwriter\nexcept ImportError:\n    from awscli.bcdoc import textwriter\n\nfrom awsshell import determine_doc_index_filename\nfrom awsshell.utils import remove_html\nfrom awsshell import docs\n\n\nSHORTHAND_DOC = ParamShorthandDocGen()\n\n\ndef new_index():\n    return {'arguments': [], 'argument_metadata': {},\n            'commands': [], 'children': {}}\n\n\ndef index_command(index_dict, help_command):\n    arg_table = help_command.arg_table\n    for arg in arg_table:\n        arg_obj = arg_table[arg]\n        metadata = {\n            'required': arg_obj.required,\n            'type_name': arg_obj.cli_type_name,\n            'minidoc': '',\n            'example': '',\n            # The name used in the API call/botocore,\n            # typically CamelCased.\n            'api_name': getattr(arg_obj, '_serialized_name', '')\n        }\n        if arg_obj.documentation:\n            metadata['minidoc'] = remove_html(\n                arg_obj.documentation.split('\\n')[0])\n        if SHORTHAND_DOC.supports_shorthand(arg_obj.argument_model):\n            service_name, op_name = help_command.event_class.rsplit('.', 1)\n            example = SHORTHAND_DOC.generate_shorthand_example(\n                cli_argument=arg_obj,\n                service_id=service_name,\n                operation_name=op_name,\n            )\n            metadata['example'] = example\n\n        index_dict['arguments'].append('--%s' % arg)\n        index_dict['argument_metadata']['--%s' % arg] = metadata\n    for cmd in help_command.command_table:\n        index_dict['commands'].append(cmd)\n        # Each sub command will trigger a recurse.\n        child = new_index()\n        index_dict['children'][cmd] = child\n        sub_command = help_command.command_table[cmd]\n        sub_help_command = sub_command.create_help_command()\n        index_command(child, sub_help_command)\n\n\ndef write_index(output_filename=None):\n    driver = awscli.clidriver.create_clidriver()\n    help_command = driver.create_help_command()\n    index = {'aws': new_index()}\n    current = index['aws']\n    index_command(current, help_command)\n\n    result = json.dumps(index)\n    if not os.path.isdir(os.path.dirname(output_filename)):\n        os.makedirs(os.path.dirname(output_filename))\n    with open(output_filename, 'w') as f:\n        f.write(result)\n\n\ndef write_doc_index(output_filename=None, db=None, help_command=None):\n    if output_filename is None:\n        output_filename = determine_doc_index_filename()\n    user_provided_db = True\n    if db is None:\n        user_provided_db = False\n        db = docs.load_doc_db(output_filename)\n    if help_command is None:\n        driver = awscli.clidriver.create_clidriver()\n        help_command = driver.create_help_command()\n\n    should_close = not user_provided_db\n    do_write_doc_index(db, help_command, close_db_on_finish=should_close)\n\n\ndef do_write_doc_index(db, help_command, close_db_on_finish):\n    try:\n        _index_docs(db, help_command)\n        db['__complete__'] = 'true'\n    finally:\n        if close_db_on_finish:\n            # If the user provided their own db object,\n            # they are responsible for closing it.\n            # If we created our own db object, we own\n            # closing the db.\n            db.close()\n\n\ndef _index_docs(db, help_command):\n    for command_name in help_command.command_table:\n        command = help_command.command_table[command_name]\n        sub_help_command = command.create_help_command()\n        text_docs = render_docs_for_cmd(sub_help_command)\n        dotted_name = '.'.join(['aws'] + command.lineage_names)\n        db[dotted_name] = text_docs\n        _index_docs(db, sub_help_command)\n\n\ndef render_docs_for_cmd(help_command):\n    renderer = FileRenderer()\n    help_command.renderer = renderer\n    help_command(None, None)\n    # The report_level override is so that we don't print anything\n    # to stdout/stderr on rendering issues.\n    original_cli_help = renderer.contents.decode('utf-8')\n    text_content = convert_rst_to_basic_text(original_cli_help)\n    index = text_content.find('DESCRIPTION')\n    if index > 0:\n        text_content = text_content[index + len('DESCRIPTION'):]\n    return text_content\n\n\ndef convert_rst_to_basic_text(contents):\n    \"\"\"Convert restructured text to basic text output.\n\n    This function removes most of the decorations added\n    in restructured text.\n\n    This function is used to generate documentation we\n    can show to users in a cross platform manner.\n\n    Basic indentation and list formatting are kept,\n    but many RST features are removed (such as\n    section underlines).\n\n    \"\"\"\n    # The report_level override is so that we don't print anything\n    # to stdout/stderr on rendering issues.\n    converted = publish_string(\n        contents, writer=BasicTextWriter(),\n        settings_overrides={'report_level': 5})\n    return converted.decode('utf-8')\n\n\nclass FileRenderer(object):\n\n    def __init__(self):\n        self._io = BytesIO()\n\n    def render(self, contents):\n        self._io.write(contents)\n\n    @property\n    def contents(self):\n        return self._io.getvalue()\n\n\nclass BasicTextWriter(textwriter.TextWriter):\n    def translate(self):\n        visitor = BasicTextTranslator(self.document)\n        self.document.walkabout(visitor)\n        self.output = visitor.body\n\n\nclass BasicTextTranslator(textwriter.TextTranslator):\n    def depart_title(self, node):\n        # Make the section titles upper cased, similar to\n        # the man page output.\n        text = ''.join(x[1] for x in self.states.pop() if x[0] == -1)\n        self.stateindent.pop()\n        self.states[-1].append((0, ['', text.upper(), '']))\n\n    # The botocore TextWriter has additional formatting\n    # for literals, for the aws-shell docs we don't want any\n    # special processing so these nodes are noops.\n\n    def visit_literal(self, node):\n        pass\n\n    def depart_literal(self, node):\n        pass\n"
  },
  {
    "path": "awsshell/resource/__init__.py",
    "content": ""
  },
  {
    "path": "awsshell/resource/index.py",
    "content": "\"\"\"Index and retrive information from the resource JSON.\n\nThe classes are organized as follows:\n\n* ResourceIndexBuilder - Takes a boto3 resource and converts into the\n  index format we need to do server side completions.\n* CompleterDescriber - Takes the index from ResourceIndexBuilder and looks\n  up how to perform the autocompletion.  Note that this class does\n  *not* actually do the autocompletion.  It merely tells you how\n  you _would_ do the autocompletion if you made the appropriate\n  service calls.\n* ServerSideCompleter - The thing that does the actual autocompletion.\n  You tell it the command/operation/param you're on, and it will\n  return a list of completions for you.\n\n\"\"\"\nimport os\nimport logging\nfrom collections import namedtuple\n\nimport jmespath\nfrom botocore import xform_name\nfrom botocore.exceptions import BotoCoreError\n\nLOG = logging.getLogger(__name__)\n\n# service - The name of the AWS service\n# operation - The name of the AWS operation\n# params - A dict of params to send in the request (not implemented yet)\n# path - A JMESPath expression to select the expected elements.\nServerCompletion = namedtuple('ServerCompletion',\n                              ['service', 'operation', 'params', 'path'])\n\n\ndef extract_field_from_jmespath(expression):\n    result = jmespath.compile(expression)\n    current = result.parsed\n    while current['children']:\n        current = current['children'][0]\n    if current['type'] == 'field':\n        return current['value']\n\n\nclass ResourceIndexBuilder(object):\n    def __init__(self):\n        pass\n\n    def build_index(self, resource_data):\n        # First we need to go through the 'resources'\n        # key and map all of its actions back to the\n        # resource name.\n        index = {\n            'operations': {},\n            'resources': {},\n        }\n        service = resource_data['service']\n        if 'hasMany' in service:\n            for model in service['hasMany'].values():\n                resource_name = model['resource']['type']\n                for identifier in model['resource']['identifiers']:\n                    first_identifier = model['resource']['identifiers'][0]\n                    index['resources'][resource_name] = {\n                        'operation': model['request']['operation'],\n                        # TODO: map all the identifiers.\n                        # We're only taking the first one for now.\n                        'resourceIdentifier': {\n                            first_identifier['target']: first_identifier['path']\n                        }\n                    }\n        for resource_name, model in resource_data['resources'].items():\n            if resource_name not in index['resources']:\n                continue\n            if 'actions' in model:\n                resource_actions = model['actions']\n                for action_model in resource_actions.values():\n                    op_name = action_model['request']['operation']\n                    current = {}\n                    index['operations'][op_name] = current\n                    for param in action_model['request']['params']:\n                        if param['source'] == 'identifier':\n                            field_name = extract_field_from_jmespath(\n                                param['target'])\n                            current[field_name] = {\n                                'resourceName': resource_name,\n                                'resourceIdentifier': param['name'],\n                            }\n        return index\n\n\nclass CompleterDescriber(object):\n    \"\"\"Describes how to autocomplete a resource.\n\n    You give this class a service/operation/param and it will\n    describe to you how you can autocomplete values for the\n    provided parameter.\n\n    It's up to the caller to actually take that description\n    and make the appropriate service calls + filtering to\n    extract out the server side values.\n\n    \"\"\"\n    def __init__(self, resource_index):\n        self._index = resource_index\n\n    def describe_autocomplete(self, service, operation, param):\n        \"\"\"Describe operation and args needed for server side completion.\n\n        :type service: str\n        :param service: The AWS service name.\n\n        :type operation: str\n        :param operation: The AWS operation name.\n\n        :type param: str\n        :param param: The name of the parameter being completed.  This must\n            match the casing in the service model (e.g. InstanceIds, not\n            --instance-ids).\n\n        :rtype: ServerCompletion\n        :return: A ServerCompletion object that describes what API call to make\n            in order to complete the response.\n\n        \"\"\"\n        service_index = self._index[service]\n        LOG.debug(service_index)\n        if param not in service_index.get('operations', {}).get(operation, {}):\n            LOG.debug(\"param not in index: %s\", param)\n            return None\n        p = service_index['operations'][operation][param]\n        resource_name = p['resourceName']\n        resource_identifier = p['resourceIdentifier']\n\n        resource_index = service_index['resources'][resource_name]\n        completion_operation = resource_index['operation']\n        path = resource_index['resourceIdentifier'][resource_identifier]\n        return ServerCompletion(service=service, operation=completion_operation,\n                                params={}, path=path)\n\n\nclass CachedClientCreator(object):\n    def __init__(self, session):\n        #: A botocore.session.Session object.  Only the\n        #: create_client() method is used.\n        self._session = session\n        self._client_cache = {}\n\n    def create_client(self, service_name):\n        if service_name not in self._client_cache:\n            client = self._session.create_client(service_name)\n            self._client_cache[service_name] = client\n        return self._client_cache[service_name]\n\n\nclass CompleterDescriberCreator(object):\n    \"\"\"Create and cache CompleterDescriber objects.\"\"\"\n    def __init__(self, loader):\n        #: A botocore.loader.Loader\n        self._loader = loader\n        self._describer_cache = {}\n        self._services_with_completions = None\n\n    def create_completer_query(self, service_name):\n        \"\"\"Create a CompleterDescriber for a service.\n\n        :type service_name: str\n        :param service_name: The name of the service, e.g. 'ec2'\n\n        :return: A CompleterDescriber object.\n\n        \"\"\"\n        if service_name not in self._describer_cache:\n            query = self._create_completer_query(service_name)\n            self._describer_cache[service_name] = query\n        return self._describer_cache[service_name]\n\n    def _create_completer_query(self, service_name):\n        completions_model = self._loader.load_service_model(\n            service_name, 'completions-1')\n        cq = CompleterDescriber({service_name: completions_model})\n        return cq\n\n    def services_with_completions(self):\n        if self._services_with_completions is not None:\n            return self._services_with_completions\n        self._services_with_completions = set(\n            self._loader.list_available_services(type_name='completions-1'))\n        return self._services_with_completions\n\n\nclass ServerSideCompleter(object):\n    def __init__(self, client_creator, describer_creator):\n        self._client_creator = client_creator\n        self._describer_creator = describer_creator\n\n    def retrieve_candidate_values(self, service, operation, param):\n        \"\"\"Retrieve server side completions.\n\n        :type service: str\n        :param service: The service name, e.g. 'ec2', 'iam'.\n\n        :type operation: str\n        :param operation: The operation name, in the casing\n            used by the CLI (words separated by hyphens), e.g.\n            'describe-instances', 'delete-user'.\n\n        :type param: str\n        :param param: The param name, as specified in the service\n            model, e.g. 'InstanceIds', 'UserName'.\n\n        :rtype: list\n        :return: A list of possible completions for the\n            service/operation/param combination.  If no\n            completions were found an empty list is returned.\n\n        \"\"\"\n        # Example call:\n        # service='ec2',\n        # operation='terminate-instances',\n        # param='InstanceIds'.\n        if service not in self._describer_creator.services_with_completions():\n            return []\n        try:\n            client = self._client_creator.create_client(service)\n        except BotoCoreError as e:\n            # create_client() could raise an exception if the session\n            # isn't fully configured (say it's missing a region).\n            # However, we don't want to turn off all server side\n            # completions because it's still possible to create\n            # clients for some services without a region, e.g. IAM.\n            LOG.debug(\"Error when trying to create a client for %s\",\n                      service, exc_info=True)\n            return []\n        api_operation_name = client.meta.method_to_api_mapping.get(\n            operation.replace('-', '_'))\n        if api_operation_name is None:\n            return []\n        # Now we need to convert the param name to the\n        # casing used by the API.\n        completer = self._describer_creator.create_completer_query(service)\n        result = completer.describe_autocomplete(\n            service, api_operation_name, param)\n        if result is None:\n            return\n        try:\n            response = getattr(client, xform_name(result.operation, '_'))()\n        except Exception as e:\n            LOG.debug(\"Error when calling %s.%s: %s\", service,\n                      result.operation, e, exc_info=True)\n            return\n        results = jmespath.search(result.path, response)\n        return results\n\n\ndef main():\n    # Generate the latest autocompletion indices from\n    # boto3.  You'll need to do this if you pull in\n    # a new boto3 version that has updated resource models.\n    import sys\n    import json\n    import os\n    import boto3.session\n    data_dir = os.path.join(\n        os.path.dirname(os.path.dirname(os.path.abspath(__file__))),\n        'data')\n    if not os.path.isdir(data_dir):\n        os.makedirs(data_dir)\n    session = boto3.session.Session()\n    loader = session._loader\n    builder = ResourceIndexBuilder()\n    for resource_name in session.get_available_resources():\n        api_version = loader.determine_latest_version(\n            resource_name, 'resources-1')\n        model = loader.load_service_model(resource_name, 'resources-1',\n                                          api_version)\n        index = builder.build_index(model)\n        output_file = os.path.join(data_dir, resource_name, api_version,\n                                   'completions-1.json')\n        if not os.path.isdir(os.path.dirname(output_file)):\n            os.makedirs(os.path.dirname(output_file))\n        with open(output_file, 'w') as f:\n            f.write(json.dumps(index, indent=2))\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "awsshell/shellcomplete.py",
    "content": "\"\"\"Autocompletion integration with python prompt toolkit.\n\nThis module integrates the low level autocomplete functionality\nprovided in awsshell.autocomplete and integrates it with the\ninterface required for autocompletion in the python prompt\ntoolkit.\n\nIf you're interested in the heavy lifting of the autocompletion\nlogic, see awsshell.autocomplete.\n\n\"\"\"\nimport os\nimport logging\n\nimport botocore.session\nfrom prompt_toolkit.completion import Completer, Completion\n\nfrom awsshell import fuzzy\n\n\nLOG = logging.getLogger(__name__)\n\n\nclass AWSShellCompleter(Completer):\n    \"\"\"Completer class for the aws-shell.\n\n    This is the completer used specifically for the aws shell.\n    Not to be confused with the AWSCLIModelCompleter, which is more\n    low level, and can be reused in contexts other than the\n    aws shell.\n    \"\"\"\n    def __init__(self, completer, server_side_completer=None):\n        self._completer = completer\n        if server_side_completer is None:\n            server_side_completer = self._create_server_side_completer()\n        self._server_side_completer = server_side_completer\n\n    def _create_server_side_completer(self, session=None):\n        from awsshell.resource import index\n        if session is None:\n            session = botocore.session.Session()\n        loader = session.get_component('data_loader')\n        completions_path = os.path.join(\n            os.path.dirname(os.path.abspath(__file__)),\n            'data')\n        loader.search_paths.insert(0, completions_path)\n\n        client_creator = index.CachedClientCreator(session)\n        describer = index.CompleterDescriberCreator(loader)\n        completer = index.ServerSideCompleter(client_creator, describer)\n        return completer\n\n    def change_profile(self, profile_name):\n        \"\"\"Change the profile used for server side completions.\"\"\"\n        self._server_side_completer = self._create_server_side_completer(\n            session=botocore.session.Session(profile=profile_name))\n\n    @property\n    def completer(self):\n        return self._completer\n\n    @completer.setter\n    def completer(self, value):\n        self._completer = value\n\n    @property\n    def last_option(self):\n        return self._completer.last_option\n\n    @property\n    def current_command(self):\n        return u' '.join(self._completer.cmd_path)\n\n    def _convert_to_prompt_completions(self, low_level_completions,\n                                       text_before_cursor):\n        # Converts the low level completions from the model autocompleter\n        # and converts them to Completion() objects used by\n        # prompt_toolkit.  We also try to enhance the metadata of the\n        # completion by including docs and marking required fields.\n        arg_meta = self._completer.arg_metadata\n        arg_meta.update(self._completer.global_arg_metadata)\n        word_before_cursor = ''\n        if text_before_cursor.strip():\n            word_before_cursor = text_before_cursor.strip().split()[-1]\n        for completion in low_level_completions:\n            # Go through the completions and add inline docs and\n            # mark which options are required.\n            if completion.startswith('--') and completion in arg_meta:\n                # TODO: Need to handle merging in global options as well.\n                meta = arg_meta[completion]\n                if meta['required']:\n                    display_text = '%s (required)' % completion\n                else:\n                    display_text = completion\n                type_name = arg_meta[completion]['type_name']\n                display_meta = '[%s] %s' % (type_name,\n                                            arg_meta[completion]['minidoc'])\n            else:\n                display_text = completion\n                display_meta = ''\n            if text_before_cursor and text_before_cursor[-1] == ' ':\n                location = 0\n            else:\n                location = -len(word_before_cursor)\n            yield Completion(completion, location,\n                             display=display_text, display_meta=display_meta)\n\n    def get_completions(self, document, complete_event):\n        text_before_cursor = document.text_before_cursor\n        completions = self._completer.autocomplete(text_before_cursor)\n        prompt_completions = list(self._convert_to_prompt_completions(\n            completions, text_before_cursor))\n        if (not prompt_completions and self._completer.last_option and\n                len(self._completer.cmd_path) == 3):\n            # If we couldn't complete anything from the JSON model\n            # completer and we're on a cli option (e.g --foo), we\n            # can ask the server side completer if it knows anything\n            # about this resource.\n            LOG.debug(\"No local autocompletions found, trying \"\n                      \"server side completion.\")\n            command = self._completer.cmd_path\n            service = command[1]\n            if service == 's3api':\n                # TODO: we need a more generic way to capture renames\n                # of commands.  This currently lives in the CLI\n                # customization code.\n                service = 's3'\n            operation = command[2]\n            param = self._completer.arg_metadata.get(\n                self._completer.last_option, {}).get('api_name')\n            if param is not None:\n                LOG.debug(\"Trying to retrieve autcompletion for: \"\n                          \"%s, %s, %s\", service, operation, param)\n                results = self._server_side_completer\\\n                    .retrieve_candidate_values(service, operation, param)\n                LOG.debug(\"Results for %s, %s, %s: %s\",\n                          service, operation, param, results)\n                word_before_cursor = text_before_cursor.strip().split()[-1]\n                location = 0\n                if text_before_cursor[-1] != ' ' and \\\n                        word_before_cursor and results:\n                    # Filter the results down by fuzzy searching what\n                    # the user has provided.\n                    results = fuzzy.fuzzy_search(word_before_cursor, results)\n                    location = -len(word_before_cursor)\n                if results is not None:\n                    for result in results:\n                        # Insert at the end\n                        yield Completion(result, location,\n                                         display=result,\n                                         display_meta='')\n        else:\n            for c in prompt_completions:\n                yield c\n"
  },
  {
    "path": "awsshell/style.py",
    "content": "# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\"). You\n# may not use this file except in compliance with the License. A copy of\n# the License is located at\n#\n#     http://aws.amazon.com/apache2.0/\n#\n# or in the \"license\" file accompanying this file. This file is\n# distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF\n# ANY KIND, either express or implied. See the License for the specific\n# language governing permissions and limitations under the License.\nfrom pygments.token import Token\nfrom pygments.util import ClassNotFound\nfrom pygments.styles import get_style_by_name\nfrom prompt_toolkit.styles import default_style_extensions, style_from_dict\n\n\nclass StyleFactory(object):\n    \"\"\"Provide styles for the autocomplete menu and the toolbar.\n\n    :type style: :class:`pygments.style.StyleMeta`\n    :param style: Contains pygments style info.\n    \"\"\"\n\n    def __init__(self, style_name):\n        self.style = self.style_factory(style_name)\n\n    def style_factory(self, style_name):\n        \"\"\"Retrieve the specified pygments style.\n\n        If the specified style is not found, the vim style is returned.\n\n        :type style_name: str\n        :param style_name: The pygments style name.\n\n        :rtype: :class:`pygments.style.StyleMeta`\n        :return: Pygments style info.\n        \"\"\"\n        try:\n            style = get_style_by_name(style_name)\n        except ClassNotFound:\n            style = get_style_by_name('vim')\n\n        # Create a style dictionary.\n        styles = {}\n        styles.update(style.styles)\n        styles.update(default_style_extensions)\n        t = Token\n        styles.update({\n            t.Menu.Completions.Completion.Current: 'bg:#00aaaa #000000',\n            t.Menu.Completions.Completion: 'bg:#008888 #ffffff',\n            t.Menu.Completions.Meta.Current: 'bg:#00aaaa #000000',\n            t.Menu.Completions.Meta: 'bg:#00aaaa #ffffff',\n            t.Scrollbar.Button: 'bg:#003333',\n            t.Scrollbar: 'bg:#00aaaa',\n            t.Toolbar: 'bg:#222222 #cccccc',\n            t.Toolbar.Off: 'bg:#222222 #696969',\n            t.Toolbar.On: 'bg:#222222 #ffffff',\n            t.Toolbar.Search: 'noinherit bold',\n            t.Toolbar.Search.Text: 'nobold',\n            t.Toolbar.System: 'noinherit bold',\n            t.Toolbar.Arg: 'noinherit bold',\n            t.Toolbar.Arg.Text: 'nobold'\n        })\n\n        return style_from_dict(styles)\n"
  },
  {
    "path": "awsshell/substring.py",
    "content": "# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\"). You\n# may not use this file except in compliance with the License. A copy of\n# the License is located at\n#\n#     http://aws.amazon.com/apache2.0/\n#\n# or in the \"license\" file accompanying this file. This file is\n# distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF\n# ANY KIND, either express or implied. See the License for the specific\n# language governing permissions and limitations under the License.\n\n\ndef substring_search(word, collection):\n    \"\"\"Find all matches in the `collection` for the specified `word`.\n\n    If `word` is empty, returns all items in `collection`.\n\n    :type word: str\n    :param word: The substring to search for.\n\n    :type collection: collection, usually a list\n    :param collection: A collection of words to match.\n\n    :rtype: list of strings\n    :return: A sorted list of matching words from collection.\n    \"\"\"\n    return [item for item in sorted(collection) if item.startswith(word)]\n"
  },
  {
    "path": "awsshell/toolbar.py",
    "content": "# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\"). You\n# may not use this file except in compliance with the License. A copy of\n# the License is located at\n#\n#     http://aws.amazon.com/apache2.0/\n#\n# or in the \"license\" file accompanying this file. This file is\n# distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF\n# ANY KIND, either express or implied. See the License for the specific\n# language governing permissions and limitations under the License.\nfrom pygments.token import Token\n\n\nclass Toolbar(object):\n    \"\"\"Show information about the aws-shell in a tool bar.\n\n    :type handler: callable\n    :param handler: Wraps the callable `get_toolbar_items`.\n\n    \"\"\"\n\n    def __init__(self, get_match_fuzzy, get_enable_vi_bindings,\n                 get_show_completion_columns, get_show_help):\n        self.handler = self._create_toolbar_handler(\n            get_match_fuzzy, get_enable_vi_bindings,\n            get_show_completion_columns, get_show_help)\n\n    def _create_toolbar_handler(self, get_match_fuzzy, get_enable_vi_bindings,\n                                get_show_completion_columns, get_show_help):\n        \"\"\"Create the toolbar handler.\n\n        :type get_fuzzy_match: callable\n        :param fuzzy_match: Gets the fuzzy matching config.\n\n        :type get_enable_vi_bindings: callable\n        :param get_enable_vi_bindings: Gets the vi (or emacs) key bindings\n            config.\n\n        :type get_show_completion_columns: callable\n        :param get_show_completion_columns: Gets the show completions in\n            multiple or single columns config.\n\n        :type get_show_help: callable\n        :param get_show_help: Gets the show help pane config.\n\n        :rtype: callable\n        :returns: get_toolbar_items.\n\n        \"\"\"\n        assert callable(get_match_fuzzy)\n        assert callable(get_enable_vi_bindings)\n        assert callable(get_show_completion_columns)\n        assert callable(get_show_help)\n\n        def get_toolbar_items(cli):\n            \"\"\"Return the toolbar items.\n\n            :type cli: :class:`prompt_toolkit.Cli`\n            :param cli: The command line interface from prompt_toolkit\n\n            :rtype: list\n            :return: A list of (pygments.Token.Toolbar, str).\n            \"\"\"\n            if get_match_fuzzy():\n                match_fuzzy_token = Token.Toolbar.On\n                match_fuzzy_cfg = 'ON'\n            else:\n                match_fuzzy_token = Token.Toolbar.Off\n                match_fuzzy_cfg = 'OFF'\n            if get_enable_vi_bindings():\n                enable_vi_bindings_token = Token.Toolbar.On\n                enable_vi_bindings_cfg = 'Vi'\n            else:\n                enable_vi_bindings_token = Token.Toolbar.On\n                enable_vi_bindings_cfg = 'Emacs'\n            if get_show_completion_columns():\n                show_columns_token = Token.Toolbar.On\n                show_columns_cfg = 'Multi'\n            else:\n                show_columns_token = Token.Toolbar.On\n                show_columns_cfg = 'Single'\n            if get_show_help():\n                show_help_token = Token.Toolbar.On\n                show_help_cfg = 'ON'\n            else:\n                show_help_token = Token.Toolbar.Off\n                show_help_cfg = 'OFF'\n            if cli.current_buffer_name == 'DEFAULT_BUFFER':\n                show_buffer_name = 'cli'\n            else:\n                show_buffer_name = 'doc'\n            return [\n                (match_fuzzy_token,\n                 ' [F2] Fuzzy: {0} '.format(match_fuzzy_cfg)),\n                (enable_vi_bindings_token,\n                 ' [F3] Keys: {0} '.format(enable_vi_bindings_cfg)),\n                (show_columns_token,\n                 ' [F4] {0} Column '.format(show_columns_cfg)),\n                (show_help_token,\n                 ' [F5] Help: {0} '.format(show_help_cfg)),\n                (Token.Toolbar,\n                 ' [F9] Focus: {0} '.format(show_buffer_name)),\n                (Token.Toolbar,\n                 ' [F10] Exit ')\n            ]\n\n        return get_toolbar_items\n"
  },
  {
    "path": "awsshell/ui.py",
    "content": "from prompt_toolkit.enums import DEFAULT_BUFFER, SEARCH_BUFFER\nfrom prompt_toolkit.filters import IsDone, HasFocus, Always, \\\n    RendererHeightIsKnown, to_cli_filter, Filter\nfrom prompt_toolkit.layout import Window, HSplit, VSplit, FloatContainer, Float\nfrom prompt_toolkit.layout.containers import ConditionalContainer\nfrom prompt_toolkit.layout.controls import BufferControl, \\\n    TokenListControl, FillControl\nfrom prompt_toolkit.layout.dimension import LayoutDimension\nfrom prompt_toolkit.layout.menus import CompletionsMenu, \\\n    MultiColumnCompletionsMenu\nfrom prompt_toolkit.layout.processors import PasswordProcessor, \\\n    HighlightSearchProcessor, HighlightSelectionProcessor, \\\n    ConditionalProcessor, AppendAutoSuggestion\nfrom prompt_toolkit.layout.prompt import DefaultPrompt\nfrom prompt_toolkit.layout.screen import Char\nfrom prompt_toolkit.layout.toolbars import ValidationToolbar, \\\n    SystemToolbar, ArgToolbar, SearchToolbar\nfrom prompt_toolkit.layout.utils import explode_tokens\nfrom prompt_toolkit.layout.lexers import PygmentsLexer\nfrom pygments.token import Token\nfrom pygments.lexer import Lexer\n\nfrom awsshell.compat import text_type\n\n\n# This is borrowed from prompt_toolkit because we actually\n# need to mess with the layouts to get documentation pulled up.\ndef create_default_layout(app, message='',\n                          lexer=None, is_password=False,\n                          reserve_space_for_menu=False,\n                          get_prompt_tokens=None,\n                          get_bottom_toolbar_tokens=None,\n                          display_completions_in_columns=False,\n                          extra_input_processors=None, multiline=False):\n    \"\"\"\n    Generate default layout.\n\n    Returns a ``Layout`` instance.\n\n    :param message: Text to be used as prompt.\n    :param lexer: Lexer to be used for the highlighting.\n    :param is_password: `bool` or `CLIFilter`. When True, display input as '*'.\n    :param reserve_space_for_menu: When True, make sure that a minimal height\n        is allocated in the terminal, in order to display the completion menu.\n    :param get_prompt_tokens: An optional callable that returns the tokens to\n        be shown in the menu. (To be used instead of a `message`.)\n    :param get_bottom_toolbar_tokens: An optional callable that returns the\n        tokens for a toolbar at the bottom.\n    :param display_completions_in_columns: `bool` or `CLIFilter`. Display the\n        completions in multiple columns.\n    :param multiline: `bool` or `CLIFilter`. When True, prefer a layout that is\n        more adapted for multiline input. Text after newlines is automatically\n        indented, and search/arg input is shown below the input, instead of\n        replacing the prompt.\n    \"\"\"\n    assert isinstance(message, text_type)\n    assert (get_bottom_toolbar_tokens is None or\n            callable(get_bottom_toolbar_tokens))\n    assert get_prompt_tokens is None or callable(get_prompt_tokens)\n    assert not (message and get_prompt_tokens)\n\n    display_completions_in_columns = to_cli_filter(\n        display_completions_in_columns)\n    multiline = to_cli_filter(multiline)\n\n    if get_prompt_tokens is None:\n        get_prompt_tokens = lambda _: [(Token.Prompt, message)]\n\n    get_prompt_tokens_1, get_prompt_tokens_2 = _split_multiline_prompt(\n        get_prompt_tokens)\n\n    # `lexer` is supposed to be a `Lexer` instance. But if a Pygments lexer\n    # class is given, turn it into a PygmentsLexer. (Important for\n    # backwards-compatibility.)\n    try:\n        if issubclass(lexer, Lexer):\n            lexer = PygmentsLexer(lexer)\n    except TypeError:\n        # Happens when lexer is `None` or an instance of something else.\n        pass\n\n    # Create processors list.\n    # (DefaultPrompt should always be at the end.)\n    input_processors = [\n        ConditionalProcessor(\n            # By default, only highlight search when the search\n            # input has the focus. (Note that this doesn't mean\n            # there is no search: the Vi 'n' binding for instance\n            # still allows to jump to the next match in\n            # navigation mode.)\n            HighlightSearchProcessor(preview_search=Always()),\n            HasFocus(SEARCH_BUFFER)),\n        HighlightSelectionProcessor(),\n        ConditionalProcessor(\n            AppendAutoSuggestion(), HasFocus(DEFAULT_BUFFER) & ~IsDone()),\n        ConditionalProcessor(PasswordProcessor(), is_password)\n    ]\n\n    if extra_input_processors:\n        input_processors.extend(extra_input_processors)\n\n    # Show the prompt before the input (using the DefaultPrompt processor.\n    # This also replaces it with reverse-i-search and 'arg' when required.\n    # (Only for single line mode.)\n    input_processors.append(ConditionalProcessor(\n        DefaultPrompt(get_prompt_tokens), ~multiline))\n\n    # Create bottom toolbar.\n    if get_bottom_toolbar_tokens:\n        toolbars = [ConditionalContainer(\n            Window(TokenListControl(get_bottom_toolbar_tokens,\n                                    default_char=Char(' ', Token.Toolbar)),\n                   height=LayoutDimension.exact(1)),\n            filter=~IsDone() & RendererHeightIsKnown())]\n    else:\n        toolbars = []\n\n    def get_height(cli):\n        # If there is an autocompletion menu to be shown, make sure that our\n        # layout has at least a minimal height in order to display it.\n        if reserve_space_for_menu and not cli.is_done:\n            return LayoutDimension(min=8)\n        else:\n            return LayoutDimension()\n\n    def separator():\n        return ConditionalContainer(\n            content=Window(height=LayoutDimension.exact(1),\n                           content=FillControl(u'\\u2500',\n                                               token=Token.Separator)),\n            filter=HasDocumentation(app) & ~IsDone())\n\n    # Create and return Layout instance.\n    return HSplit([\n        ConditionalContainer(\n            Window(\n                TokenListControl(get_prompt_tokens_1),\n                dont_extend_height=True),\n            filter=multiline,\n        ),\n        VSplit([\n            # In multiline mode, the prompt is displayed in a left pane.\n            ConditionalContainer(\n                Window(\n                    TokenListControl(get_prompt_tokens_2),\n                    dont_extend_width=True,\n                ),\n                filter=multiline,\n            ),\n            # The main input, with completion menus floating on top of it.\n            FloatContainer(\n                Window(\n                    BufferControl(\n                        input_processors=input_processors,\n                        lexer=lexer,\n                        # Enable preview_search, we want to have immediate\n                        # feedback in reverse-i-search mode.\n                        preview_search=Always(),\n                        focus_on_click=True,\n                    ),\n                    get_height=get_height,\n                ),\n                [\n                    Float(xcursor=True,\n                          ycursor=True,\n                          content=CompletionsMenu(\n                              max_height=16,\n                              scroll_offset=1,\n                              extra_filter=(HasFocus(DEFAULT_BUFFER) &\n                                            ~display_completions_in_columns))),\n                    Float(xcursor=True,\n                          ycursor=True,\n                          content=MultiColumnCompletionsMenu(\n                              extra_filter=(HasFocus(DEFAULT_BUFFER) &\n                                            display_completions_in_columns),\n                              show_meta=Always()))\n                ]\n            ),\n        ]),\n        separator(),\n        ConditionalContainer(\n            content=Window(\n                BufferControl(\n                    focus_on_click=True,\n                    buffer_name=u'clidocs',\n                ),\n                height=LayoutDimension(max=15)),\n            filter=HasDocumentation(app) & ~IsDone(),\n        ),\n        separator(),\n        ValidationToolbar(),\n        SystemToolbar(),\n\n        # In multiline mode, we use two toolbars for 'arg' and 'search'.\n        ConditionalContainer(ArgToolbar(), multiline),\n        ConditionalContainer(SearchToolbar(), multiline),\n    ] + toolbars)\n\n\ndef _split_multiline_prompt(get_prompt_tokens):\n    \"\"\"Split prompt tokens into two multiline prompt token functions.\n\n    Take a `get_prompt_tokens` function. and return two new functions instead.\n    One that returns the tokens to be shown on the lines above the input, and\n    another one with the tokens to be shown at the first line of the input.\n\n    \"\"\"\n    def before(cli):\n        result = []\n        found_nl = False\n        for token, char in reversed(explode_tokens(get_prompt_tokens(cli))):\n            if char == '\\n':\n                found_nl = True\n            elif found_nl:\n                result.insert(0, (token, char))\n        return result\n\n    def first_input_line(cli):\n        result = []\n        for token, char in reversed(explode_tokens(get_prompt_tokens(cli))):\n            if char == '\\n':\n                break\n            else:\n                result.insert(0, (token, char))\n        return result\n\n    return before, first_input_line\n\n\nclass HasDocumentation(Filter):\n    def __init__(self, app):\n        self._app = app\n\n    def __call__(self, cli):\n        return bool(self._app.current_docs)\n"
  },
  {
    "path": "awsshell/utils.py",
    "content": "\"\"\"Utility module for misc aws shell functions.\"\"\"\nfrom __future__ import print_function\nimport os\nimport contextlib\nimport tempfile\nimport uuid\n\nimport awscli\n\nfrom awsshell.compat import HTMLParser\n\n\nAWSCLI_VERSION = awscli.__version__\n\n\nclass FileReadError(Exception):\n    pass\n\n\ndef remove_html(html):\n    s = DataOnly()\n    s.feed(html)\n    return s.get_data()\n\n\ndef build_config_file_path(file_name):\n    return os.path.join(os.path.expanduser('~'), '.aws', 'shell', file_name)\n\n\n@contextlib.contextmanager\ndef temporary_file(mode):\n    \"\"\"Cross platform temporary file creation.\n\n    This is an alternative to ``tempfile.NamedTemporaryFile`` that\n    also works on windows and avoids the \"file being used by\n    another process\" error.\n    \"\"\"\n    tempdir = tempfile.gettempdir()\n    basename = 'tmpfile-%s' % (uuid.uuid4())\n    full_filename = os.path.join(tempdir, basename)\n    if 'w' not in mode:\n        # We need to create the file before we can open\n        # it in 'r' mode.\n        open(full_filename, 'w').close()\n    try:\n        with open(full_filename, mode) as f:\n            yield f\n    finally:\n        os.remove(f.name)\n\n\nclass DataOnly(HTMLParser):\n    def __init__(self):\n        # HTMLParser is an old-style class, which can't be used with super()\n        HTMLParser.__init__(self)\n        self.reset()\n        self.lines = []\n\n    def handle_data(self, data):\n        self.lines.append(data)\n\n    def get_data(self):\n        return ''.join(self.lines)\n\n\nclass FSLayer(object):\n    \"\"\"Abstraction over common OS commands.\n\n    Provides a simpler interface given the operations needed\n    by the AWS Shell.\n\n    \"\"\"\n    def file_contents(self, filename, binary=False):\n        \"\"\"Return the file for a given filename.\n\n        If you want binary content use ``mode='rb'``.\n\n        \"\"\"\n        if binary:\n            mode = 'rb'\n        else:\n            mode = 'r'\n        try:\n            with open(filename, mode) as f:\n                return f.read()\n        except (OSError, IOError) as e:\n            raise FileReadError(str(e))\n\n    def file_exists(self, filename):\n        \"\"\"Check if a file exists.\n\n        This method returns true if:\n\n            * The file exists.\n            * The filename is a file (not a directory).\n\n        \"\"\"\n        return os.path.isfile(filename)\n\n\nclass InMemoryFSLayer(object):\n    \"\"\"Same interface as FSLayer with an in memory implementation.\"\"\"\n\n    def __init__(self, file_mapping):\n        # path -> file_contents\n        # file_contents are expected to be text, not\n        # binary.\n        self._file_mapping = file_mapping\n\n    def file_contents(self, filename, binary=False):\n        try:\n            contents = self._file_mapping[filename]\n        except KeyError:\n            raise FileReadError(filename)\n        if binary:\n            contents = contents.encode('utf-8')\n        return contents\n\n    def file_exists(self, filename):\n        return filename in self._file_mapping\n"
  },
  {
    "path": "requirements-dev.txt",
    "content": "doc8==0.6.0\nflake8==2.5.0\npep257==0.7.0\npylint==1.7.2\n-rrequirements-test.txt\n"
  },
  {
    "path": "requirements-test.txt",
    "content": "pytest==3.0.2\npytest-cov==2.3.1\nmock==1.3.0\ntox==2.2.1\nconfigobj==5.0.6\n# Note you need at least pip --version of 6.0 or\n# higher to be able to pick on these version specifiers.\nunittest2==1.1.0; python_version == '2.6'\n"
  },
  {
    "path": "scripts/ci/install",
    "content": "#!/usr/bin/env python\nimport os\nimport sys\nfrom subprocess import check_call\nimport shutil\n\n_dname = os.path.dirname\n\nREPO_ROOT = _dname(_dname(_dname(os.path.abspath(__file__))))\nos.chdir(REPO_ROOT)\n\n\ndef run(command):\n    return check_call(command, shell=True)\n\n\ntry:\n    # Has the form \"major.minor\"\n    python_version = os.environ['PYTHON_VERSION']\nexcept KeyError:\n    python_version = '.'.join([str(i) for i in sys.version_info[:2]])\n\nrun('pip install -r requirements-test.txt')\nif os.path.isdir('dist') and os.listdir('dist'):\n    shutil.rmtree('dist')\nrun('python setup.py bdist_wheel')\nwheel_dist = os.listdir('dist')[0]\nrun('pip install %s' % (os.path.join('dist', wheel_dist)))\n"
  },
  {
    "path": "scripts/ci/run-integ-tests",
    "content": "#!/usr/bin/env python\n# Don't run tests from the root repo dir.\n# We want to ensure we're importing from the installed\n# binary package not from the CWD.\n\nimport os\nfrom subprocess import check_call\n\n_dname = os.path.dirname\n\nREPO_ROOT = _dname(_dname(_dname(os.path.abspath(__file__))))\nos.chdir(os.path.join(REPO_ROOT, 'tests'))\n\n\ndef run(command):\n    return check_call(command, shell=True)\n\n\nrun('py.test --cov awsshell --junitxml=./pytests.xml --cov-report term-missing'\n    ' integration/')\n"
  },
  {
    "path": "scripts/ci/run-tests",
    "content": "#!/usr/bin/env python\n# Don't run tests from the root repo dir.\n# We want to ensure we're importing from the installed\n# binary package not from the CWD.\n\nimport os\nfrom subprocess import check_call\n\n_dname = os.path.dirname\n\nREPO_ROOT = _dname(_dname(_dname(os.path.abspath(__file__))))\nos.chdir(os.path.join(REPO_ROOT, 'tests'))\n\n\ndef run(command):\n    return check_call(command, shell=True)\n\n\nrun('py.test --cov awsshell --junitxml=./pytests.xml --cov-report term-missing'\n    ' unit/')\n"
  },
  {
    "path": "scripts/new-change",
    "content": "#!/usr/bin/env python\n\"\"\"Generate a new changelog entry.\n\nUsage\n=====\n\nTo generate a new changelog entry::\n\n    scripts/new-change\n\nThis will open up a file in your editor (via the ``EDITOR`` env  var).\nYou'll see this template::\n\n    # Type should be one of: feature, bugfix\n    type:\n\n    # Category is the high level feature area.\n    # This can be a service identifier (e.g ``s3``),\n    # or something like: Paginator.\n    category:\n\n    # A brief description of the change.  You can\n    # use github style references to issues such as\n    # \"fixes #489\", \"boto/boto3#100\", etc.  These\n    # will get automatically replaced with the correct\n    # link.\n    description:\n\nFill in the appropriate values, save and exit the editor.\nMake sure to commit these changes as part of your pull request.\n\nIf, when your editor is open, you decide don't don't want to add a changelog\nentry, save an empty file and no entry will be generated.\n\nYou can then use the ``scripts/render-change`` to generate the\nCHANGELOG.rst file.\n\n\"\"\"\nimport os\nimport re\nimport sys\nimport json\nimport string\nimport random\nimport tempfile\nimport subprocess\nimport argparse\n\n\nVALID_CHARS = set(string.ascii_letters + string.digits)\nCHANGES_DIR = os.path.join(\n    os.path.dirname(os.path.dirname(os.path.abspath(__file__))),\n    '.changes'\n)\nTEMPLATE = \"\"\"\\\n# Type should be one of: feature, bugfix, enhancement, api-change\n# feature: A larger feature or change in behavior, usually resulting in a\n#          minor version bump.\n# bugfix: Fixing a bug in an existing code path.\n# enhancment: Small change to an underlying implementation detail.\n# api-change: Changes to a modeled API.\ntype: {change_type}\n\n# Category is the high level feature area.\n# This can be a service identifier (e.g ``s3``),\n# or something like: Paginator.\ncategory: {category}\n\n# A brief description of the change.  You can\n# use github style references to issues such as\n# \"fixes #489\", \"boto/boto3#100\", etc.  These\n# will get automatically replaced with the correct\n# link.\ndescription: {description}\n\"\"\"\n\n\ndef new_changelog_entry(args):\n    # Changelog values come from one of two places.\n    # Either all values are provided on the command line,\n    # or we open a text editor and let the user provide\n    # enter their values.\n    if all_values_provided(args):\n        parsed_values = {\n            'type': args.change_type,\n            'category': args.category,\n            'description': args.description,\n        }\n    else:\n        parsed_values = get_values_from_editor(args)\n    if has_empty_values(parsed_values):\n        sys.stderr.write(\n            \"Empty changelog values received, skipping entry creation.\\n\")\n        return 1\n    replace_issue_references(parsed_values, args.repo)\n    write_new_change(parsed_values)\n    return 0\n\n\ndef has_empty_values(parsed_values):\n    return not (parsed_values.get('type') and\n                parsed_values.get('category') and\n                parsed_values.get('description'))\n\n\ndef all_values_provided(args):\n    return args.change_type and args.category and args.description\n\n\ndef get_values_from_editor(args):\n    with tempfile.NamedTemporaryFile('w') as f:\n        contents = TEMPLATE.format(\n            change_type=args.change_type,\n            category=args.category,\n            description=args.description,\n        )\n        f.write(contents)\n        f.flush()\n        env = os.environ\n        editor = env.get('VISUAL', env.get('EDITOR', 'vim'))\n        p = subprocess.Popen('%s %s' % (editor, f.name), shell=True)\n        p.communicate()\n        with open(f.name) as f:\n            filled_in_contents = f.read()\n            parsed_values = parse_filled_in_contents(filled_in_contents)\n            return parsed_values\n\n\ndef replace_issue_references(parsed, repo_name):\n    description = parsed['description']\n\n    def linkify(match):\n        number = match.group()[1:]\n        return (\n            '`%s <https://github.com/%s/issues/%s>`__' % (\n                match.group(), repo_name, number))\n\n    new_description = re.sub('#\\d+', linkify, description)\n    parsed['description'] = new_description\n\n\ndef write_new_change(parsed_values):\n    if not os.path.isdir(CHANGES_DIR):\n        os.makedirs(CHANGES_DIR)\n    # Assume that new changes go into the next release.\n    dirname = os.path.join(CHANGES_DIR, 'next-release')\n    if not os.path.isdir(dirname):\n        os.makedirs(dirname)\n    # Need to generate a unique filename for this change.\n    # We'll try a couple things until we get a unique match.\n    category = parsed_values['category']\n    short_summary = ''.join(filter(lambda x: x in VALID_CHARS, category))\n    filename = '{type_name}-{summary}'.format(\n        type_name=parsed_values['type'],\n        summary=short_summary)\n    possible_filename = os.path.join(\n        dirname, '%s-%s.json' % (filename, str(random.randint(1, 100000))))\n    while os.path.isfile(possible_filename):\n        possible_filename = os.path.join(\n            dirname, '%s-%s.json' % (filename, str(random.randint(1, 100000))))\n    with open(possible_filename, 'w') as f:\n        f.write(json.dumps(parsed_values, indent=2) + \"\\n\")\n\n\ndef parse_filled_in_contents(contents):\n    \"\"\"Parse filled in file contents and returns parsed dict.\n\n    Return value will be::\n        {\n          \"type\": \"bugfix\",\n          \"category\": \"category\",\n          \"description\": \"This is a description\"\n        }\n\n    \"\"\"\n    if not contents.strip():\n        return {}\n    parsed = {}\n    lines = iter(contents.splitlines())\n    for line in lines:\n        line = line.strip()\n        if line.startswith('#'):\n            continue\n        if 'type' not in parsed and line.startswith('type:'):\n            parsed['type'] = line.split(':')[1].strip()\n        elif 'category' not in parsed and line.startswith('category:'):\n            parsed['category'] = line.split(':')[1].strip()\n        elif 'description' not in parsed and line.startswith('description:'):\n            # Assume that everything until the end of the file is part\n            # of the description, so we can break once we pull in the\n            # remaining lines.\n            first_line = line.split(':')[1].strip()\n            full_description = '\\n'.join([first_line] + list(lines))\n            parsed['description'] = full_description.strip()\n            break\n    return parsed\n\n\ndef main():\n    parser = argparse.ArgumentParser()\n    parser.add_argument('-t', '--type', dest='change_type',\n                        default='', choices=('bugfix', 'feature',\n                                             'enhancement', 'api-change'))\n    parser.add_argument('-c', '--category', dest='category',\n                        default='')\n    parser.add_argument('-d', '--description', dest='description',\n                        default='')\n    parser.add_argument('-r', '--repo', default='awslabs/aws-shell',\n                        help='Optional repo name, e.g: awslabs/aws-shell')\n    args = parser.parse_args()\n    sys.exit(new_changelog_entry(args))\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "setup.cfg",
    "content": "[bdist_wheel]\nuniversal = 1\n"
  },
  {
    "path": "setup.py",
    "content": "#!/usr/bin/env python\nimport re\nimport ast\n\nfrom setuptools import setup, find_packages\n\n\nrequires = [\n    'awscli>=1.16.10,<2.0.0',\n    'prompt-toolkit>=1.0.0,<1.1.0',\n    'boto3>=1.9.0,<2.0.0',\n    'configobj>=5.0.6,<6.0.0',\n    'Pygments>=2.1.3,<3.0.0',\n]\n\n\nwith open('awsshell/__init__.py', 'r') as f:\n    version = str(\n        ast.literal_eval(\n            re.search(\n                r'__version__\\s+=\\s+(.*)',\n                f.read()).group(1)))\n\n\nsetup(\n    name='aws-shell',\n    version=version,\n    description='AWS Shell',\n    long_description=open('README.rst').read(),\n    author='James Saryerwinnie',\n    url='https://github.com/awslabs/aws-shell',\n    packages=find_packages(exclude=['tests*']),\n    include_package_data=True,\n    package_data={'awsshell': ['data/*/*.json',\n                               'awsshellrc']},\n    install_requires=requires,\n    entry_points={\n        'console_scripts': [\n            'aws-shell = awsshell:main',\n            'aws-shell-mkindex = awsshell.makeindex:main',\n        ]\n    },\n    license=\"Apache License 2.0\",\n    classifiers=(\n        'Development Status :: 3 - Alpha',\n        'Intended Audience :: Developers',\n        'Intended Audience :: System Administrators',\n        'Natural Language :: English',\n        'License :: OSI Approved :: Apache Software License',\n        'Programming Language :: Python',\n        'Programming Language :: Python :: 2.6',\n        'Programming Language :: Python :: 2.7',\n        'Programming Language :: Python :: 3',\n        'Programming Language :: Python :: 3.3',\n        'Programming Language :: Python :: 3.4',\n        'Programming Language :: Python :: 3.5',\n        'Programming Language :: Python :: 3.6',\n    ),\n)\n"
  },
  {
    "path": "tests/__init__.py",
    "content": "try:\n    import unittest2 as unittest\nexcept ImportError:\n    import unittest\n"
  },
  {
    "path": "tests/compat.py",
    "content": "# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\"). You\n# may not use this file except in compliance with the License. A copy of\n# the License is located at\n#\n#     http://aws.amazon.com/apache2.0/\n#\n# or in the \"license\" file accompanying this file. This file is\n# distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF\n# ANY KIND, either express or implied. See the License for the specific\n# language governing permissions and limitations under the License.\nimport sys\n\nif sys.version_info < (2, 7):\n    import unittest2 as unittest\nelse:\n    import unittest\n"
  },
  {
    "path": "tests/integration/__init__.py",
    "content": ""
  },
  {
    "path": "tests/integration/test_config.py",
    "content": "# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\"). You\n# may not use this file except in compliance with the License. A copy of\n# the License is located at\n#\n#     http://aws.amazon.com/apache2.0/\n#\n# or in the \"license\" file accompanying this file. This file is\n# distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF\n# ANY KIND, either express or implied. See the License for the specific\n# language governing permissions and limitations under the License.\nimport mock\nimport os\nimport unittest\n\nfrom awsshell.app import AWSShell\nfrom awsshell.config import Config\nfrom awsshell.utils import build_config_file_path\n\n\nclass ConfigTest(unittest.TestCase):\n\n    def test_config_off(self):\n        try:\n            os.remove(build_config_file_path('test-awsshellrc'))\n        except OSError:\n            pass\n        self.aws_shell = AWSShell(None, mock.Mock(), mock.Mock())\n        self.aws_shell.model_completer.match_fuzzy = False\n        self.aws_shell.enable_vi_bindings = False\n        self.aws_shell.show_completion_columns = False\n        self.aws_shell.show_help = False\n        self.aws_shell.theme = 'none'\n        self.aws_shell.save_config()\n        self.aws_shell.load_config()\n        assert self.aws_shell.model_completer.match_fuzzy == False\n        assert self.aws_shell.enable_vi_bindings == False\n        assert self.aws_shell.show_completion_columns == False\n        assert self.aws_shell.show_help == False\n        assert self.aws_shell.theme == 'none'\n\n    def test_config_on(self):\n        self.aws_shell = AWSShell(None, mock.Mock(), mock.Mock())\n        self.aws_shell.model_completer.match_fuzzy = True\n        self.aws_shell.enable_vi_bindings = True\n        self.aws_shell.show_completion_columns = True\n        self.aws_shell.show_help = True\n        self.aws_shell.theme = 'vim'\n        self.aws_shell.save_config()\n        self.aws_shell.load_config()\n        assert self.aws_shell.config_section.as_bool('match_fuzzy') == True\n        assert self.aws_shell.config_section.as_bool(\n            'enable_vi_bindings') == True\n        assert self.aws_shell.config_section.as_bool(\n            'show_completion_columns') == True\n        assert self.aws_shell.config_section.as_bool('show_help') == True\n        assert self.aws_shell.config_section['theme'] == 'vim'\n"
  },
  {
    "path": "tests/integration/test_db.py",
    "content": "from awsshell import db\nimport pytest\n\n\n@pytest.fixture\ndef shell_db(tmpdir):\n    filename = tmpdir.join('docs.db').strpath\n    d = db.ConcurrentDBM.create(filename)\n    return d\n\n\ndef test_can_get_and_set_value(shell_db):\n    shell_db['foo'] = 'bar'\n    assert shell_db['foo'] == 'bar'\n\n\ndef test_raise_key_error_when_no_key_exists(shell_db):\n    with pytest.raises(KeyError) as e:\n        shell_db['foo']\n    assert 'foo' in str(e.value)\n\n\ndef test_can_set_multiple_values(shell_db):\n    shell_db['foo'] = 'a'\n    shell_db['bar'] = 'b'\n    assert shell_db['foo'] == 'a'\n    assert shell_db['bar'] == 'b'\n\n\ndef test_can_change_existing_value(shell_db):\n    shell_db['foo'] = 'first'\n    shell_db['foo'] = 'second'\n    assert shell_db['foo'] == 'second'\n\n\ndef test_can_update_multiple_times(shell_db):\n    for i in range(100):\n        shell_db['foo'] = str(i)\n    assert shell_db['foo'] == '99'\n\n\ndef test_can_handle_unicode(shell_db):\n    shell_db['foo'] = u'\\u2713'\n    assert shell_db['foo'] == u'\\u2713'\n\n\ndef test_can_create_and_open_db(tmpdir):\n    filename = tmpdir.join('foo.db').strpath\n    d = db.ConcurrentDBM.create(filename)\n    d['foo'] = 'bar'\n    d.close()\n\n    # Should be able to reopen the database and look up 'foo'.\n    d = db.ConcurrentDBM.open(filename)\n    assert d['foo'] == 'bar'\n"
  },
  {
    "path": "tests/integration/test_keys.py",
    "content": "# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\"). You\n# may not use this file except in compliance with the License. A copy of\n# the License is located at\n#\n#     http://aws.amazon.com/apache2.0/\n#\n# or in the \"license\" file accompanying this file. This file is\n# distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF\n# ANY KIND, either express or implied. See the License for the specific\n# language governing permissions and limitations under the License.\nimport mock\n\nfrom prompt_toolkit.input import PipeInput\nfrom prompt_toolkit.output import DummyOutput\nfrom prompt_toolkit.key_binding.input_processor import KeyPress\nfrom prompt_toolkit.keys import Keys\n\nfrom tests.compat import unittest\nfrom awsshell.app import AWSShell, InputInterrupt\n\n\nclass KeysTest(unittest.TestCase):\n\n    def setUp(self):\n        self.input = PipeInput()\n        output = DummyOutput()\n        self.aws_shell = AWSShell(None, mock.Mock(), mock.Mock(),\n                                  input=self.input, output=output)\n        self.processor = self.aws_shell.cli.input_processor\n\n    def tearDown(self):\n        self.input.close()\n\n    def feed_key(self, key):\n        self.processor.feed(KeyPress(key, u''))\n        self.processor.process_keys()\n\n    def test_F2(self):\n        match_fuzzy = self.aws_shell.model_completer.match_fuzzy\n        self.feed_key(Keys.F2)\n        assert match_fuzzy != self.aws_shell.model_completer.match_fuzzy\n\n    def test_F3(self):\n        enable_vi_bindings = self.aws_shell.enable_vi_bindings\n        with self.assertRaises(InputInterrupt):\n            self.feed_key(Keys.F3)\n        assert enable_vi_bindings != self.aws_shell.enable_vi_bindings\n\n    def test_F4(self):\n        show_completion_columns = self.aws_shell.show_completion_columns\n        with self.assertRaises(InputInterrupt):\n            self.feed_key(Keys.F4)\n        assert show_completion_columns != \\\n            self.aws_shell.show_completion_columns\n\n    def test_F5(self):\n        show_help = self.aws_shell.show_help\n        with self.assertRaises(InputInterrupt):\n            self.feed_key(Keys.F5)\n        assert show_help != self.aws_shell.show_help\n\n    def test_F9(self):\n        assert self.aws_shell.cli.current_buffer_name == u'DEFAULT_BUFFER'\n        self.feed_key(Keys.F9)\n        assert self.aws_shell.cli.current_buffer_name == u'clidocs'\n\n    def test_F10(self):\n        self.feed_key(Keys.F10)\n        assert self.aws_shell.cli.is_exiting\n"
  },
  {
    "path": "tests/integration/test_makeindex.py",
    "content": "import awscli.clidriver\nfrom awsshell import makeindex\n\nimport pytest\n\n\n@pytest.fixture\ndef cloudformation_command():\n    driver = awscli.clidriver.create_clidriver()\n    cmd = driver.create_help_command()\n    cfn = cmd.command_table['cloudformation']\n    return cfn\n\n\ndef test_can_write_doc_index_for_single_operation(cloudformation_command):\n    # We don't want to try to generate the entire doc index\n    # for all commands.  We're just trying to ensure we're\n    # integrating with the AWS CLi's help commands properly\n    # so we're going to pick a single operation to document.\n    create_stack = cloudformation_command.create_help_command()\\\n            .command_table['create-stack']\n    help_command = create_stack.create_help_command()\n    rendered = makeindex.render_docs_for_cmd(help_command=help_command)\n    # We *really* don't want these to fail when the wording\n    # changes so I'm purposefully not picking long phrases.\n    assert 'Creates a stack' in rendered\n    # Should also see sections in the rendered content.\n    assert 'SYNOPSIS' in rendered\n    assert 'EXAMPLES' in rendered\n    assert 'OUTPUT' in rendered\n    # Should also see a parameter.\n    assert '--stack-name' in rendered\n\n\ndef test_can_document_all_service_commands(cloudformation_command):\n    db = {}\n    help_command = cloudformation_command.create_help_command()\n    makeindex.write_doc_index(db=db, help_command=help_command)\n    # Again, we don't want these to fail when cloudformation has\n    # API updates so I don't have very strict checking.\n    assert 'aws.cloudformation.create-stack' in db\n    assert 'aws.cloudformation.delete-stack' in db\n    assert 'SYNOPSIS' in db['aws.cloudformation.create-stack']\n\n\ndef test_can_index_a_command(cloudformation_command):\n    help_command = cloudformation_command.create_help_command()\n    index = makeindex.new_index()\n    makeindex.index_command(index, help_command)\n"
  },
  {
    "path": "tests/unit/__init__.py",
    "content": ""
  },
  {
    "path": "tests/unit/index/__init__.py",
    "content": ""
  },
  {
    "path": "tests/unit/index/test_completions.py",
    "content": "from tests import unittest\n\nfrom awsshell.index import completion\nfrom awsshell.utils import InMemoryFSLayer\n\n\nclass TestCompletionIndex(unittest.TestCase):\n    def setUp(self):\n        # filename -> file content\n        self.files = {}\n        self.fslayer = InMemoryFSLayer(self.files)\n\n    def test_can_load_index(self):\n        c = completion.CompletionIndex(cache_dir='/tmp/cache',\n                                       fslayer=self.fslayer)\n        self.files['/tmp/cache/completions-1.9.1.json'] = '{}'\n        try:\n            c.load_index('1.9.1')\n        except completion.IndexLoadError as e:\n            self.fail(\"Expected to load index for '1.9.1', \"\n                      \"but was unable.\")\n\n    def test_index_does_not_exist_raises_error(self):\n        c = completion.CompletionIndex(cache_dir='/tmp/cache',\n                                       fslayer=self.fslayer)\n        with self.assertRaises(completion.IndexLoadError):\n            c.load_index('1.9.1')\n"
  },
  {
    "path": "tests/unit/test_app.py",
    "content": "import pytest\nimport mock\n\n\nfrom awsshell import app\nfrom awsshell import shellcomplete\nfrom awsshell import compat\n\n\n@pytest.fixture\ndef errstream():\n    return compat.StringIO()\n\n\ndef test_can_dispatch_dot_commands():\n    call_args = []\n    class CustomHandler(object):\n        def run(self, command, context):\n            call_args.append((command, context))\n    handler = app.DotCommandHandler()\n    handler.HANDLER_CLASSES['foo'] = CustomHandler\n    context = object()\n\n    handler.handle_cmd('.foo a b c', context)\n\n    assert call_args == [(['.foo', 'a', 'b', 'c'], context)]\n\n\nclass PopenLogger(object):\n    def __call__(self, cmd):\n        self.cmd = cmd\n        filename = cmd[1]\n        with open(filename, 'r') as f:\n            self.contents = f.read()\n        return mock.Mock()\n\n\ndef test_edit_handler():\n    env = {'EDITOR': 'my-editor'}\n    popen_cls = mock.Mock()\n    application = mock.Mock()\n    application.history = [\n        '!ls',\n        '.edit',\n        'aws ec2 describe-instances',\n        'aws ec2 allocate-hosts',\n    ]\n    popen = PopenLogger()\n    handler = app.EditHandler(popen, env)\n    handler.run(['.edit'], application)\n    # Ensure our editor was called with some arbitrary temp filename.\n    command_run = popen.cmd\n    assert len(command_run) == 2\n    assert command_run[0] == 'my-editor'\n    # Ensure the contents of the temp file are correct\n    expected_contents = 'aws ec2 describe-instances\\naws ec2 allocate-hosts'\n    assert popen.contents == expected_contents\n\n\ndef test_error_msg_printed_on_error_handler(errstream):\n    env = {'EDITOR': 'my-editor'}\n    popen_cls = mock.Mock()\n    popen_cls.side_effect = OSError()\n    context = mock.Mock()\n    context.history = []\n    handler = app.EditHandler(popen_cls, env, errstream)\n    handler.run(['.edit'], context)\n\n    # Then we should not propagate an exception, and we\n    # should print a helpful error message.\n    assert 'Unable to launch editor: my-editor' in errstream.getvalue()\n\n\ndef test_profile_handler_prints_profile():\n    shell = mock.Mock(spec=app.AWSShell)\n    shell.profile = 'myprofile'\n    stdout = compat.StringIO()\n    handler = app.ProfileHandler(stdout)\n    handler.run(['.profile'], shell)\n    assert stdout.getvalue().strip() == 'Current shell profile: myprofile'\n\n\ndef test_profile_handler_when_no_profile_configured():\n    shell = mock.Mock(spec=app.AWSShell)\n    shell.profile = None\n    stdout = compat.StringIO()\n    handler = app.ProfileHandler(stdout)\n    handler.run(['.profile'], shell)\n    assert stdout.getvalue() == (\n        'Current shell profile: no profile configured\\n'\n        'You can change profiles using: .profile profile-name\\n'\n    )\n\n\ndef test_profile_command_changes_profile():\n    shell = mock.Mock(spec=app.AWSShell)\n    shell.profile = 'myprofile'\n    stdout = compat.StringIO()\n    handler = app.ProfileHandler(stdout)\n\n    handler.run(['.profile', 'newprofile'], shell)\n\n    assert shell.profile == 'newprofile'\n\n\ndef test_profile_prints_error_on_bad_syntax():\n    stderr = compat.StringIO()\n    handler = app.ProfileHandler(None, stderr)\n    handler.run(['.profile', 'a', 'b', 'c'], None)\n\n    # We don't really care about the exact usage message here,\n    # we just want to ensure usage was written to stderr.\n    assert 'Usage' in stderr.getvalue()\n\n\ndef test_prints_error_message_on_unknown_dot_command(errstream):\n    handler = app.DotCommandHandler(err=errstream)\n    handler.handle_cmd(\".unknown foo bar\", None)\n    assert errstream.getvalue() == \"Unknown dot command: .unknown\\n\"\n\n\ndef test_delegates_to_complete_changing_profile():\n    completer = mock.Mock(spec=shellcomplete.AWSShellCompleter)\n    shell = app.AWSShell(completer, mock.Mock(), mock.Mock())\n    shell.profile = 'mynewprofile'\n    assert completer.change_profile.call_args == mock.call('mynewprofile')\n    assert shell.profile == 'mynewprofile'\n\n\ndef test_cd_handler_can_chdir():\n    chdir = mock.Mock()\n    handler = app.ChangeDirHandler(chdir=chdir)\n    handler.run(['.cd', 'foo/bar'], None)\n    assert chdir.call_args == mock.call('foo/bar')\n\n\ndef test_chdir_syntax_error_prints_err_msg(errstream):\n    chdir = mock.Mock()\n    handler = app.ChangeDirHandler(err=errstream, chdir=chdir)\n    handler.run(['.cd'], None)\n    assert 'invalid syntax' in errstream.getvalue()\n    assert not chdir.called\n\n\ndef test_error_displayed_when_chdir_fails(errstream):\n    chdir = mock.Mock()\n    chdir.side_effect = OSError(\"FAILED\")\n    handler = app.ChangeDirHandler(err=errstream, chdir=chdir)\n    handler.run(['.cd', 'foo'], None)\n    assert 'FAILED' in errstream.getvalue()\n\n\ndef test_history_stored_correctly():\n    mock_prompter = mock.Mock()\n    mock_prompter.buffers = {'clidocs': mock.Mock()}\n    # Simulate the user entering various commands\n    quit_document = mock.Mock()\n    quit_document.text = '.quit'\n    command_document = mock.Mock()\n    command_document.text = 'ec2 describe-instances'\n    mock_prompter.run.side_effect = [command_document, quit_document]\n    shell = app.AWSShell(mock.Mock(), mock.Mock(), mock.Mock(),\n            popen_cls=mock.Mock())\n    shell.create_cli_interface = mock.Mock(return_value=mock_prompter)\n    shell.run()\n\n    # two calls should have been made, history should have added aws\n    assert mock_prompter.run.call_count == 2\n    assert list(shell.history) == ['aws ec2 describe-instances']\n\n\ndef test_exit_dot_command_exits_shell():\n    mock_prompter = mock.Mock()\n    # Simulate the user entering '.quit'\n    fake_document = mock.Mock()\n    fake_document.text = '.quit'\n    mock_prompter.run.return_value = fake_document\n    shell = app.AWSShell(mock.Mock(), mock.Mock(), mock.Mock())\n    shell.create_cli_interface = mock.Mock(return_value=mock_prompter)\n    shell.run()\n\n    # Should have only called run() once.  As soon as we\n    # see the .quit command, we immediately exit and stop prompting\n    # for more shell commands.\n    assert mock_prompter.run.call_count == 1\n"
  },
  {
    "path": "tests/unit/test_autocomplete.py",
    "content": "import pytest\nfrom awsshell.autocomplete import AWSCLIModelCompleter\n\n@pytest.fixture\ndef index_data():\n    return {\n        'aws': {\n            'argument_metadata': {},\n            'arguments': [],\n            'commands': [],\n            'children': {},\n        }\n    }\n\n\ndef test_completes_service_names(index_data):\n    index_data['aws']['commands'] = ['first', 'second']\n    completer = AWSCLIModelCompleter(index_data)\n    assert completer.autocomplete('fi') == ['first']\n\n\ndef test_completes_service_names_substring(index_data):\n    index_data['aws']['commands'] = ['foo', 'bar foo']\n    completer = AWSCLIModelCompleter(index_data)\n    completer.match_fuzzy = False\n    assert completer.autocomplete('fo') == ['foo']\n\n\ndef test_completes_multiple_service_names(index_data):\n    index_data['aws']['commands'] = ['abc', 'acd', 'b']\n    completer = AWSCLIModelCompleter(index_data)\n    assert completer.autocomplete('a') == ['abc', 'acd']\n\n\ndef test_no_completion(index_data):\n    index_data['aws']['commands'] = ['foo', 'bar']\n    completer = AWSCLIModelCompleter(index_data)\n    assert completer.autocomplete('baz') == []\n\n\ndef test_can_complete_subcommands(index_data):\n    index_data['aws']['commands'] = ['ec2']\n    index_data['aws']['children'] = {\n        'ec2': {\n            'arguments': [],\n            'commands': ['copy-image', 'copy-snapshot', 'other'],\n            'children': {},\n        }\n    }\n    completer = AWSCLIModelCompleter(index_data)\n    # The completer tracks state to optimize lookups,\n    # so we simulate exactly how it's called.\n    completer.autocomplete('e')\n    completer.autocomplete('ec')\n    completer.autocomplete('ec2')\n    completer.autocomplete('ec2 ')\n    completer.autocomplete('ec2 c')\n    completer.autocomplete('ec2 co')\n    assert completer.autocomplete('ec2 cop') == ['copy-image', 'copy-snapshot']\n\ndef test_everything_completed_on_space(index_data):\n    # Right after \"aws ec2<space>\" all the operations should be\n    # autocompleted.\n    index_data['aws']['commands'] = ['ec2']\n    index_data['aws']['children'] = {\n        'ec2': {\n            'arguments': [],\n            'commands': ['copy-image', 'copy-snapshot', 'other'],\n            'children': {},\n        }\n    }\n    completer = AWSCLIModelCompleter(index_data)\n    completer.autocomplete('e')\n    completer.autocomplete('ec')\n    completer.autocomplete('ec2')\n    assert completer.autocomplete('ec2 ') == ['copy-image', 'copy-snapshot',\n                                              'other']\n\n\ndef test_autocomplete_top_leve_services_on_space(index_data):\n    index_data['aws']['commands'] = ['first', 'second']\n    completer = AWSCLIModelCompleter(index_data)\n    assert completer.autocomplete(' ') == ['first', 'second']\n\n\ndef test_reset_auto_complete(index_data):\n    index_data['aws']['commands'] = ['first', 'second']\n    completer = AWSCLIModelCompleter(index_data)\n    completer.autocomplete('f')\n    completer.autocomplete('fi')\n    completer.autocomplete('fir')\n    # Then the user hits enter.\n    # Now they've moved on to the next command.\n    assert completer.autocomplete('d') == ['second']\n\n\ndef test_reset_after_subcommand_completion(index_data):\n    index_data['aws']['commands'] = ['ec2', 's3']\n    index_data['aws']['children'] = {\n        'ec2': {\n            'arguments': [],\n            'commands': ['copy-image', 'copy-snapshot', 'other'],\n            'children': {},\n        }\n    }\n    completer = AWSCLIModelCompleter(index_data)\n    # The completer tracks state to optimize lookups,\n    # so we simulate exactly how it's called.\n    completer.autocomplete('e')\n    completer.autocomplete('ec')\n    completer.autocomplete('ec2')\n    completer.autocomplete('ec2 ')\n    completer.autocomplete('ec2 c')\n    completer.autocomplete('ec2 co')\n    # The user hits enter and auto completes copy-snapshot.\n    # The next request should be to auto complete\n    # top level commands:\n    assert completer.autocomplete('s') == ['s3']\n\n\ndef test_backspace_should_complete_previous_command(index_data):\n    pass\n\n\ndef test_can_handle_entire_word_deleted(index_data):\n    pass\n\n\ndef test_can_handle_entire_line_deleted(index_data):\n    index_data['aws']['commands'] = ['ec2', 's3']\n    index_data['aws']['children'] = {\n        'ec2': {\n            'arguments': [],\n            'commands': ['copy-image', 'copy-snapshot', 'other'],\n            'children': {},\n        }\n    }\n    completer = AWSCLIModelCompleter(index_data)\n    c = completer.autocomplete\n    c('e')\n    c('ec')\n    c('ec2')\n    c('ec2 ')\n    c('ec2 c')\n    c('ec2 co')\n    # Use hits backspace a few times.\n    c('ec2 c')\n    c('ec2 ')\n    c('ec2')\n    # Now we should be auto completing 'ec2'\n    assert c('ec') == ['ec2']\n\n\ndef test_autocompletes_argument_names(index_data):\n    index_data['aws']['arguments'] = ['--query', '--debug']\n    completer = AWSCLIModelCompleter(index_data)\n    # These should only appear once in the output.  So we need\n    # to know if we're a top level argument or not.\n    assert completer.autocomplete('-') == ['--query', '--debug']\n    assert completer.autocomplete('--q') == ['--query']\n\n\ndef test_autocompletes_argument_names_substring(index_data):\n    index_data['aws']['arguments'] = ['--foo', '--bar foo']\n    completer = AWSCLIModelCompleter(index_data)\n    completer.match_fuzzy = False\n    # These should only appear once in the output.  So we need\n    # to know if we're a top level argument or not.\n    assert completer.autocomplete('--f') == ['--foo']\n\n\ndef test_autocompletes_global_and_service_args(index_data):\n    index_data['aws']['arguments'] = ['--query', '--debug']\n    index_data['aws']['commands'] = ['ec2']\n    index_data['aws']['children'] = {\n        'ec2': {\n            'arguments': ['--query-ec2', '--instance-id'],\n            'commands': [],\n            'children': {},\n        }\n    }\n    completer = AWSCLIModelCompleter(index_data)\n    c = completer.autocomplete\n    c('e')\n    c('ec')\n    c('ec2')\n    c('ec2 ')\n    c('ec2 -')\n    c('ec2 --')\n    assert c('ec2 --q') == ['--query', '--query-ec2']\n\n\ndef test_can_mix_options_and_commands(index_data):\n    index_data['aws']['arguments'] = ['--no-validate-ssl']\n    index_data['aws']['commands'] = ['ec2']\n    index_data['aws']['children'] = {\n        'ec2': {\n            'argument_metadata': {},\n            'arguments': ['--query-ec2', '--instance-id'],\n            'commands': ['create-tags', 'describe-instances'],\n            'children': {},\n        }\n    }\n    completer = AWSCLIModelCompleter(index_data)\n    c = completer.autocomplete\n    partial_cmd = 'ec2 --no-validate-ssl'\n    for i in range(1, len(partial_cmd)):\n        c(partial_cmd[:i])\n\n    assert c('ec2 --no-validate-ssl ') == ['create-tags', 'describe-instances']\n    c('ec2 --no-validate-ssl c')\n    c('ec2 --no-validate-ssl cr')\n    c('ec2 --no-validate-ssl cre')\n    c('ec2 --no-validate-ssl crea')\n    assert c('ec2 --no-validate-ssl creat') == ['create-tags']\n\n\ndef test_only_change_context_when_in_index(index_data):\n    index_data['aws']['arguments'] = ['--region']\n    index_data['aws']['commands'] = ['ec2']\n    index_data['aws']['children'] = {\n        'ec2': {\n            'commands': ['create-tags', 'describe-instances'],\n            'children': {},\n            'argument_metadata': {},\n            'arguments': [],\n        }\n    }\n    completer = AWSCLIModelCompleter(index_data)\n    c = completer.autocomplete\n    partial_cmd = 'ec2 --region us-west-2'\n    for i in range(1, len(partial_cmd)):\n        c(partial_cmd[:i])\n\n    # We should ignore \"us-west-2\" because it's not a child\n    # of ec2.\n    assert c('ec2 --region us-west-2 ') == ['create-tags', 'describe-instances']\n\n\ndef test_can_handle_skips_in_completion(index_data):\n    # Normally, completion is always requested char by char.\n    # Typing \"ec2 describe-inst\"\n    # will subsequent calls to the autocompleter:\n    # 'e', 'ec', 'ec2', 'ec2 ', 'ec2 d', 'ec2 de' ... all the way\n    # up to 'ec2 describe-inst'.\n    # However, the autocompleter should gracefully handle when there's\n    # skips, so two subsequent calls are 'ec' and then 'ec2 describe-ta',\n    # the autocompleter should still do the right thing.  The tradeoff\n    # will just be that this case will be slower than the common case\n    # of char by char additions.\n    index_data['aws']['commands'] = ['ec2']\n    index_data['aws']['children'] = {\n        'ec2': {\n            'commands': ['create-tags', 'describe-instances'],\n            'argument_metadata': {},\n            'arguments': [],\n            'children': {\n                'create-tags': {\n                    'argument_metadata': {\n                        '--resources': {'example': '', 'minidoc': 'foo'},\n                        '--tags': {'example': 'bar', 'minidoc': 'baz'},\n                    },\n                    'arguments': ['--resources', '--tags'],\n                }\n            },\n        }\n    }\n    completer = AWSCLIModelCompleter(index_data)\n    c = completer.autocomplete\n    result = c('ec2 create-ta')\n    assert result == ['create-tags']\n\n\ndef test_cmd_path_updated_on_completions(index_data):\n    index_data['aws']['commands'] = ['ec2']\n    index_data['aws']['children'] = {\n        'ec2': {\n            'commands': ['create-tags', 'describe-instances'],\n            'argument_metadata': {},\n            'arguments': [],\n            'children': {\n                'create-tags': {\n                    'commands': [],\n                    'argument_metadata': {\n                        '--resources': {'example': '', 'minidoc': 'foo'},\n                        '--tags': {'example': 'bar', 'minidoc': 'baz'},\n                    },\n                    'arguments': ['--resources', '--tags'],\n                }\n            }\n        }\n    }\n    completer = AWSCLIModelCompleter(index_data)\n    c = completer.autocomplete\n    result = c('ec2 create-tags ')\n    assert result == []\n    assert completer.cmd_path == ['aws', 'ec2', 'create-tags']\n    assert completer.arg_metadata == {\n        '--resources': {'example': '', 'minidoc': 'foo'},\n        '--tags': {'example': 'bar', 'minidoc': 'baz'},\n    }\n\n\ndef test_last_option_updated_up_releated_api_params(index_data):\n    index_data['aws']['commands'] = ['ec2']\n    index_data['aws']['children'] = {\n        'ec2': {\n            'commands': ['create-tags'],\n            'argument_metadata': {},\n            'arguments': [],\n            'children': {\n                'create-tags': {\n                    'commands': [],\n                    'argument_metadata': {\n                        '--resources': {'example': '', 'minidoc': 'foo'},\n                        '--tags': {'example': 'bar', 'minidoc': 'baz'},\n                    },\n                    'arguments': ['--resources', '--tags'],\n                    'children': {},\n                }\n            }\n        }\n    }\n    completer = AWSCLIModelCompleter(index_data)\n    completer.autocomplete('ec2 create-tags --resources ')\n    assert completer.last_option == '--resources'\n    completer.autocomplete('ec2 create-tags --resources f --tags ')\n    # last_option should be updated.\n    assert completer.last_option == '--tags'\n\n\ndef test_last_option_is_updated_on_global_options(index_data):\n    index_data['aws']['arguments'] = ['--no-sign-request']\n    index_data['aws']['commands'] = ['ec2']\n    index_data['aws']['children'] = {\n        'ec2': {\n            'commands': ['create-tags'],\n            'argument_metadata': {},\n            'arguments': [],\n            'children': {\n                'create-tags': {\n                    'commands': [],\n                    'argument_metadata': {\n                        '--resources': {'example': '', 'minidoc': 'foo'},\n                    },\n                    'arguments': ['--resources'],\n                    'children': {},\n                }\n            }\n        }\n    }\n    completer = AWSCLIModelCompleter(index_data)\n    completer.autocomplete('ec2 create-tags --resources ')\n    assert completer.last_option == '--resources'\n    completer.autocomplete('ec2 create-tags --resources f --no-sign-request ')\n    assert completer.last_option == '--no-sign-request'\n\n\ndef test_can_handle_autocompleting_same_string_twice(index_data):\n    index_data['aws']['commands'] = ['first', 'second']\n    completer = AWSCLIModelCompleter(index_data)\n    completer.autocomplete('f')\n    assert completer.autocomplete('f') == ['first']\n\n\ndef test_can_handle_autocomplete_empty_string_twice(index_data):\n    # Sometimes prompt_toolkit will try to autocomplete\n    # the empty string multiple times.  We need to handle this\n    # gracefully.\n    index_data['aws']['commands'] = ['first', 'second']\n    completer = AWSCLIModelCompleter(index_data)\n    assert completer.autocomplete('') == []\n    assert completer.autocomplete('') == []\n\n\ndef test_global_arg_metadata_property(index_data):\n    index_data['aws']['argument_metadata'] = {\n        '--global1': {},\n        '--global2': {},\n    }\n    completer = AWSCLIModelCompleter(index_data)\n    assert '--global1' in completer.global_arg_metadata\n"
  },
  {
    "path": "tests/unit/test_docs.py",
    "content": "from awsshell import docs\nfrom awsshell import db\n\n\ndef test_lazy_doc_factory(tmpdir):\n    filename = tmpdir.join('foo.db').strpath\n    doc_index = docs.load_lazy_doc_index(filename)\n    assert isinstance(doc_index, docs.DocRetriever)\n\n\ndef test_load_doc_db(tmpdir):\n    filename = tmpdir.join(\"foo.db\").strpath\n    d = docs.load_doc_db(filename)\n    assert isinstance(d, db.ConcurrentDBM)\n"
  },
  {
    "path": "tests/unit/test_fuzzy.py",
    "content": "import pytest\nfrom awsshell.fuzzy import fuzzy_search\n\n\n@pytest.mark.parametrize(\"search,corpus,expected\", [\n    ('foo', ['foobar', 'foobaz'], ['foobar', 'foobaz']),\n    ('f', ['foo', 'foobar', 'bar'], ['foo', 'foobar']),\n    ('fbb', ['foo-bar-baz', 'fo-ba-baz', 'bar'], ['foo-bar-baz', 'fo-ba-baz']),\n    ('fff', ['fi-fi-fi', 'fo'], ['fi-fi-fi']),\n    # The more chars it matches, the higher the score.\n    ('pre', ['prefix', 'pre', 'not'], ['pre', 'prefix']),\n    ('nomatch', ['noma', 'nomatccc'], []),\n])\ndef test_subsequences(search, corpus, expected):\n    actual = fuzzy_search(search, corpus)\n    assert actual == expected\n"
  },
  {
    "path": "tests/unit/test_load_completions.py",
    "content": "# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\"). You\n# may not use this file except in compliance with the License. A copy of\n# the License is located at\n#\n#     http://aws.amazon.com/apache2.0/\n#\n# or in the \"license\" file accompanying this file. This file is\n# distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF\n# ANY KIND, either express or implied. See the License for the specific\n# language governing permissions and limitations under the License.\nimport unittest\n\nfrom awsshell.index.completion import CompletionIndex\n\n\nclass LoadCompletionsTest(unittest.TestCase):\n\n    def setUp(self):\n        self.completion_index = CompletionIndex()\n        # This would probably be cleaner with a pytest.fixture like\n        # test_completions.index_data\n        DATA = (\n            '{\"aws\": '\n            '{\"commands\": [\"devicefarm\", \"foo\"], '\n            '\"arguments\": [\"--debug\", \"--endpoint-url\"], '\n            '\"children\": {\"devicefarm\": '\n            '{\"commands\": [\"create-device-pool\"], '\n            '\"children\": {\"create-device-pool\": '\n            '{\"commands\": [], '\n            '\"arguments\": [\"--project-arn\", \"--name\"]}}}, '\n            '\"foo\": '\n            '{\"commands\": [\"bar\"], '\n            '\"children\": {\"bar\": '\n            '{\"commands\": [], \"arguments\": [\"--baz\"]}}}}}}'\n        )\n        self.completion_index.load_index = lambda x: DATA\n        self.completion_index.load_completions()\n\n    def test_load_completions(self):\n        assert self.completion_index.commands == [\n            'devicefarm', 'foo']\n        assert self.completion_index.subcommands == [\n            'create-device-pool', 'bar']\n        assert self.completion_index.global_opts == [\n            '--debug', '--endpoint-url']\n        assert self.completion_index.args_opts == set([\n            '--project-arn', '--name', '--baz'])\n"
  },
  {
    "path": "tests/unit/test_makeindex.py",
    "content": "import textwrap\nfrom awsshell import makeindex\n\ndef test_can_convert_rst_text():\n    content = textwrap.dedent(\"\"\"\\\n        MySection\n        =========\n\n        This is some text.\n        Here's a list:\n\n        * foo\n        * bar\n\n        Literal text: ``--foo-bar``\n    \"\"\")\n    converted = makeindex.convert_rst_to_basic_text(content)\n    assert converted == textwrap.dedent(\"\"\"\\\n\n        MYSECTION\n\n        This is some text. Here's a list:\n\n        * foo\n\n        * bar\n\n        Literal text: --foo-bar\n    \"\"\")\n"
  },
  {
    "path": "tests/unit/test_resources.py",
    "content": "\"\"\"Index and retrive information from the resource JSON.\"\"\"\nimport pytest\nimport mock\n\nfrom botocore.exceptions import NoRegionError\n\nfrom awsshell.resource import index\n\n\n@pytest.fixture\ndef describer_creator():\n    class FakeDescriberCreator(object):\n        SERVICES = ['ec2']\n\n        def services_with_completions(self):\n            return self.SERVICES\n\n    return FakeDescriberCreator()\n\n\ndef test_build_from_has_many():\n    resource = {\n        'service': {\n            'hasMany': {\n                'Tables': {\n                    'request': {'operation': 'ListTables'},\n                    'resource': {\n                        'type': 'Table',\n                        'identifiers': [\n                            {'target': 'Name',\n                             'source': 'response',\n                             'path': 'TableNames[]',\n                            }\n                        ]\n                    }\n                }\n            }\n        },\n        'resources': {\n            'Table': {\n                'actions': {\n                    'Delete': {\n                        'request': {\n                            'operation': 'DeleteTable',\n                            'params': [\n                                {'target': 'TableName',\n                                 'source': 'identifier',\n                                 'name': 'Name'},\n                            ]\n                        }\n                    }\n                }\n            }\n        }\n    }\n    builder = index.ResourceIndexBuilder()\n    built_index = builder.build_index(resource)\n    assert built_index == {\n        'operations': {\n            'DeleteTable': {\n                'TableName': {\n                    'resourceName': 'Table',\n                    'resourceIdentifier': 'Name',\n                }\n            }\n        },\n        'resources': {\n            'Table': {\n                'operation': 'ListTables',\n                'resourceIdentifier': {\n                    'Name': 'TableNames[]',\n                }\n            }\n        }\n    }\n\n\ndef test_removes_jmespath_expressions_from_targets():\n    resource = {\n        'service': {\n            'hasMany': {\n                'Instances': {\n                    'request': {'operation': 'DescribeInstances'},\n                    'resource': {\n                        'type': 'Instance',\n                        'identifiers': [\n                            {'target': 'Id',\n                             'source': 'response',\n                             'path': 'Reservations[].Instances[].InstanceId',\n                            }\n                        ]\n                    }\n                }\n            }\n        },\n        'resources': {\n            'Instance': {\n                'actions': {\n                    'Terminate': {\n                        'request': {\n                            'operation': 'TerminateInstances',\n                            'params': [\n                                {'target': 'InstanceIds[0]',\n                                 'source': 'identifier',\n                                 'name': 'Id'},\n                            ]\n                        }\n                    }\n                }\n            }\n        }\n    }\n    builder = index.ResourceIndexBuilder()\n    built_index = builder.build_index(resource)\n    assert built_index == {\n        'operations': {\n            'TerminateInstances': {\n                'InstanceIds': {\n                    'resourceName': 'Instance',\n                    'resourceIdentifier': 'Id',\n                }\n            }\n        },\n        'resources': {\n            'Instance': {\n                'operation': 'DescribeInstances',\n                'resourceIdentifier': {\n                    'Id': 'Reservations[].Instances[].InstanceId',\n                }\n            }\n        }\n    }\n\n\ndef test_resource_not_included_if_no_has_many():\n    # This is something we can fix, but for now the resource\n    # must be in the hasMany.\n    resource = {\n        'service': {\n            'hasMany': {}\n        },\n        'resources': {\n            'Tag': {\n                'actions': {\n                    'Delete': {\n                        'request': {\n                            'operation': 'DeleteTags',\n                            'params': [\n                                {'target': 'Resources[0]',\n                                 'source': 'identifier',\n                                 'name': 'ResourceId'},\n                            ]\n                        }\n                    }\n                }\n            }\n        }\n    }\n    builder = index.ResourceIndexBuilder()\n    built_index = builder.build_index(resource)\n    # The index is empty because there was not matching\n    # hasMany resource.\n    assert built_index == {\n        'operations': {},\n        'resources': {},\n    }\n\n\ndef test_can_complete_query():\n    built_index = {\n        'dynamodb': {\n            'operations': {\n                'DeleteTable': {\n                    'TableName': {\n                        'resourceName': 'Table',\n                        'resourceIdentifier': 'Name',\n                    }\n                }\n            },\n            'resources': {\n                'Table': {\n                    'operation': 'ListTables',\n                    'resourceIdentifier': {\n                        'Name': 'TableNames[]',\n                    }\n                }\n            }\n        }\n    }\n    q = index.CompleterDescriber(built_index)\n    result = q.describe_autocomplete(\n        'dynamodb', 'DeleteTable', 'TableName')\n    assert result.service == 'dynamodb'\n    assert result.operation == 'ListTables'\n    assert result.params == {}\n    assert result.path == 'TableNames[]'\n\n\ndef test_cached_client_creator_returns_same_instance():\n    class FakeSession(object):\n        def create_client(self, service_name):\n            return object()\n\n    cached_creator = index.CachedClientCreator(FakeSession())\n    ec2 = cached_creator.create_client('ec2')\n    s3 = cached_creator.create_client('s3')\n    assert ec2 != s3\n    # However, asking for a client we've already created\n    # should return the exact same instance.\n    assert cached_creator.create_client('ec2') == ec2\n\n\ndef test_can_create_service_completers_from_cache():\n    class FakeDescriberCreator(object):\n        def load_service_model(self, service_name, type_name):\n            assert type_name == 'completions-1'\n            return \"fake_completions_for_%s\" % service_name\n\n        def services_with_completions(self):\n            return []\n\n    loader = FakeDescriberCreator()\n    factory = index.CompleterDescriberCreator(loader)\n    result = factory.create_completer_query('ec2')\n    assert isinstance(result, index.CompleterDescriber)\n    assert factory.create_completer_query('ec2') == result\n\n\ndef test_empty_results_returned_when_no_completion_data_exists(describer_creator):\n    describer_creator.SERVICES = []\n\n    completer = index.ServerSideCompleter(\n        client_creator=None,\n        describer_creator=describer_creator,\n    )\n    assert completer.retrieve_candidate_values(\n        'ec2', 'run-instances', 'ImageId') == []\n\n\ndef test_no_completions_when_cant_create_client(describer_creator):\n    client_creator = mock.Mock(spec=index.CachedClientCreator)\n    # This is raised when you don't have a region configured via config file\n    # env var or manually via a session.\n    client_creator.create_client.side_effect = NoRegionError()\n    completer = index.ServerSideCompleter(\n        client_creator=client_creator,\n        describer_creator=describer_creator)\n\n    assert completer.retrieve_candidate_values(\n        'ec2', 'foo', 'Bar') == []\n\n\ndef test_no_completions_returned_on_unknown_operation(describer_creator):\n    client = mock.Mock()\n    client_creator = mock.Mock(spec=index.CachedClientCreator)\n    client_creator.create_client.return_value = client\n\n    client.meta.method_to_api_mapping = {\n        'describe_foo': 'DescribeFoo'\n    }\n\n    completer = index.ServerSideCompleter(\n        client_creator=client_creator,\n        describer_creator=describer_creator)\n\n    assert completer.retrieve_candidate_values(\n        'ec2', 'not_describe_foo', 'Bar') == []\n"
  },
  {
    "path": "tests/unit/test_substring.py",
    "content": "# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\"). You\n# may not use this file except in compliance with the License. A copy of\n# the License is located at\n#\n#     http://aws.amazon.com/apache2.0/\n#\n# or in the \"license\" file accompanying this file. This file is\n# distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF\n# ANY KIND, either express or implied. See the License for the specific\n# language governing permissions and limitations under the License.\nimport pytest\n\nfrom awsshell.substring import substring_search\n\n\n@pytest.mark.parametrize(\"search,corpus,expected\", [\n    ('foo', ['foobar', 'foobaz'], ['foobar', 'foobaz']),\n    ('f', ['foo', 'foobar', 'bar'], ['foo', 'foobar']),\n    ('z', ['foo', 'foobar', 'bar'], []),\n])\ndef test_subsequences(search, corpus, expected):\n    actual = substring_search(search, corpus)\n    assert actual == expected\n"
  },
  {
    "path": "tests/unit/test_toolbar.py",
    "content": "# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\"). You\n# may not use this file except in compliance with the License. A copy of\n# the License is located at\n#\n#     http://aws.amazon.com/apache2.0/\n#\n# or in the \"license\" file accompanying this file. This file is\n# distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF\n# ANY KIND, either express or implied. See the License for the specific\n# language governing permissions and limitations under the License.\nimport mock\nimport unittest\nfrom pygments.token import Token\n\nfrom awsshell.app import AWSShell\nfrom awsshell.toolbar import Toolbar\n\n\nclass ToolbarTest(unittest.TestCase):\n\n    def setUp(self):\n        self.aws_shell = AWSShell(mock.Mock(), mock.Mock(), mock.Mock())\n        self.cli = mock.Mock()\n        self.toolbar = Toolbar(\n            lambda: self.aws_shell.model_completer.match_fuzzy,\n            lambda: self.aws_shell.enable_vi_bindings,\n            lambda: self.aws_shell.show_completion_columns,\n            lambda: self.aws_shell.show_help)\n\n    def test_toolbar_on(self):\n        self.aws_shell.model_completer.match_fuzzy = True\n        self.aws_shell.enable_vi_bindings = True\n        self.aws_shell.show_completion_columns = True\n        self.aws_shell.show_help = True\n        expected = [\n            (Token.Toolbar.On, ' [F2] Fuzzy: ON '),\n            (Token.Toolbar.On, ' [F3] Keys: Vi '),\n            (Token.Toolbar.On, ' [F4] Multi Column '),\n            (Token.Toolbar.On, ' [F5] Help: ON '),\n            (Token.Toolbar, ' [F9] Focus: doc '),\n            (Token.Toolbar, ' [F10] Exit ')]\n        assert expected == self.toolbar.handler(self.cli)\n\n    def test_toolbar_off(self):\n        self.aws_shell.model_completer.match_fuzzy = False\n        self.aws_shell.enable_vi_bindings = False\n        self.aws_shell.show_completion_columns = False\n        self.aws_shell.show_help = False\n        self.cli.current_buffer_name = 'DEFAULT_BUFFER'\n        expected = [\n            (Token.Toolbar.Off, ' [F2] Fuzzy: OFF '),\n            (Token.Toolbar.On, ' [F3] Keys: Emacs '),\n            (Token.Toolbar.On, ' [F4] Single Column '),\n            (Token.Toolbar.Off, ' [F5] Help: OFF '),\n            (Token.Toolbar, ' [F9] Focus: cli '),\n            (Token.Toolbar, ' [F10] Exit ')]\n        assert expected == self.toolbar.handler(self.cli)\n"
  },
  {
    "path": "tests/unit/test_utils.py",
    "content": "from tests import unittest\nimport os\nimport tempfile\nimport shutil\n\nfrom awsshell.utils import FSLayer\nfrom awsshell.utils import InMemoryFSLayer\nfrom awsshell.utils import FileReadError\nfrom awsshell.utils import temporary_file\n\n\nclass TestFSLayer(unittest.TestCase):\n    # TestFSLayer provides abstractions over the OS.\n    # It is one of the only exceptions in the AWS Shell\n    # code where it's ok to test by using actual files.\n    # All other test code should use FSLayer.\n    def setUp(self):\n        self.tempdir = tempfile.mkdtemp()\n        self.temporary_filename = os.path.join(\n            self.tempdir, 'tempfilefoo')\n        self.fslayer = FSLayer()\n\n    def tearDown(self):\n        shutil.rmtree(self.tempdir)\n\n    def test_can_read_file_contents(self):\n        with open(self.temporary_filename, 'w') as f:\n            f.write('helloworld')\n\n        self.assertEqual(\n            self.fslayer.file_contents(self.temporary_filename),\n            'helloworld')\n        self.assertEqual(\n            self.fslayer.file_contents(self.temporary_filename, binary=True),\n            b'helloworld')\n\n    def test_file_exists(self):\n        self.assertFalse(self.fslayer.file_exists(self.temporary_filename))\n\n        with open(self.temporary_filename, 'w') as f:\n            pass\n\n        self.assertTrue(self.fslayer.file_exists(self.temporary_filename))\n\n    def test_file_does_not_exist_error(self):\n        with self.assertRaises(FileReadError):\n            self.fslayer.file_contents('/tmp/thisdoesnot-exist.asdf')\n\n\nclass TestInMemoryFSLayer(unittest.TestCase):\n    def setUp(self):\n        self.file_mapping = {}\n        self.fslayer = InMemoryFSLayer(self.file_mapping)\n\n    def test_file_exists(self):\n        self.file_mapping['/my/fake/path'] = 'file contents'\n        self.assertTrue(self.fslayer.file_exists('/my/fake/path'))\n\n    def test_can_read_file_contents(self):\n        self.file_mapping['/myfile'] = 'helloworld'\n        self.assertEqual(self.fslayer.file_contents('/myfile'), 'helloworld')\n        self.assertEqual(self.fslayer.file_contents('/myfile', binary=True),\n                         b'helloworld')\n\n    def test_file_does_not_exist_error(self):\n        with self.assertRaises(FileReadError):\n            self.fslayer.file_contents('/tmp/thisdoesnot-exist.asdf')\n\n\nclass TestTemporaryFile(unittest.TestCase):\n    def test_can_use_as_context_manager(self):\n        with temporary_file('w') as f:\n            filename = f.name\n            f.write(\"foobar\")\n            f.flush()\n            self.assertEqual(open(filename).read(), \"foobar\")\n\n    def test_is_removed_after_exiting_context(self):\n        with temporary_file('w') as f:\n            filename = f.name\n            f.write(\"foobar\")\n            f.flush()\n        self.assertFalse(os.path.isfile(filename))\n\n    def test_can_open_in_read(self):\n        with temporary_file('r') as f:\n            filename = f.name\n            assert f.read() == ''\n            # Verify we can open the file again\n            # in another file descriptor.\n            with open(filename, 'w') as f2:\n                f2.write(\"foobar\")\n            f.seek(0)\n            assert f.read() == \"foobar\"\n        self.assertFalse(os.path.isfile(filename))\n"
  },
  {
    "path": "tox.ini",
    "content": "[tox]\nenvlist = py26,py27,py33,py34,py35,py36\n\n[testenv]\ncommands = py.test\ndeps = -rrequirements-test.txt\n"
  }
]