Inital Commit

This commit is contained in:
Brett Woodruff
2024-06-10 12:24:37 -04:00
commit 106024bcb4
2223 changed files with 241071 additions and 0 deletions

BIN
AWS-DDNS.zip Normal file

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,8 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import re
import sys
from charset_normalizer.cli import cli_detect
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
sys.exit(cli_detect())

View File

@@ -0,0 +1 @@
pip

View File

@@ -0,0 +1,177 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS

View File

@@ -0,0 +1,183 @@
Metadata-Version: 2.1
Name: boto3
Version: 1.34.39
Summary: The AWS SDK for Python
Home-page: https://github.com/boto/boto3
Author: Amazon Web Services
License: Apache License 2.0
Project-URL: Documentation, https://boto3.amazonaws.com/v1/documentation/api/latest/index.html
Project-URL: Source, https://github.com/boto/boto3
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Natural Language :: English
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Requires-Python: >= 3.8
License-File: LICENSE
License-File: NOTICE
Requires-Dist: botocore (<1.35.0,>=1.34.39)
Requires-Dist: jmespath (<2.0.0,>=0.7.1)
Requires-Dist: s3transfer (<0.11.0,>=0.10.0)
Provides-Extra: crt
Requires-Dist: botocore[crt] (<2.0a0,>=1.21.0) ; extra == 'crt'
===============================
Boto3 - The AWS SDK for Python
===============================
|Version| |Python| |License|
Boto3 is the Amazon Web Services (AWS) Software Development Kit (SDK) for
Python, which allows Python developers to write software that makes use
of services like Amazon S3 and Amazon EC2. You can find the latest, most
up to date, documentation at our `doc site`_, including a list of
services that are supported.
Boto3 is maintained and published by `Amazon Web Services`_.
Boto (pronounced boh-toh) was named after the fresh water dolphin native to the Amazon river. The name was chosen by the author of the original Boto library, Mitch Garnaat, as a reference to the company.
Notices
-------
On 2023-12-13, support for Python 3.7 ended for Boto3. This follows the
Python Software Foundation `end of support <https://peps.python.org/pep-0537/#lifespan>`__
for the runtime which occurred on 2023-06-27.
For more information, see this `blog post <https://aws.amazon.com/blogs/developer/python-support-policy-updates-for-aws-sdks-and-tools/>`__.
.. _boto: https://docs.pythonboto.org/
.. _`doc site`: https://boto3.amazonaws.com/v1/documentation/api/latest/index.html
.. _`Amazon Web Services`: https://aws.amazon.com/what-is-aws/
.. |Python| image:: https://img.shields.io/pypi/pyversions/boto3.svg?style=flat
:target: https://pypi.python.org/pypi/boto3/
:alt: Python Versions
.. |Version| image:: http://img.shields.io/pypi/v/boto3.svg?style=flat
:target: https://pypi.python.org/pypi/boto3/
:alt: Package Version
.. |License| image:: http://img.shields.io/pypi/l/boto3.svg?style=flat
:target: https://github.com/boto/boto3/blob/develop/LICENSE
:alt: License
Getting Started
---------------
Assuming that you have a supported version of Python installed, you can first
set up your environment with:
.. code-block:: sh
$ python -m venv .venv
...
$ . .venv/bin/activate
Then, you can install boto3 from PyPI with:
.. code-block:: sh
$ python -m pip install boto3
or install from source with:
.. code-block:: sh
$ git clone https://github.com/boto/boto3.git
$ cd boto3
$ python -m pip install -r requirements.txt
$ python -m pip install -e .
Using Boto3
~~~~~~~~~~~~~~
After installing boto3
Next, set up credentials (in e.g. ``~/.aws/credentials``):
.. code-block:: ini
[default]
aws_access_key_id = YOUR_KEY
aws_secret_access_key = YOUR_SECRET
Then, set up a default region (in e.g. ``~/.aws/config``):
.. code-block:: ini
[default]
region=us-east-1
Other credential configuration methods can be found `here <https://boto3.amazonaws.com/v1/documentation/api/latest/guide/credentials.html>`__
Then, from a Python interpreter:
.. code-block:: python
>>> import boto3
>>> s3 = boto3.resource('s3')
>>> for bucket in s3.buckets.all():
print(bucket.name)
Running Tests
~~~~~~~~~~~~~
You can run tests in all supported Python versions using ``tox``. By default,
it will run all of the unit and functional tests, but you can also specify your own
``pytest`` options. Note that this requires that you have all supported
versions of Python installed, otherwise you must pass ``-e`` or run the
``pytest`` command directly:
.. code-block:: sh
$ tox
$ tox -- unit/test_session.py
$ tox -e py26,py33 -- integration/
You can also run individual tests with your default Python version:
.. code-block:: sh
$ pytest tests/unit
Getting Help
------------
We use GitHub issues for tracking bugs and feature requests and have limited
bandwidth to address them. Please use these community resources for getting
help:
* Ask a question on `Stack Overflow <https://stackoverflow.com/>`__ and tag it with `boto3 <https://stackoverflow.com/questions/tagged/boto3>`__
* Open a support ticket with `AWS Support <https://console.aws.amazon.com/support/home#/>`__
* If it turns out that you may have found a bug, please `open an issue <https://github.com/boto/boto3/issues/new>`__
Contributing
------------
We value feedback and contributions from our community. Whether it's a bug report, new feature, correction, or additional documentation, we welcome your issues and pull requests. Please read through this `CONTRIBUTING <https://github.com/boto/boto3/blob/develop/CONTRIBUTING.rst>`__ document before submitting any issues or pull requests to ensure we have all the necessary information to effectively respond to your contribution.
Maintenance and Support for SDK Major Versions
----------------------------------------------
Boto3 was made generally available on 06/22/2015 and is currently in the full support phase of the availability life cycle.
For information about maintenance and support for SDK major versions and their underlying dependencies, see the following in the AWS SDKs and Tools Shared Configuration and Credentials Reference Guide:
* `AWS SDKs and Tools Maintenance Policy <https://docs.aws.amazon.com/sdkref/latest/guide/maint-policy.html>`__
* `AWS SDKs and Tools Version Support Matrix <https://docs.aws.amazon.com/sdkref/latest/guide/version-support-matrix.html>`__
More Resources
--------------
* `NOTICE <https://github.com/boto/boto3/blob/develop/NOTICE>`__
* `Changelog <https://github.com/boto/boto3/blob/develop/CHANGELOG.rst>`__
* `License <https://github.com/boto/boto3/blob/develop/LICENSE>`__

View File

@@ -0,0 +1,2 @@
boto3
Copyright 2013-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.

View File

@@ -0,0 +1,104 @@
boto3-1.34.39.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
boto3-1.34.39.dist-info/LICENSE,sha256=DVQuDIgE45qn836wDaWnYhSdxoLXgpRRKH4RuTjpRZQ,10174
boto3-1.34.39.dist-info/METADATA,sha256=ITvHDoNEs6a6UxC-4W-s8MDpPjPX7LEG1FCChPnVO3E,6620
boto3-1.34.39.dist-info/NOTICE,sha256=BPseYUhKeBDxugm7QrwByljJrzOSfXxaIVVuTE0cf6Q,83
boto3-1.34.39.dist-info/RECORD,,
boto3-1.34.39.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
boto3-1.34.39.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92
boto3-1.34.39.dist-info/top_level.txt,sha256=MP6_SI1GcPseXodd3Ykt5F_mCBsrUksiziLxjEZKGUU,6
boto3/__init__.py,sha256=RxCtP9O6cQq_SQeJ9D1fT0-RIb-UZTebzhvJHWudeFo,3420
boto3/__pycache__/__init__.cpython-310.pyc,,
boto3/__pycache__/compat.cpython-310.pyc,,
boto3/__pycache__/crt.cpython-310.pyc,,
boto3/__pycache__/exceptions.cpython-310.pyc,,
boto3/__pycache__/session.cpython-310.pyc,,
boto3/__pycache__/utils.cpython-310.pyc,,
boto3/compat.py,sha256=1T-LvBYqd0z-L9hI-soU7O7v-678tyWWR2pztF055u0,2887
boto3/crt.py,sha256=VFstUtHMZrZ6eHJJ-YdXb4vqfIOcHbv1l51fdeY5cS0,5407
boto3/data/cloudformation/2010-05-15/resources-1.json,sha256=5mFVKJVtbVoHyPdHSyNfZ5mpkgCAws5PhnveSu4qzdI,5110
boto3/data/cloudwatch/2010-08-01/resources-1.json,sha256=q4AgE8F4pbscd-2U3NYSGAzK55zpMyOQGr83JUxbZXI,11690
boto3/data/dynamodb/2012-08-10/resources-1.json,sha256=hBLa1Jt7bdT557U9A7UcSi8SCpONKzdbtDRTzjM1-Y0,3849
boto3/data/ec2/2014-10-01/resources-1.json,sha256=tMG1AMYP2ksnPWY6-3l8DB-EhKsSNtAO9YHhvHqBKu0,68469
boto3/data/ec2/2015-03-01/resources-1.json,sha256=tMG1AMYP2ksnPWY6-3l8DB-EhKsSNtAO9YHhvHqBKu0,68469
boto3/data/ec2/2015-04-15/resources-1.json,sha256=tMG1AMYP2ksnPWY6-3l8DB-EhKsSNtAO9YHhvHqBKu0,68469
boto3/data/ec2/2015-10-01/resources-1.json,sha256=SOfYX2c1KgvnxMO2FCdJpV42rJWNMwVhlFAXhvUPTzA,76564
boto3/data/ec2/2016-04-01/resources-1.json,sha256=SOfYX2c1KgvnxMO2FCdJpV42rJWNMwVhlFAXhvUPTzA,76564
boto3/data/ec2/2016-09-15/resources-1.json,sha256=SOfYX2c1KgvnxMO2FCdJpV42rJWNMwVhlFAXhvUPTzA,76564
boto3/data/ec2/2016-11-15/resources-1.json,sha256=vx7YiL-sUvBFeo4SZ81G7Qa2Hy-y6xY4z2YlSx7_wEw,76922
boto3/data/glacier/2012-06-01/resources-1.json,sha256=GT5qWQLGeXtrHgTDNG23Mrpyweg6O0Udgd139BuNTVs,19940
boto3/data/iam/2010-05-08/resources-1.json,sha256=PsOT9yBqSJtluBFHCVRsg6k6Ly2VkSYODnYxSl0DVOc,50357
boto3/data/opsworks/2013-02-18/resources-1.json,sha256=Y6ygEyegsbYA1gGZn-Ad2yuDd3jUCOt2UKrW_b2YBeM,4136
boto3/data/s3/2006-03-01/resources-1.json,sha256=VeKALhMRqv7fyDHMLOM5_RzXUEuDdg_n6OIRi3sdB-o,37204
boto3/data/sns/2010-03-31/resources-1.json,sha256=7zmKQhafgsRDu4U1yiw3NXHz-zJhHKrOmtuoYlxQP-s,9091
boto3/data/sqs/2012-11-05/resources-1.json,sha256=LRIIr5BId3UDeuBfLn-vRiWsSZCM9_ynqdxF8uzHgy8,6545
boto3/docs/__init__.py,sha256=ncXQfWgitU2kFSghqy2lezeeW1RneKZ-3wcsvEddsr0,1845
boto3/docs/__pycache__/__init__.cpython-310.pyc,,
boto3/docs/__pycache__/action.cpython-310.pyc,,
boto3/docs/__pycache__/attr.cpython-310.pyc,,
boto3/docs/__pycache__/base.cpython-310.pyc,,
boto3/docs/__pycache__/client.cpython-310.pyc,,
boto3/docs/__pycache__/collection.cpython-310.pyc,,
boto3/docs/__pycache__/docstring.cpython-310.pyc,,
boto3/docs/__pycache__/method.cpython-310.pyc,,
boto3/docs/__pycache__/resource.cpython-310.pyc,,
boto3/docs/__pycache__/service.cpython-310.pyc,,
boto3/docs/__pycache__/subresource.cpython-310.pyc,,
boto3/docs/__pycache__/utils.cpython-310.pyc,,
boto3/docs/__pycache__/waiter.cpython-310.pyc,,
boto3/docs/action.py,sha256=5ZQ2C9vIZdk8grFlnej-cwpVoNz0drcMiirKzqHczck,8178
boto3/docs/attr.py,sha256=BnG3tR1KKQvvY58aeJiWQ5W5DiMnJ_9jUjmG6tDbFiU,2500
boto3/docs/base.py,sha256=nOrQSCeUSIZPkn-I59o7CfjEthgdkpCt_rXtE9zQnXc,2103
boto3/docs/client.py,sha256=RpCngTolE4OtGIPvvJvkw8FJCqh5-7b-Q0QN5mVE7to,1078
boto3/docs/collection.py,sha256=pWO9I9LTMyhyYyCT_alrO4hZpqNI1228IwABotYTqBU,11679
boto3/docs/docstring.py,sha256=oPugaubdAXY6aNa-kXGI51lP1xE2s4AnfTsLhibf7-E,2511
boto3/docs/method.py,sha256=kJ3UJS2JBSt6SB_3TsEf3lxcjda5TAAfmocrLmxtSLc,2733
boto3/docs/resource.py,sha256=HBFq7c-tio19Da2Wb60j99EcW-I9M5-_C-9IjTuWrok,15376
boto3/docs/service.py,sha256=bCd2LPfZOeTkDOKggTyXJYXXPkuYUy91x5KYyqPPQnE,8544
boto3/docs/subresource.py,sha256=W19brjJjeW55ssyYhCnFaZICfp2LjOoC4BP_jW2ViC8,5864
boto3/docs/utils.py,sha256=H0UeVvmVbYBZ6F-CVEUxVggLMBOIoA5q8y8hxBFnRKE,5436
boto3/docs/waiter.py,sha256=xfnXtbMTOCyNG9vTNZW7Alsy77ZXuJCFcQcq0sNtg8Q,5175
boto3/dynamodb/__init__.py,sha256=GkSq-WxXWfVHu1SEcMrlJbzkfw9ACgF3UdCL6fPpTmY,562
boto3/dynamodb/__pycache__/__init__.cpython-310.pyc,,
boto3/dynamodb/__pycache__/conditions.cpython-310.pyc,,
boto3/dynamodb/__pycache__/table.cpython-310.pyc,,
boto3/dynamodb/__pycache__/transform.cpython-310.pyc,,
boto3/dynamodb/__pycache__/types.cpython-310.pyc,,
boto3/dynamodb/conditions.py,sha256=sjkd0kIqFP_h8aUvysZQel0zts5HF22ogqKiv0t0KRw,15045
boto3/dynamodb/table.py,sha256=us79dxZSQSno8gsUoAdQyzc2oBJL2riUpN6RGc8vQk8,6343
boto3/dynamodb/transform.py,sha256=JnW5ZzPIfxEcDszSvXKUZmp_1rw445tsddS3FG--JwA,12909
boto3/dynamodb/types.py,sha256=ch0vIKaAYexjL42S_OJWyvjWMcb0UbNrmkKGcz76O3c,9541
boto3/ec2/__init__.py,sha256=GkSq-WxXWfVHu1SEcMrlJbzkfw9ACgF3UdCL6fPpTmY,562
boto3/ec2/__pycache__/__init__.cpython-310.pyc,,
boto3/ec2/__pycache__/createtags.cpython-310.pyc,,
boto3/ec2/__pycache__/deletetags.cpython-310.pyc,,
boto3/ec2/createtags.py,sha256=pUPJOYn7m0Jcch9UL-DEVGgbQHoyAemECPBhzyBx28c,1577
boto3/ec2/deletetags.py,sha256=KaYcqSt8FFM_TW0g0pZ14qDjVnmRCPV0sMe6DprEtvo,1217
boto3/examples/cloudfront.rst,sha256=K-sBWZxoLjABCZHrqAZs57cYefwPmDir03pm6PE_mh4,1390
boto3/examples/s3.rst,sha256=jCfgEDfpw08nFtCizCN2OGg15zQRkx3DiJXZUfqhE2s,5486
boto3/exceptions.py,sha256=i13QpGxoFizxAGCzA2qmF9ldbI5IfBpn37DH75ddRF8,4127
boto3/resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
boto3/resources/__pycache__/__init__.cpython-310.pyc,,
boto3/resources/__pycache__/action.cpython-310.pyc,,
boto3/resources/__pycache__/base.cpython-310.pyc,,
boto3/resources/__pycache__/collection.cpython-310.pyc,,
boto3/resources/__pycache__/factory.cpython-310.pyc,,
boto3/resources/__pycache__/model.cpython-310.pyc,,
boto3/resources/__pycache__/params.cpython-310.pyc,,
boto3/resources/__pycache__/response.cpython-310.pyc,,
boto3/resources/action.py,sha256=vPfVHVgXiGqhwpgRSCC7lSsY3vGjgsSiYhXa14CMAqw,9600
boto3/resources/base.py,sha256=Nf5Anssquo3urPDyWLAN8di379z5oafjwzl3gD9WbsI,5044
boto3/resources/collection.py,sha256=bSV0353zcTRLEPws2qqMFd2Xure8I8LgU-IDR-TM3sI,19242
boto3/resources/factory.py,sha256=iXV5l7UZePNIfkkUMgUNC0tIdJhxr_65m9KYdwIOfKA,22708
boto3/resources/model.py,sha256=3mCNSvnmCKPzjK-hW4yEv0PjKYb0hxBsAE9nopY-3bU,20394
boto3/resources/params.py,sha256=i6KAjOzjzou7ouViYbRZCz0CwqB6fA_6gOJFDIruTV8,6112
boto3/resources/response.py,sha256=aC1AZuO08qtb1psJtbrc5Na32AQ9WI-Il4DpVxsUtXs,11694
boto3/s3/__init__.py,sha256=GkSq-WxXWfVHu1SEcMrlJbzkfw9ACgF3UdCL6fPpTmY,562
boto3/s3/__pycache__/__init__.cpython-310.pyc,,
boto3/s3/__pycache__/constants.cpython-310.pyc,,
boto3/s3/__pycache__/inject.cpython-310.pyc,,
boto3/s3/__pycache__/transfer.cpython-310.pyc,,
boto3/s3/constants.py,sha256=ZaYknNwqGwsJEGkL92GXaBs9kjfRbyCDFt89wei8t7E,690
boto3/s3/inject.py,sha256=t1XiGqUIB_BNtJSiKgqu1hfzhfCntWzmUSxWgCPv4bU,28205
boto3/s3/transfer.py,sha256=FPQQ-ov8foEmA4DY6CYjitY7BchyGSzCpfBRPPhNj-0,15928
boto3/session.py,sha256=ITqrFauYJ74IfZrpPS411rLaKNNVQKgCe388fpnUV-0,20758
boto3/utils.py,sha256=dBw0Eu23TOhDsP1Lkrp4uOVMn5DS8s0kRGwVRiCD_KM,3141

View File

@@ -0,0 +1,5 @@
Wheel-Version: 1.0
Generator: bdist_wheel (0.37.0)
Root-Is-Purelib: true
Tag: py3-none-any

View File

@@ -0,0 +1 @@
boto3

View File

@@ -0,0 +1,111 @@
# Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
# https://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.
import logging
from boto3.compat import _warn_deprecated_python
from boto3.session import Session
__author__ = 'Amazon Web Services'
__version__ = '1.34.39'
# The default Boto3 session; autoloaded when needed.
DEFAULT_SESSION = None
def setup_default_session(**kwargs):
"""
Set up a default session, passing through any parameters to the session
constructor. There is no need to call this unless you wish to pass custom
parameters, because a default session will be created for you.
"""
global DEFAULT_SESSION
DEFAULT_SESSION = Session(**kwargs)
def set_stream_logger(name='boto3', level=logging.DEBUG, format_string=None):
"""
Add a stream handler for the given name and level to the logging module.
By default, this logs all boto3 messages to ``stdout``.
>>> import boto3
>>> boto3.set_stream_logger('boto3.resources', logging.INFO)
For debugging purposes a good choice is to set the stream logger to ``''``
which is equivalent to saying "log everything".
.. WARNING::
Be aware that when logging anything from ``'botocore'`` the full wire
trace will appear in your logs. If your payloads contain sensitive data
this should not be used in production.
:type name: string
:param name: Log name
:type level: int
:param level: Logging level, e.g. ``logging.INFO``
:type format_string: str
:param format_string: Log message format
"""
if format_string is None:
format_string = "%(asctime)s %(name)s [%(levelname)s] %(message)s"
logger = logging.getLogger(name)
logger.setLevel(level)
handler = logging.StreamHandler()
handler.setLevel(level)
formatter = logging.Formatter(format_string)
handler.setFormatter(formatter)
logger.addHandler(handler)
def _get_default_session():
"""
Get the default session, creating one if needed.
:rtype: :py:class:`~boto3.session.Session`
:return: The default session
"""
if DEFAULT_SESSION is None:
setup_default_session()
_warn_deprecated_python()
return DEFAULT_SESSION
def client(*args, **kwargs):
"""
Create a low-level service client by name using the default session.
See :py:meth:`boto3.session.Session.client`.
"""
return _get_default_session().client(*args, **kwargs)
def resource(*args, **kwargs):
"""
Create a resource service client by name using the default session.
See :py:meth:`boto3.session.Session.resource`.
"""
return _get_default_session().resource(*args, **kwargs)
# Set up logging to ``/dev/null`` like a library is supposed to.
# https://docs.python.org/3.3/howto/logging.html#configuring-logging-for-a-library
class NullHandler(logging.Handler):
def emit(self, record):
pass
logging.getLogger('boto3').addHandler(NullHandler())

View File

@@ -0,0 +1,82 @@
# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
# https://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.
import sys
import os
import errno
import socket
import warnings
from boto3.exceptions import PythonDeprecationWarning
# In python3, socket.error is OSError, which is too general
# for what we want (i.e FileNotFoundError is a subclass of OSError).
# In py3 all the socket related errors are in a newly created
# ConnectionError
SOCKET_ERROR = ConnectionError
import collections.abc as collections_abc
if sys.platform.startswith('win'):
def rename_file(current_filename, new_filename):
try:
os.remove(new_filename)
except OSError as e:
if not e.errno == errno.ENOENT:
# We only want to a ignore trying to remove
# a file that does not exist. If it fails
# for any other reason we should be propagating
# that exception.
raise
os.rename(current_filename, new_filename)
else:
rename_file = os.rename
def filter_python_deprecation_warnings():
"""
Invoking this filter acknowledges your runtime will soon be deprecated
at which time you will stop receiving all updates to your client.
"""
warnings.filterwarnings(
'ignore',
message=".*Boto3 will no longer support Python.*",
category=PythonDeprecationWarning,
module=r".*boto3\.compat"
)
def _warn_deprecated_python():
"""Use this template for future deprecation campaigns as needed."""
py_37_params = {
'date': 'December 13, 2023',
'blog_link': (
'https://aws.amazon.com/blogs/developer/'
'python-support-policy-updates-for-aws-sdks-and-tools/'
)
}
deprecated_versions = {
# Example template for future deprecations
(3, 7): py_37_params,
}
py_version = sys.version_info[:2]
if py_version in deprecated_versions:
params = deprecated_versions[py_version]
warning = (
"Boto3 will no longer support Python {}.{} "
"starting {}. To continue receiving service updates, "
"bug fixes, and security updates please upgrade to Python 3.8 or "
"later. More information can be found here: {}"
).format(py_version[0], py_version[1], params['date'], params['blog_link'])
warnings.warn(warning, PythonDeprecationWarning)

View File

@@ -0,0 +1,167 @@
# Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
# https://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.
"""
This file contains private functionality for interacting with the AWS
Common Runtime library (awscrt) in boto3.
All code contained within this file is for internal usage within this
project and is not intended for external consumption. All interfaces
contained within are subject to abrupt breaking changes.
"""
import threading
import botocore.exceptions
from botocore.session import Session
from s3transfer.crt import (
BotocoreCRTCredentialsWrapper,
BotocoreCRTRequestSerializer,
CRTTransferManager,
acquire_crt_s3_process_lock,
create_s3_crt_client,
)
# Singletons for CRT-backed transfers
CRT_S3_CLIENT = None
BOTOCORE_CRT_SERIALIZER = None
CLIENT_CREATION_LOCK = threading.Lock()
PROCESS_LOCK_NAME = 'boto3'
def _create_crt_client(session, config, region_name, cred_provider):
"""Create a CRT S3 Client for file transfer.
Instantiating many of these may lead to degraded performance or
system resource exhaustion.
"""
create_crt_client_kwargs = {
'region': region_name,
'use_ssl': True,
'crt_credentials_provider': cred_provider,
}
return create_s3_crt_client(**create_crt_client_kwargs)
def _create_crt_request_serializer(session, region_name):
return BotocoreCRTRequestSerializer(
session, {'region_name': region_name, 'endpoint_url': None}
)
def _create_crt_s3_client(
session, config, region_name, credentials, lock, **kwargs
):
"""Create boto3 wrapper class to manage crt lock reference and S3 client."""
cred_wrapper = BotocoreCRTCredentialsWrapper(credentials)
cred_provider = cred_wrapper.to_crt_credentials_provider()
return CRTS3Client(
_create_crt_client(session, config, region_name, cred_provider),
lock,
region_name,
cred_wrapper,
)
def _initialize_crt_transfer_primatives(client, config):
lock = acquire_crt_s3_process_lock(PROCESS_LOCK_NAME)
if lock is None:
# If we're unable to acquire the lock, we cannot
# use the CRT in this process and should default to
# the classic s3transfer manager.
return None, None
session = Session()
region_name = client.meta.region_name
credentials = client._get_credentials()
serializer = _create_crt_request_serializer(session, region_name)
s3_client = _create_crt_s3_client(
session, config, region_name, credentials, lock
)
return serializer, s3_client
def get_crt_s3_client(client, config):
global CRT_S3_CLIENT
global BOTOCORE_CRT_SERIALIZER
with CLIENT_CREATION_LOCK:
if CRT_S3_CLIENT is None:
serializer, s3_client = _initialize_crt_transfer_primatives(
client, config
)
BOTOCORE_CRT_SERIALIZER = serializer
CRT_S3_CLIENT = s3_client
return CRT_S3_CLIENT
class CRTS3Client:
"""
This wrapper keeps track of our underlying CRT client, the lock used to
acquire it and the region we've used to instantiate the client.
Due to limitations in the existing CRT interfaces, we can only make calls
in a single region and does not support redirects. We track the region to
ensure we don't use the CRT client when a successful request cannot be made.
"""
def __init__(self, crt_client, process_lock, region, cred_provider):
self.crt_client = crt_client
self.process_lock = process_lock
self.region = region
self.cred_provider = cred_provider
def is_crt_compatible_request(client, crt_s3_client):
"""
Boto3 client must use same signing region and credentials
as the CRT_S3_CLIENT singleton. Otherwise fallback to classic.
"""
if crt_s3_client is None:
return False
boto3_creds = client._get_credentials()
if boto3_creds is None:
return False
is_same_identity = compare_identity(
boto3_creds.get_frozen_credentials(), crt_s3_client.cred_provider
)
is_same_region = client.meta.region_name == crt_s3_client.region
return is_same_region and is_same_identity
def compare_identity(boto3_creds, crt_s3_creds):
try:
crt_creds = crt_s3_creds()
except botocore.exceptions.NoCredentialsError:
return False
is_matching_identity = (
boto3_creds.access_key == crt_creds.access_key_id
and boto3_creds.secret_key == crt_creds.secret_access_key
and boto3_creds.token == crt_creds.session_token
)
return is_matching_identity
def create_crt_transfer_manager(client, config):
"""Create a CRTTransferManager for optimized data transfer."""
crt_s3_client = get_crt_s3_client(client, config)
if is_crt_compatible_request(client, crt_s3_client):
return CRTTransferManager(
crt_s3_client.crt_client, BOTOCORE_CRT_SERIALIZER
)
return None

View File

@@ -0,0 +1,195 @@
{
"service": {
"actions": {
"CreateStack": {
"request": { "operation": "CreateStack" },
"resource": {
"type": "Stack",
"identifiers": [
{ "target": "Name", "source": "requestParameter", "path": "StackName" }
]
}
}
},
"has": {
"Event": {
"resource": {
"type": "Event",
"identifiers": [
{ "target": "Id", "source": "input" }
]
}
},
"Stack": {
"resource": {
"type": "Stack",
"identifiers": [
{ "target": "Name", "source": "input" }
]
}
}
},
"hasMany": {
"Stacks": {
"request": { "operation": "DescribeStacks" },
"resource": {
"type": "Stack",
"identifiers": [
{ "target": "Name", "source": "response", "path": "Stacks[].StackName" }
],
"path": "Stacks[]"
}
}
}
},
"resources": {
"Event": {
"identifiers": [
{
"name": "Id",
"memberName": "EventId"
}
],
"shape": "StackEvent"
},
"Stack": {
"identifiers": [
{
"name": "Name",
"memberName": "StackName"
}
],
"shape": "Stack",
"load": {
"request": {
"operation": "DescribeStacks",
"params": [
{ "target": "StackName", "source": "identifier", "name": "Name" }
]
},
"path": "Stacks[0]"
},
"actions": {
"CancelUpdate": {
"request": {
"operation": "CancelUpdateStack",
"params": [
{ "target": "StackName", "source": "identifier", "name": "Name" }
]
}
},
"Delete": {
"request": {
"operation": "DeleteStack",
"params": [
{ "target": "StackName", "source": "identifier", "name": "Name" }
]
}
},
"Update": {
"request": {
"operation": "UpdateStack",
"params": [
{ "target": "StackName", "source": "identifier", "name": "Name" }
]
}
}
},
"has": {
"Resource": {
"resource": {
"type": "StackResource",
"identifiers": [
{ "target": "StackName", "source": "identifier", "name": "Name" },
{ "target": "LogicalId", "source": "input" }
]
}
}
},
"hasMany": {
"Events": {
"request": {
"operation": "DescribeStackEvents",
"params": [
{ "target": "StackName", "source": "identifier", "name": "Name" }
]
},
"resource": {
"type": "Event",
"identifiers": [
{ "target": "Id", "source": "response", "path": "StackEvents[].EventId" }
],
"path": "StackEvents[]"
}
},
"ResourceSummaries": {
"request": {
"operation": "ListStackResources",
"params": [
{ "target": "StackName", "source": "identifier", "name": "Name" }
]
},
"resource": {
"type": "StackResourceSummary",
"identifiers": [
{ "target": "LogicalId", "source": "response", "path": "StackResourceSummaries[].LogicalResourceId" },
{ "target": "StackName", "source": "requestParameter", "path": "StackName" }
],
"path": "StackResourceSummaries[]"
}
}
}
},
"StackResource": {
"identifiers": [
{ "name": "StackName" },
{
"name": "LogicalId",
"memberName": "LogicalResourceId"
}
],
"shape": "StackResourceDetail",
"load": {
"request": {
"operation": "DescribeStackResource",
"params": [
{ "target": "LogicalResourceId", "source": "identifier", "name": "LogicalId" },
{ "target": "StackName", "source": "identifier", "name": "StackName" }
]
},
"path": "StackResourceDetail"
},
"has": {
"Stack": {
"resource": {
"type": "Stack",
"identifiers": [
{ "target": "Name", "source": "identifier", "name": "StackName" }
]
}
}
}
},
"StackResourceSummary": {
"identifiers": [
{ "name": "StackName" },
{
"name": "LogicalId",
"memberName": "LogicalResourceId"
}
],
"shape": "StackResourceSummary",
"has": {
"Resource": {
"resource": {
"type": "StackResource",
"identifiers": [
{ "target": "LogicalId", "source": "identifier", "name": "LogicalId" },
{ "target": "StackName", "source": "identifier", "name": "StackName" }
]
}
}
}
}
}
}

View File

@@ -0,0 +1,334 @@
{
"service": {
"has": {
"Alarm": {
"resource": {
"type": "Alarm",
"identifiers": [
{
"target": "Name",
"source": "input"
}
]
}
},
"Metric": {
"resource": {
"type": "Metric",
"identifiers": [
{
"target": "Namespace",
"source": "input"
},
{
"target": "Name",
"source": "input"
}
]
}
}
},
"hasMany": {
"Alarms": {
"request": { "operation": "DescribeAlarms" },
"resource": {
"type": "Alarm",
"identifiers": [
{
"target": "Name",
"source": "response",
"path": "MetricAlarms[].AlarmName"
}
],
"path": "MetricAlarms[]"
}
},
"Metrics": {
"request": { "operation": "ListMetrics" },
"resource": {
"type": "Metric",
"identifiers": [
{
"target": "Namespace",
"source": "response",
"path": "Metrics[].Namespace"
},
{
"target": "Name",
"source": "response",
"path": "Metrics[].MetricName"
}
],
"path": "Metrics[]"
}
}
}
},
"resources": {
"Alarm": {
"identifiers": [
{
"name": "Name",
"memberName": "AlarmName"
}
],
"shape": "MetricAlarm",
"load": {
"request": {
"operation": "DescribeAlarms",
"params": [
{
"target": "AlarmNames[0]",
"source": "identifier",
"name": "Name"
}
]
},
"path": "MetricAlarms[0]"
},
"actions": {
"Delete": {
"request": {
"operation": "DeleteAlarms",
"params": [
{
"target": "AlarmNames[0]",
"source": "identifier",
"name": "Name"
}
]
}
},
"DescribeHistory": {
"request": {
"operation": "DescribeAlarmHistory",
"params": [
{
"target": "AlarmName",
"source": "identifier",
"name": "Name"
}
]
}
},
"DisableActions": {
"request": {
"operation": "DisableAlarmActions",
"params": [
{
"target": "AlarmNames[0]",
"source": "identifier",
"name": "Name"
}
]
}
},
"EnableActions": {
"request": {
"operation": "EnableAlarmActions",
"params": [
{
"target": "AlarmNames[0]",
"source": "identifier",
"name": "Name"
}
]
}
},
"SetState": {
"request": {
"operation": "SetAlarmState",
"params": [
{
"target": "AlarmName",
"source": "identifier",
"name": "Name"
}
]
}
}
},
"batchActions": {
"Delete": {
"request": {
"operation": "DeleteAlarms",
"params": [
{
"target": "AlarmNames[]",
"source": "identifier",
"name": "Name"
}
]
}
},
"DisableActions": {
"request": {
"operation": "DisableAlarmActions",
"params": [
{
"target": "AlarmNames[]",
"source": "identifier",
"name": "Name"
}
]
}
},
"EnableActions": {
"request": {
"operation": "EnableAlarmActions",
"params": [
{
"target": "AlarmNames[]",
"source": "identifier",
"name": "Name"
}
]
}
}
},
"has": {
"Metric": {
"resource": {
"type": "Metric",
"identifiers": [
{
"target": "Namespace",
"source": "data",
"path": "Namespace"
},
{
"target": "Name",
"source": "data",
"path": "MetricName"
}
]
}
}
}
},
"Metric": {
"identifiers": [
{
"name": "Namespace",
"memberName": "Namespace"
},
{
"name": "Name",
"memberName": "MetricName"
}
],
"shape": "Metric",
"load": {
"request": {
"operation": "ListMetrics",
"params": [
{
"target": "MetricName",
"source": "identifier",
"name": "Name"
},
{
"target": "Namespace",
"source": "identifier",
"name": "Namespace"
}
]
},
"path": "Metrics[0]"
},
"actions": {
"GetStatistics": {
"request": {
"operation": "GetMetricStatistics",
"params": [
{
"target": "Namespace",
"source": "identifier",
"name": "Namespace"
},
{
"target": "MetricName",
"source": "identifier",
"name": "Name"
}
]
}
},
"PutAlarm": {
"request": {
"operation": "PutMetricAlarm",
"params": [
{
"target": "Namespace",
"source": "identifier",
"name": "Namespace"
},
{
"target": "MetricName",
"source": "identifier",
"name": "Name"
}
]
},
"resource": {
"type": "Alarm",
"identifiers": [
{
"target": "Name",
"source": "requestParameter",
"path": "AlarmName"
}
]
}
},
"PutData": {
"request": {
"operation": "PutMetricData",
"params": [
{
"target": "Namespace",
"source": "identifier",
"name": "Namespace"
},
{
"target": "MetricData[].MetricName",
"source": "identifier",
"name": "Name"
}
]
}
}
},
"hasMany": {
"Alarms": {
"request": {
"operation": "DescribeAlarmsForMetric",
"params": [
{
"target": "Namespace",
"source": "identifier",
"name": "Namespace"
},
{
"target": "MetricName",
"source": "identifier",
"name": "Name"
}
]
},
"resource": {
"type": "Alarm",
"identifiers": [
{
"target": "Name",
"source": "response",
"path": "MetricAlarms[].AlarmName"
}
],
"path": "MetricAlarms[]"
}
}
}
}
}
}

View File

@@ -0,0 +1,150 @@
{
"service": {
"actions": {
"BatchGetItem": {
"request": { "operation": "BatchGetItem" }
},
"BatchWriteItem": {
"request": { "operation": "BatchWriteItem" }
},
"CreateTable": {
"request": { "operation": "CreateTable" },
"resource": {
"type": "Table",
"identifiers": [
{ "target": "Name", "source": "response", "path": "TableDescription.TableName" }
],
"path": "TableDescription"
}
}
},
"has": {
"Table": {
"resource": {
"type": "Table",
"identifiers": [
{ "target": "Name", "source": "input" }
]
}
}
},
"hasMany": {
"Tables": {
"request": { "operation": "ListTables" },
"resource": {
"type": "Table",
"identifiers": [
{ "target": "Name", "source": "response", "path": "TableNames[]" }
]
}
}
}
},
"resources": {
"Table": {
"identifiers": [
{
"name": "Name",
"memberName": "TableName"
}
],
"shape": "TableDescription",
"load": {
"request": {
"operation": "DescribeTable",
"params": [
{ "target": "TableName", "source": "identifier", "name": "Name" }
]
},
"path": "Table"
},
"actions": {
"Delete": {
"request": {
"operation": "DeleteTable",
"params": [
{ "target": "TableName", "source": "identifier", "name": "Name" }
]
}
},
"DeleteItem": {
"request": {
"operation": "DeleteItem",
"params": [
{ "target": "TableName", "source": "identifier", "name": "Name" }
]
}
},
"GetItem": {
"request": {
"operation": "GetItem",
"params": [
{ "target": "TableName", "source": "identifier", "name": "Name" }
]
}
},
"PutItem": {
"request": {
"operation": "PutItem",
"params": [
{ "target": "TableName", "source": "identifier", "name": "Name" }
]
}
},
"Query": {
"request": {
"operation": "Query",
"params": [
{ "target": "TableName", "source": "identifier", "name": "Name" }
]
}
},
"Scan": {
"request": {
"operation": "Scan",
"params": [
{ "target": "TableName", "source": "identifier", "name": "Name" }
]
}
},
"Update": {
"request": {
"operation": "UpdateTable",
"params": [
{ "target": "TableName", "source": "identifier", "name": "Name" }
]
},
"resource": {
"type": "Table",
"identifiers": [
{ "target": "Name", "source": "identifier", "name": "Name" }
],
"path": "TableDescription"
}
},
"UpdateItem": {
"request": {
"operation": "UpdateItem",
"params": [
{ "target": "TableName", "source": "identifier", "name": "Name" }
]
}
}
},
"waiters":{
"Exists": {
"waiterName": "TableExists",
"params": [
{ "target": "TableName", "source": "identifier", "name": "Name" }
]
},
"NotExists": {
"waiterName": "TableNotExists",
"params": [
{ "target": "TableName", "source": "identifier", "name": "Name" }
]
}
}
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,581 @@
{
"service": {
"actions": {
"CreateVault": {
"request": {
"operation": "CreateVault",
"params": [
{ "target": "accountId", "source": "string", "value": "-" }
]
},
"resource": {
"type": "Vault",
"identifiers": [
{ "target": "AccountId", "source": "requestParameter", "path": "accountId" },
{ "target": "Name", "source": "requestParameter", "path": "vaultName" }
]
}
}
},
"has": {
"Account": {
"resource": {
"type": "Account",
"identifiers": [
{ "target": "Id", "source": "input" }
]
}
}
},
"hasMany": {
"Vaults": {
"request": {
"operation": "ListVaults",
"params": [
{ "target": "accountId", "source": "string", "value": "-" }
]
},
"resource": {
"type": "Vault",
"identifiers": [
{ "target": "AccountId", "source": "requestParameter", "path": "accountId" },
{ "target": "Name", "source": "response", "path": "VaultList[].VaultName" }
],
"path": "VaultList[]"
}
}
}
},
"resources": {
"Account": {
"identifiers": [
{ "name": "Id" }
],
"actions": {
"CreateVault": {
"request": {
"operation": "CreateVault",
"params": [
{ "target": "accountId", "source": "identifier", "name": "Id" }
]
},
"resource": {
"type": "Vault",
"identifiers": [
{ "target": "AccountId", "source": "identifier", "name": "Id" },
{ "target": "Name", "source": "requestParameter", "path": "vaultName" }
]
}
}
},
"has": {
"Vault": {
"resource": {
"type": "Vault",
"identifiers": [
{ "target": "AccountId", "source": "identifier", "name": "Id" },
{ "target": "Name", "source": "input" }
]
}
}
},
"hasMany": {
"Vaults": {
"request": {
"operation": "ListVaults",
"params": [
{ "target": "accountId", "source": "identifier", "name": "Id" }
]
},
"resource": {
"type": "Vault",
"identifiers": [
{ "target": "AccountId", "source": "identifier", "name": "Id" },
{ "target": "Name", "source": "response", "path": "VaultList[].VaultName" }
],
"path": "VaultList[]"
}
}
}
},
"Archive": {
"identifiers": [
{ "name": "AccountId" },
{ "name": "VaultName" },
{ "name": "Id" }
],
"actions": {
"Delete": {
"request": {
"operation": "DeleteArchive",
"params": [
{ "target": "accountId", "source": "identifier", "name": "AccountId" },
{ "target": "vaultName", "source": "identifier", "name": "VaultName" },
{ "target": "archiveId", "source": "identifier", "name": "Id" }
]
}
},
"InitiateArchiveRetrieval": {
"request": {
"operation": "InitiateJob",
"params": [
{ "target": "vaultName", "source": "identifier", "name": "VaultName" },
{ "target": "accountId", "source": "identifier", "name": "AccountId" },
{ "target": "jobParameters.Type", "source": "string", "value": "archive-retrieval" },
{ "target": "jobParameters.ArchiveId", "source": "identifier", "name": "Id" }
]
},
"resource": {
"type": "Job",
"identifiers": [
{ "target": "Id", "source": "response", "path": "jobId" },
{ "target": "AccountId", "source": "identifier", "name": "AccountId" },
{ "target": "VaultName", "source": "identifier", "name": "VaultName" }
]
}
}
},
"has": {
"Vault": {
"resource": {
"type": "Vault",
"identifiers": [
{ "target": "AccountId", "source": "identifier", "name": "AccountId" },
{ "target": "Name", "source": "identifier", "name": "VaultName" }
]
}
}
}
},
"Job": {
"identifiers": [
{ "name": "AccountId" },
{ "name": "VaultName" },
{
"name": "Id",
"memberName": "JobId"
}
],
"shape": "GlacierJobDescription",
"load": {
"request": {
"operation": "DescribeJob",
"params": [
{ "target": "accountId", "source": "identifier", "name": "AccountId" },
{ "target": "vaultName", "source": "identifier", "name": "VaultName" },
{ "target": "jobId", "source": "identifier", "name": "Id" }
]
},
"path": "@"
},
"actions": {
"GetOutput": {
"request": {
"operation": "GetJobOutput",
"params": [
{ "target": "accountId", "source": "identifier", "name": "AccountId" },
{ "target": "vaultName", "source": "identifier", "name": "VaultName" },
{ "target": "jobId", "source": "identifier", "name": "Id" }
]
}
}
},
"has": {
"Vault": {
"resource": {
"type": "Vault",
"identifiers": [
{ "target": "AccountId", "source": "identifier", "name": "AccountId" },
{ "target": "Name", "source": "identifier", "name": "VaultName" }
]
}
}
}
},
"MultipartUpload": {
"identifiers": [
{ "name": "AccountId" },
{ "name": "VaultName" },
{
"name": "Id",
"memberName": "MultipartUploadId"
}
],
"shape": "UploadListElement",
"actions": {
"Abort": {
"request": {
"operation": "AbortMultipartUpload",
"params": [
{ "target": "accountId", "source": "identifier", "name": "AccountId" },
{ "target": "vaultName", "source": "identifier", "name": "VaultName" },
{ "target": "uploadId", "source": "identifier", "name": "Id" }
]
}
},
"Complete": {
"request": {
"operation": "CompleteMultipartUpload",
"params": [
{ "target": "accountId", "source": "identifier", "name": "AccountId" },
{ "target": "vaultName", "source": "identifier", "name": "VaultName" },
{ "target": "uploadId", "source": "identifier", "name": "Id" }
]
}
},
"Parts": {
"request": {
"operation": "ListParts",
"params": [
{ "target": "accountId", "source": "identifier", "name": "AccountId" },
{ "target": "vaultName", "source": "identifier", "name": "VaultName" },
{ "target": "uploadId", "source": "identifier", "name": "Id" }
]
}
},
"UploadPart": {
"request": {
"operation": "UploadMultipartPart",
"params": [
{ "target": "accountId", "source": "identifier", "name": "AccountId" },
{ "target": "vaultName", "source": "identifier", "name": "VaultName" },
{ "target": "uploadId", "source": "identifier", "name": "Id" }
]
}
}
},
"has": {
"Vault": {
"resource": {
"type": "Vault",
"identifiers": [
{ "target": "AccountId", "source": "identifier", "name": "AccountId" },
{ "target": "Name", "source": "identifier", "name": "VaultName" }
]
}
}
}
},
"Notification": {
"identifiers": [
{ "name": "AccountId" },
{ "name": "VaultName" }
],
"shape": "VaultNotificationConfig",
"load": {
"request": {
"operation": "GetVaultNotifications",
"params": [
{ "target": "accountId", "source": "identifier", "name": "AccountId" },
{ "target": "vaultName", "source": "identifier", "name": "VaultName" }
]
},
"path": "vaultNotificationConfig"
},
"actions": {
"Delete": {
"request": {
"operation": "DeleteVaultNotifications",
"params": [
{ "target": "accountId", "source": "identifier", "name": "AccountId" },
{ "target": "vaultName", "source": "identifier", "name": "VaultName" }
]
}
},
"Set": {
"request": {
"operation": "SetVaultNotifications",
"params": [
{ "target": "accountId", "source": "identifier", "name": "AccountId" },
{ "target": "vaultName", "source": "identifier", "name": "VaultName" }
]
}
}
},
"has": {
"Vault": {
"resource": {
"type": "Vault",
"identifiers": [
{ "target": "AccountId", "source": "identifier", "name": "AccountId" },
{ "target": "Name", "source": "identifier", "name": "VaultName" }
]
}
}
}
},
"Vault": {
"identifiers": [
{ "name": "AccountId" },
{
"name": "Name",
"memberName": "VaultName"
}
],
"shape": "DescribeVaultOutput",
"load": {
"request": {
"operation": "DescribeVault",
"params": [
{ "target": "vaultName", "source": "identifier", "name": "Name" },
{ "target": "accountId", "source": "identifier", "name": "AccountId" }
]
},
"path": "@"
},
"actions": {
"Create": {
"request": {
"operation": "CreateVault",
"params": [
{ "target": "vaultName", "source": "identifier", "name": "Name" },
{ "target": "accountId", "source": "identifier", "name": "AccountId" }
]
}
},
"Delete": {
"request": {
"operation": "DeleteVault",
"params": [
{ "target": "vaultName", "source": "identifier", "name": "Name" },
{ "target": "accountId", "source": "identifier", "name": "AccountId" }
]
}
},
"InitiateInventoryRetrieval": {
"request": {
"operation": "InitiateJob",
"params": [
{ "target": "vaultName", "source": "identifier", "name": "Name" },
{ "target": "accountId", "source": "identifier", "name": "AccountId" },
{ "target": "jobParameters.Type", "source": "string", "value": "inventory-retrieval" }
]
},
"resource": {
"type": "Job",
"identifiers": [
{ "target": "Id", "source": "response", "path": "jobId" },
{ "target": "AccountId", "source": "identifier", "name": "AccountId" },
{ "target": "VaultName", "source": "identifier", "name": "Name" }
]
}
},
"InitiateMultipartUpload": {
"request": {
"operation": "InitiateMultipartUpload",
"params": [
{ "target": "vaultName", "source": "identifier", "name": "Name" },
{ "target": "accountId", "source": "identifier", "name": "AccountId" }
]
},
"resource": {
"type": "MultipartUpload",
"identifiers": [
{ "target": "Id", "source": "response", "path": "uploadId" },
{ "target": "AccountId", "source": "identifier", "name": "AccountId" },
{ "target": "VaultName", "source": "identifier", "name": "Name" }
]
}
},
"UploadArchive": {
"request": {
"operation": "UploadArchive",
"params": [
{ "target": "vaultName", "source": "identifier", "name": "Name" },
{ "target": "accountId", "source": "identifier", "name": "AccountId" }
]
},
"resource": {
"type": "Archive",
"identifiers": [
{ "target": "Id", "source": "response", "path": "archiveId" },
{ "target": "AccountId", "source": "identifier", "name": "AccountId" },
{ "target": "VaultName", "source": "identifier", "name": "Name" }
]
}
}
},
"has": {
"Account": {
"resource": {
"type": "Account",
"identifiers": [
{ "target": "Id", "source": "identifier", "name": "AccountId" }
]
}
},
"Archive": {
"resource": {
"type": "Archive",
"identifiers": [
{ "target": "AccountId", "source": "identifier", "name": "AccountId" },
{ "target": "VaultName", "source": "identifier", "name": "Name" },
{ "target": "Id", "source": "input" }
]
}
},
"Job": {
"resource": {
"type": "Job",
"identifiers": [
{ "target": "AccountId", "source": "identifier", "name": "AccountId" },
{ "target": "VaultName", "source": "identifier", "name": "Name" },
{ "target": "Id", "source": "input" }
]
}
},
"MultipartUpload": {
"resource": {
"type": "MultipartUpload",
"identifiers": [
{ "target": "AccountId", "source": "identifier", "name": "AccountId" },
{ "target": "VaultName", "source": "identifier", "name": "Name" },
{ "target": "Id", "source": "input" }
]
}
},
"Notification": {
"resource": {
"type": "Notification",
"identifiers": [
{ "target": "AccountId", "source": "identifier", "name": "AccountId" },
{ "target": "VaultName", "source": "identifier", "name": "Name" }
]
}
}
},
"hasMany": {
"CompletedJobs": {
"request": {
"operation": "ListJobs",
"params": [
{ "target": "accountId", "source": "identifier", "name": "AccountId" },
{ "target": "vaultName", "source": "identifier", "name": "Name" },
{ "target": "completed", "source": "string", "value": "true" }
]
},
"resource": {
"type": "Job",
"identifiers": [
{ "target": "AccountId", "source": "identifier", "name": "AccountId" },
{ "target": "VaultName", "source": "identifier", "name": "Name" },
{ "target": "Id", "source": "response", "path": "JobList[].JobId" }
],
"path": "JobList[]"
}
},
"FailedJobs": {
"request": {
"operation": "ListJobs",
"params": [
{ "target": "accountId", "source": "identifier", "name": "AccountId" },
{ "target": "vaultName", "source": "identifier", "name": "Name" },
{ "target": "statuscode", "source": "string", "value": "Failed" }
]
},
"resource": {
"type": "Job",
"identifiers": [
{ "target": "AccountId", "source": "identifier", "name": "AccountId" },
{ "target": "VaultName", "source": "identifier", "name": "Name" },
{ "target": "Id", "source": "response", "path": "JobList[].JobId" }
],
"path": "JobList[]"
}
},
"Jobs": {
"request": {
"operation": "ListJobs",
"params": [
{ "target": "accountId", "source": "identifier", "name": "AccountId" },
{ "target": "vaultName", "source": "identifier", "name": "Name" }
]
},
"resource": {
"type": "Job",
"identifiers": [
{ "target": "AccountId", "source": "identifier", "name": "AccountId" },
{ "target": "VaultName", "source": "identifier", "name": "Name" },
{ "target": "Id", "source": "response", "path": "JobList[].JobId" }
],
"path": "JobList[]"
}
},
"JobsInProgress": {
"request": {
"operation": "ListJobs",
"params": [
{ "target": "accountId", "source": "identifier", "name": "AccountId" },
{ "target": "vaultName", "source": "identifier", "name": "Name" },
{ "target": "statuscode", "source": "string", "value": "InProgress" }
]
},
"resource": {
"type": "Job",
"identifiers": [
{ "target": "AccountId", "source": "identifier", "name": "AccountId" },
{ "target": "VaultName", "source": "identifier", "name": "Name" },
{ "target": "Id", "source": "response", "path": "JobList[].JobId" }
],
"path": "JobList[]"
}
},
"MultipartUplaods": {
"request": {
"operation": "ListMultipartUploads",
"params": [
{ "target": "vaultName", "source": "identifier", "name": "Name" },
{ "target": "accountId", "source": "identifier", "name": "AccountId" }
]
},
"resource": {
"type": "MultipartUpload",
"identifiers": [
{ "target": "AccountId", "source": "identifier", "name": "AccountId" },
{ "target": "VaultName", "source": "identifier", "name": "Name" },
{ "target": "Id", "source": "response", "path": "UploadsList[].MultipartUploadId" }
],
"path": "UploadsList[]"
}
},
"MultipartUploads": {
"request": {
"operation": "ListMultipartUploads",
"params": [
{ "target": "vaultName", "source": "identifier", "name": "Name" },
{ "target": "accountId", "source": "identifier", "name": "AccountId" }
]
},
"resource": {
"type": "MultipartUpload",
"identifiers": [
{ "target": "AccountId", "source": "identifier", "name": "AccountId" },
{ "target": "VaultName", "source": "identifier", "name": "Name" },
{ "target": "Id", "source": "response", "path": "UploadsList[].MultipartUploadId" }
],
"path": "UploadsList[]"
}
},
"SucceededJobs": {
"request": {
"operation": "ListJobs",
"params": [
{ "target": "accountId", "source": "identifier", "name": "AccountId" },
{ "target": "vaultName", "source": "identifier", "name": "Name" },
{ "target": "statuscode", "source": "string", "value": "Succeeded" }
]
},
"resource": {
"type": "Job",
"identifiers": [
{ "target": "AccountId", "source": "identifier", "name": "AccountId" },
{ "target": "VaultName", "source": "identifier", "name": "Name" },
{ "target": "Id", "source": "response", "path": "JobList[].JobId" }
],
"path": "JobList[]"
}
}
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,173 @@
{
"service": {
"actions": {
"CreateStack": {
"request": { "operation": "CreateStack" },
"resource": {
"type": "Stack",
"identifiers": [
{ "target": "Id", "source": "response", "path": "StackId" }
]
}
}
},
"has": {
"Layer": {
"resource": {
"type": "Layer",
"identifiers": [
{ "target": "Id", "source": "input" }
]
}
},
"Stack": {
"resource": {
"type": "Stack",
"identifiers": [
{ "target": "Id", "source": "input" }
]
}
}
},
"hasMany": {
"Stacks": {
"request": { "operation": "DescribeStacks" },
"resource": {
"type": "Stack",
"identifiers": [
{ "target": "Id", "source": "response", "path": "Stacks[].StackId" }
],
"path": "Stacks[]"
}
}
}
},
"resources": {
"Layer": {
"identifiers": [
{ "name": "Id" }
],
"shape": "Layer",
"load": {
"request": {
"operation": "DescribeLayers",
"params": [
{ "target": "LayerIds[]", "source": "identifier", "name": "Id" }
]
},
"path": "Layers[0]"
},
"actions": {
"Delete": {
"request": {
"operation": "DeleteLayer",
"params": [
{ "target": "LayerId", "source": "identifier", "name": "Id" }
]
}
}
},
"has": {
"Stack": {
"resource": {
"type": "Stack",
"identifiers": [
{ "target": "Id", "source": "data", "path": "StackId" }
]
}
}
}
},
"Stack": {
"identifiers": [
{ "name": "Id" }
],
"shape": "Stack",
"load": {
"request": {
"operation": "DescribeStacks",
"params": [
{ "target": "StackIds[]", "source": "identifier", "name": "Id" }
]
},
"path": "Stacks[0]"
},
"actions": {
"CreateLayer": {
"request": {
"operation": "CreateLayer",
"params": [
{ "target": "StackId", "source": "identifier", "name": "Id" }
]
},
"resource": {
"type": "Layer",
"identifiers": [
{ "target": "Id", "source": "response", "path": "LayerId" }
]
}
},
"Delete": {
"request": {
"operation": "DeleteStack",
"params": [
{ "target": "StackId", "source": "identifier", "name": "Id" }
]
}
}
},
"has": {
"Summary": {
"resource": {
"type": "StackSummary",
"identifiers": [
{ "target": "StackId", "source": "identifier", "name": "Id" }
]
}
}
},
"hasMany": {
"Layers": {
"request": {
"operation": "DescribeLayers",
"params": [
{ "target": "StackId", "source": "identifier", "name": "Id" }
]
},
"resource": {
"type": "Layer",
"identifiers": [
{ "target": "Id", "source": "response", "path": "Layers[].LayerId" }
],
"path": "Layers[]"
}
}
}
},
"StackSummary": {
"identifiers": [
{ "name": "StackId" }
],
"shape": "StackSummary",
"load": {
"request": {
"operation": "DescribeStackSummary",
"params": [
{ "target": "StackId", "source": "identifier", "name": "StackId" }
]
},
"path": "StackSummary"
},
"has": {
"Stack": {
"resource": {
"type": "Stack",
"identifiers": [
{ "target": "Id", "source": "identifier", "name": "StackId" }
]
}
}
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,327 @@
{
"service": {
"actions": {
"CreatePlatformApplication": {
"request": { "operation": "CreatePlatformApplication" },
"resource": {
"type": "PlatformApplication",
"identifiers": [
{ "target": "Arn", "source": "response", "path": "PlatformApplicationArn" }
]
}
},
"CreateTopic": {
"request": { "operation": "CreateTopic" },
"resource": {
"type": "Topic",
"identifiers": [
{ "target": "Arn", "source": "response", "path": "TopicArn" }
]
}
}
},
"has": {
"PlatformApplication": {
"resource": {
"type": "PlatformApplication",
"identifiers": [
{ "target": "Arn", "source": "input" }
]
}
},
"PlatformEndpoint": {
"resource": {
"type": "PlatformEndpoint",
"identifiers": [
{ "target": "Arn", "source": "input" }
]
}
},
"Subscription": {
"resource": {
"type": "Subscription",
"identifiers": [
{ "target": "Arn", "source": "input" }
]
}
},
"Topic": {
"resource": {
"type": "Topic",
"identifiers": [
{ "target": "Arn", "source": "input" }
]
}
}
},
"hasMany": {
"PlatformApplications": {
"request": { "operation": "ListPlatformApplications" },
"resource": {
"type": "PlatformApplication",
"identifiers": [
{ "target": "Arn", "source": "response", "path": "PlatformApplications[].PlatformApplicationArn" }
]
}
},
"Subscriptions": {
"request": { "operation": "ListSubscriptions" },
"resource": {
"type": "Subscription",
"identifiers": [
{ "target": "Arn", "source": "response", "path": "Subscriptions[].SubscriptionArn" }
]
}
},
"Topics": {
"request": { "operation": "ListTopics" },
"resource": {
"type": "Topic",
"identifiers": [
{ "target": "Arn", "source": "response", "path": "Topics[].TopicArn" }
]
}
}
}
},
"resources": {
"PlatformApplication": {
"identifiers": [
{ "name": "Arn" }
],
"shape": "GetPlatformApplicationAttributesResponse",
"load": {
"request": {
"operation": "GetPlatformApplicationAttributes",
"params": [
{ "target": "PlatformApplicationArn", "source": "identifier", "name": "Arn" }
]
},
"path": "@"
},
"actions": {
"CreatePlatformEndpoint": {
"request": {
"operation": "CreatePlatformEndpoint",
"params": [
{ "target": "PlatformApplicationArn", "source": "identifier", "name": "Arn" }
]
},
"resource": {
"type": "PlatformEndpoint",
"identifiers": [
{ "target": "Arn", "source": "response", "path": "EndpointArn" }
]
}
},
"Delete": {
"request": {
"operation": "DeletePlatformApplication",
"params": [
{ "target": "PlatformApplicationArn", "source": "identifier", "name": "Arn" }
]
}
},
"SetAttributes": {
"request": {
"operation": "SetPlatformApplicationAttributes",
"params": [
{ "target": "PlatformApplicationArn", "source": "identifier", "name": "Arn" }
]
}
}
},
"hasMany": {
"Endpoints": {
"request": {
"operation": "ListEndpointsByPlatformApplication",
"params": [
{ "target": "PlatformApplicationArn", "source": "identifier", "name": "Arn" }
]
},
"resource": {
"type": "PlatformEndpoint",
"identifiers": [
{ "target": "Arn", "source": "response", "path": "Endpoints[].EndpointArn" }
]
}
}
}
},
"PlatformEndpoint": {
"identifiers": [
{ "name": "Arn" }
],
"shape": "GetEndpointAttributesResponse",
"load": {
"request": {
"operation": "GetEndpointAttributes",
"params": [
{ "target": "EndpointArn", "source": "identifier", "name": "Arn" }
]
},
"path": "@"
},
"actions": {
"Delete": {
"request": {
"operation": "DeleteEndpoint",
"params": [
{ "target": "EndpointArn", "source": "identifier", "name": "Arn" }
]
}
},
"Publish": {
"request": {
"operation": "Publish",
"params": [
{ "target": "TargetArn", "source": "identifier", "name": "Arn" }
]
}
},
"SetAttributes": {
"request": {
"operation": "SetEndpointAttributes",
"params": [
{ "target": "EndpointArn", "source": "identifier", "name": "Arn" }
]
}
}
}
},
"Subscription": {
"identifiers": [
{ "name": "Arn" }
],
"shape": "GetSubscriptionAttributesResponse",
"load": {
"request": {
"operation": "GetSubscriptionAttributes",
"params": [
{ "target": "SubscriptionArn", "source": "identifier", "name": "Arn" }
]
},
"path": "@"
},
"actions": {
"Delete": {
"request": {
"operation": "Unsubscribe",
"params": [
{ "target": "SubscriptionArn", "source": "identifier", "name": "Arn" }
]
}
},
"SetAttributes": {
"request": {
"operation": "SetSubscriptionAttributes",
"params": [
{ "target": "SubscriptionArn", "source": "identifier", "name": "Arn" }
]
}
}
}
},
"Topic": {
"identifiers": [
{ "name": "Arn" }
],
"shape": "GetTopicAttributesResponse",
"load": {
"request": {
"operation": "GetTopicAttributes",
"params": [
{ "target": "TopicArn", "source": "identifier", "name": "Arn" }
]
},
"path": "@"
},
"actions": {
"AddPermission": {
"request": {
"operation": "AddPermission",
"params": [
{ "target": "TopicArn", "source": "identifier", "name": "Arn" }
]
}
},
"ConfirmSubscription": {
"request": {
"operation": "ConfirmSubscription",
"params": [
{ "target": "TopicArn", "source": "identifier", "name": "Arn" }
]
},
"resource": {
"type": "Subscription",
"identifiers": [
{ "target": "Arn", "source": "response", "path": "SubscriptionArn" }
]
}
},
"Delete": {
"request": {
"operation": "DeleteTopic",
"params": [
{ "target": "TopicArn", "source": "identifier", "name": "Arn" }
]
}
},
"Publish": {
"request": {
"operation": "Publish",
"params": [
{ "target": "TopicArn", "source": "identifier", "name": "Arn" }
]
}
},
"RemovePermission": {
"request": {
"operation": "RemovePermission",
"params": [
{ "target": "TopicArn", "source": "identifier", "name": "Arn" }
]
}
},
"SetAttributes": {
"request": {
"operation": "SetTopicAttributes",
"params": [
{ "target": "TopicArn", "source": "identifier", "name": "Arn" }
]
}
},
"Subscribe": {
"request": {
"operation": "Subscribe",
"params": [
{ "target": "TopicArn", "source": "identifier", "name": "Arn" }
]
},
"resource": {
"type": "Subscription",
"identifiers": [
{ "target": "Arn", "source": "response", "path": "SubscriptionArn" }
]
}
}
},
"hasMany": {
"Subscriptions": {
"request": {
"operation": "ListSubscriptionsByTopic",
"params": [
{ "target": "TopicArn", "source": "identifier", "name": "Arn" }
]
},
"resource": {
"type": "Subscription",
"identifiers": [
{ "target": "Arn", "source": "response", "path": "Subscriptions[].SubscriptionArn" }
]
}
}
}
}
}
}

View File

@@ -0,0 +1,232 @@
{
"service": {
"actions": {
"CreateQueue": {
"request": { "operation": "CreateQueue" },
"resource": {
"type": "Queue",
"identifiers": [
{ "target": "Url", "source": "response", "path": "QueueUrl" }
]
}
},
"GetQueueByName": {
"request": { "operation": "GetQueueUrl" },
"resource": {
"type": "Queue",
"identifiers": [
{ "target": "Url", "source": "response", "path": "QueueUrl" }
]
}
}
},
"has": {
"Queue": {
"resource": {
"type": "Queue",
"identifiers": [
{ "target": "Url", "source": "input" }
]
}
}
},
"hasMany": {
"Queues": {
"request": { "operation": "ListQueues" },
"resource": {
"type": "Queue",
"identifiers": [
{ "target": "Url", "source": "response", "path": "QueueUrls[]" }
]
}
}
}
},
"resources": {
"Message": {
"identifiers": [
{ "name": "QueueUrl" },
{
"name": "ReceiptHandle",
"memberName": "ReceiptHandle"
}
],
"shape": "Message",
"actions": {
"ChangeVisibility": {
"request": {
"operation": "ChangeMessageVisibility",
"params": [
{ "target": "QueueUrl", "source": "identifier", "name": "QueueUrl" },
{ "target": "ReceiptHandle", "source": "identifier", "name": "ReceiptHandle" }
]
}
},
"Delete": {
"request": {
"operation": "DeleteMessage",
"params": [
{ "target": "QueueUrl", "source": "identifier", "name": "QueueUrl" },
{ "target": "ReceiptHandle", "source": "identifier", "name": "ReceiptHandle" }
]
}
}
},
"batchActions": {
"Delete": {
"request": {
"operation": "DeleteMessageBatch",
"params": [
{ "target": "QueueUrl", "source": "identifier", "name": "QueueUrl" },
{ "target": "Entries[*].Id", "source": "data", "path": "MessageId" },
{ "target": "Entries[*].ReceiptHandle", "source": "identifier", "name": "ReceiptHandle" }
]
}
}
},
"has": {
"Queue": {
"resource": {
"type": "Queue",
"identifiers": [
{ "target": "Url", "source": "identifier", "name": "QueueUrl" }
]
}
}
}
},
"Queue": {
"identifiers": [
{ "name": "Url" }
],
"shape": "GetQueueAttributesResult",
"load": {
"request": {
"operation": "GetQueueAttributes",
"params": [
{ "target": "QueueUrl", "source": "identifier", "name": "Url" },
{ "target": "AttributeNames[]", "source": "string", "value": "All" }
]
},
"path": "@"
},
"actions": {
"AddPermission": {
"request": {
"operation": "AddPermission",
"params": [
{ "target": "QueueUrl", "source": "identifier", "name": "Url" }
]
}
},
"ChangeMessageVisibilityBatch": {
"request": {
"operation": "ChangeMessageVisibilityBatch",
"params": [
{ "target": "QueueUrl", "source": "identifier", "name": "Url" }
]
}
},
"Delete": {
"request": {
"operation": "DeleteQueue",
"params": [
{ "target": "QueueUrl", "source": "identifier", "name": "Url" }
]
}
},
"DeleteMessages": {
"request": {
"operation": "DeleteMessageBatch",
"params": [
{ "target": "QueueUrl", "source": "identifier", "name": "Url" }
]
}
},
"Purge": {
"request": {
"operation": "PurgeQueue",
"params": [
{ "target": "QueueUrl", "source": "identifier", "name": "Url" }
]
}
},
"ReceiveMessages": {
"request": {
"operation": "ReceiveMessage",
"params": [
{ "target": "QueueUrl", "source": "identifier", "name": "Url" }
]
},
"resource": {
"type": "Message",
"identifiers": [
{ "target": "QueueUrl", "source": "identifier", "name": "Url" },
{ "target": "ReceiptHandle", "source": "response", "path": "Messages[].ReceiptHandle" }
],
"path": "Messages[]"
}
},
"RemovePermission": {
"request": {
"operation": "RemovePermission",
"params": [
{ "target": "QueueUrl", "source": "identifier", "name": "Url" }
]
}
},
"SendMessage": {
"request": {
"operation": "SendMessage",
"params": [
{ "target": "QueueUrl", "source": "identifier", "name": "Url" }
]
}
},
"SendMessages": {
"request": {
"operation": "SendMessageBatch",
"params": [
{ "target": "QueueUrl", "source": "identifier", "name": "Url" }
]
}
},
"SetAttributes": {
"request": {
"operation": "SetQueueAttributes",
"params": [
{ "target": "QueueUrl", "source": "identifier", "name": "Url" }
]
}
}
},
"has": {
"Message": {
"resource": {
"type": "Message",
"identifiers": [
{ "target": "QueueUrl", "source": "identifier", "name": "Url" },
{ "target": "ReceiptHandle", "source": "input" }
]
}
}
},
"hasMany": {
"DeadLetterSourceQueues": {
"request": {
"operation": "ListDeadLetterSourceQueues",
"params": [
{ "target": "QueueUrl", "source": "identifier", "name": "Url" }
]
},
"resource": {
"type": "Queue",
"identifiers": [
{ "target": "Url", "source": "response", "path": "queueUrls[]" }
]
}
}
}
}
}
}

View File

@@ -0,0 +1,51 @@
# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
# https://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.
import os
from botocore.docs import DEPRECATED_SERVICE_NAMES
from boto3.docs.service import ServiceDocumenter
def generate_docs(root_dir, session):
"""Generates the reference documentation for botocore
This will go through every available AWS service and output ReSTructured
text files documenting each service.
:param root_dir: The directory to write the reference files to. Each
service's reference documentation is loacated at
root_dir/reference/services/service-name.rst
:param session: The boto3 session
"""
services_doc_path = os.path.join(root_dir, 'reference', 'services')
if not os.path.exists(services_doc_path):
os.makedirs(services_doc_path)
# Prevents deprecated service names from being generated in docs.
available_services = [
service
for service in session.get_available_services()
if service not in DEPRECATED_SERVICE_NAMES
]
for service_name in available_services:
docs = ServiceDocumenter(
service_name, session, services_doc_path
).document_service()
service_doc_path = os.path.join(
services_doc_path, service_name + '.rst'
)
with open(service_doc_path, 'wb') as f:
f.write(docs)

View File

@@ -0,0 +1,217 @@
# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
# https://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.
import os
from botocore import xform_name
from botocore.docs.bcdoc.restdoc import DocumentStructure
from botocore.docs.method import (
document_custom_method,
document_model_driven_method,
)
from botocore.model import OperationModel
from botocore.utils import get_service_module_name
from boto3.docs.base import NestedDocumenter
from boto3.docs.method import document_model_driven_resource_method
from boto3.docs.utils import (
add_resource_type_overview,
get_resource_ignore_params,
get_resource_public_actions,
)
PUT_DATA_WARNING_MESSAGE = """
.. warning::
It is recommended to use the :py:meth:`put_metric_data`
:doc:`client method <../../cloudwatch/client/put_metric_data>`
instead. If you would still like to use this resource method,
please make sure that ``MetricData[].MetricName`` is equal to
the metric resource's ``name`` attribute.
"""
WARNING_MESSAGES = {
"Metric": {"put_data": PUT_DATA_WARNING_MESSAGE},
}
IGNORE_PARAMS = {"Metric": {"put_data": ["Namespace"]}}
class ActionDocumenter(NestedDocumenter):
def document_actions(self, section):
modeled_actions_list = self._resource_model.actions
modeled_actions = {}
for modeled_action in modeled_actions_list:
modeled_actions[modeled_action.name] = modeled_action
resource_actions = get_resource_public_actions(
self._resource.__class__
)
self.member_map['actions'] = sorted(resource_actions)
add_resource_type_overview(
section=section,
resource_type='Actions',
description=(
'Actions call operations on resources. They may '
'automatically handle the passing in of arguments set '
'from identifiers and some attributes.'
),
intro_link='actions_intro',
)
resource_warnings = WARNING_MESSAGES.get(self._resource_name, {})
for action_name in sorted(resource_actions):
# Create a new DocumentStructure for each action and add contents.
action_doc = DocumentStructure(action_name, target='html')
breadcrumb_section = action_doc.add_new_section('breadcrumb')
breadcrumb_section.style.ref(self._resource_class_name, 'index')
breadcrumb_section.write(f' / Action / {action_name}')
action_doc.add_title_section(action_name)
warning_message = resource_warnings.get(action_name)
if warning_message is not None:
action_doc.add_new_section("warning").write(warning_message)
action_section = action_doc.add_new_section(
action_name,
context={'qualifier': f'{self.class_name}.'},
)
if action_name in ['load', 'reload'] and self._resource_model.load:
document_load_reload_action(
section=action_section,
action_name=action_name,
resource_name=self._resource_name,
event_emitter=self._resource.meta.client.meta.events,
load_model=self._resource_model.load,
service_model=self._service_model,
)
elif action_name in modeled_actions:
document_action(
section=action_section,
resource_name=self._resource_name,
event_emitter=self._resource.meta.client.meta.events,
action_model=modeled_actions[action_name],
service_model=self._service_model,
)
else:
document_custom_method(
action_section, action_name, resource_actions[action_name]
)
# Write actions in individual/nested files.
# Path: <root>/reference/services/<service>/<resource_name>/<action_name>.rst
actions_dir_path = os.path.join(
self._root_docs_path,
f'{self._service_name}',
f'{self._resource_sub_path}',
)
action_doc.write_to_file(actions_dir_path, action_name)
def document_action(
section,
resource_name,
event_emitter,
action_model,
service_model,
include_signature=True,
):
"""Documents a resource action
:param section: The section to write to
:param resource_name: The name of the resource
:param event_emitter: The event emitter to use to emit events
:param action_model: The model of the action
:param service_model: The model of the service
:param include_signature: Whether or not to include the signature.
It is useful for generating docstrings.
"""
operation_model = service_model.operation_model(
action_model.request.operation
)
ignore_params = IGNORE_PARAMS.get(resource_name, {}).get(
action_model.name,
get_resource_ignore_params(action_model.request.params),
)
example_return_value = 'response'
if action_model.resource:
example_return_value = xform_name(action_model.resource.type)
example_resource_name = xform_name(resource_name)
if service_model.service_name == resource_name:
example_resource_name = resource_name
example_prefix = '{} = {}.{}'.format(
example_return_value, example_resource_name, action_model.name
)
full_action_name = (
f"{section.context.get('qualifier', '')}{action_model.name}"
)
document_model_driven_resource_method(
section=section,
method_name=full_action_name,
operation_model=operation_model,
event_emitter=event_emitter,
method_description=operation_model.documentation,
example_prefix=example_prefix,
exclude_input=ignore_params,
resource_action_model=action_model,
include_signature=include_signature,
)
def document_load_reload_action(
section,
action_name,
resource_name,
event_emitter,
load_model,
service_model,
include_signature=True,
):
"""Documents the resource load action
:param section: The section to write to
:param action_name: The name of the loading action should be load or reload
:param resource_name: The name of the resource
:param event_emitter: The event emitter to use to emit events
:param load_model: The model of the load action
:param service_model: The model of the service
:param include_signature: Whether or not to include the signature.
It is useful for generating docstrings.
"""
description = (
'Calls :py:meth:`{}.Client.{}` to update the attributes of the '
'{} resource. Note that the load and reload methods are '
'the same method and can be used interchangeably.'.format(
get_service_module_name(service_model),
xform_name(load_model.request.operation),
resource_name,
)
)
example_resource_name = xform_name(resource_name)
if service_model.service_name == resource_name:
example_resource_name = resource_name
example_prefix = f'{example_resource_name}.{action_name}'
full_action_name = f"{section.context.get('qualifier', '')}{action_name}"
document_model_driven_method(
section=section,
method_name=full_action_name,
operation_model=OperationModel({}, service_model),
event_emitter=event_emitter,
method_description=description,
example_prefix=example_prefix,
include_signature=include_signature,
)

View File

@@ -0,0 +1,72 @@
# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
# https://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.
from botocore.docs.params import ResponseParamsDocumenter
from boto3.docs.utils import get_identifier_description
class ResourceShapeDocumenter(ResponseParamsDocumenter):
EVENT_NAME = 'resource-shape'
def document_attribute(
section,
service_name,
resource_name,
attr_name,
event_emitter,
attr_model,
include_signature=True,
):
if include_signature:
full_attr_name = f"{section.context.get('qualifier', '')}{attr_name}"
section.style.start_sphinx_py_attr(full_attr_name)
# Note that an attribute may have one, may have many, or may have no
# operations that back the resource's shape. So we just set the
# operation_name to the resource name if we ever to hook in and modify
# a particular attribute.
ResourceShapeDocumenter(
service_name=service_name,
operation_name=resource_name,
event_emitter=event_emitter,
).document_params(section=section, shape=attr_model)
def document_identifier(
section,
resource_name,
identifier_model,
include_signature=True,
):
if include_signature:
full_identifier_name = (
f"{section.context.get('qualifier', '')}{identifier_model.name}"
)
section.style.start_sphinx_py_attr(full_identifier_name)
description = get_identifier_description(
resource_name, identifier_model.name
)
section.write(f'*(string)* {description}')
def document_reference(section, reference_model, include_signature=True):
if include_signature:
full_reference_name = (
f"{section.context.get('qualifier', '')}{reference_model.name}"
)
section.style.start_sphinx_py_attr(full_reference_name)
reference_type = f'(:py:class:`{reference_model.resource.type}`) '
section.write(reference_type)
section.include_doc_string(
f'The related {reference_model.name} if set, otherwise ``None``.'
)

View File

@@ -0,0 +1,51 @@
# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
# https://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.
from botocore.compat import OrderedDict
class BaseDocumenter:
def __init__(self, resource):
self._resource = resource
self._client = self._resource.meta.client
self._resource_model = self._resource.meta.resource_model
self._service_model = self._client.meta.service_model
self._resource_name = self._resource.meta.resource_model.name
self._service_name = self._service_model.service_name
self._service_docs_name = self._client.__class__.__name__
self.member_map = OrderedDict()
self.represents_service_resource = (
self._service_name == self._resource_name
)
self._resource_class_name = self._resource_name
if self._resource_name == self._service_name:
self._resource_class_name = 'ServiceResource'
@property
def class_name(self):
return f'{self._service_docs_name}.{self._resource_name}'
class NestedDocumenter(BaseDocumenter):
def __init__(self, resource, root_docs_path):
super().__init__(resource)
self._root_docs_path = root_docs_path
self._resource_sub_path = self._resource_name.lower()
if self._resource_name == self._service_name:
self._resource_sub_path = 'service-resource'
@property
def class_name(self):
resource_class_name = self._resource_name
if self._resource_name == self._service_name:
resource_class_name = 'ServiceResource'
return f'{self._service_docs_name}.{resource_class_name}'

View File

@@ -0,0 +1,28 @@
# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
# https://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.
from botocore.docs.client import ClientDocumenter
class Boto3ClientDocumenter(ClientDocumenter):
def _add_client_creation_example(self, section):
section.style.start_codeblock()
section.style.new_line()
section.write('import boto3')
section.style.new_line()
section.style.new_line()
section.write(
'client = boto3.client(\'{service}\')'.format(
service=self._service_name
)
)
section.style.end_codeblock()

View File

@@ -0,0 +1,312 @@
# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
# https://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.
import os
from botocore import xform_name
from botocore.docs.bcdoc.restdoc import DocumentStructure
from botocore.docs.method import get_instance_public_methods
from botocore.docs.utils import DocumentedShape
from boto3.docs.base import NestedDocumenter
from boto3.docs.method import document_model_driven_resource_method
from boto3.docs.utils import (
add_resource_type_overview,
get_resource_ignore_params,
)
class CollectionDocumenter(NestedDocumenter):
def document_collections(self, section):
collections = self._resource.meta.resource_model.collections
collections_list = []
add_resource_type_overview(
section=section,
resource_type='Collections',
description=(
'Collections provide an interface to iterate over and '
'manipulate groups of resources. '
),
intro_link='guide_collections',
)
self.member_map['collections'] = collections_list
for collection in collections:
collections_list.append(collection.name)
# Create a new DocumentStructure for each collection and add contents.
collection_doc = DocumentStructure(collection.name, target='html')
breadcrumb_section = collection_doc.add_new_section('breadcrumb')
breadcrumb_section.style.ref(self._resource_class_name, 'index')
breadcrumb_section.write(f' / Collection / {collection.name}')
collection_doc.add_title_section(collection.name)
collection_section = collection_doc.add_new_section(
collection.name,
context={'qualifier': f'{self.class_name}.'},
)
self._document_collection(collection_section, collection)
# Write collections in individual/nested files.
# Path: <root>/reference/services/<service>/<resource_name>/<collection_name>.rst
collections_dir_path = os.path.join(
self._root_docs_path,
f'{self._service_name}',
f'{self._resource_sub_path}',
)
collection_doc.write_to_file(collections_dir_path, collection.name)
def _document_collection(self, section, collection):
methods = get_instance_public_methods(
getattr(self._resource, collection.name)
)
document_collection_object(section, collection)
batch_actions = {}
for batch_action in collection.batch_actions:
batch_actions[batch_action.name] = batch_action
for method in sorted(methods):
method_section = section.add_new_section(method)
if method in batch_actions:
document_batch_action(
section=method_section,
resource_name=self._resource_name,
event_emitter=self._resource.meta.client.meta.events,
batch_action_model=batch_actions[method],
collection_model=collection,
service_model=self._resource.meta.client.meta.service_model,
)
else:
document_collection_method(
section=method_section,
resource_name=self._resource_name,
action_name=method,
event_emitter=self._resource.meta.client.meta.events,
collection_model=collection,
service_model=self._resource.meta.client.meta.service_model,
)
def document_collection_object(
section,
collection_model,
include_signature=True,
):
"""Documents a collection resource object
:param section: The section to write to
:param collection_model: The model of the collection
:param include_signature: Whether or not to include the signature.
It is useful for generating docstrings.
"""
if include_signature:
full_collection_name = (
f"{section.context.get('qualifier', '')}{collection_model.name}"
)
section.style.start_sphinx_py_attr(full_collection_name)
section.include_doc_string(
f'A collection of {collection_model.resource.type} resources.'
)
section.include_doc_string(
f'A {collection_model.resource.type} Collection will include all '
f'resources by default, and extreme caution should be taken when '
f'performing actions on all resources.'
)
def document_batch_action(
section,
resource_name,
event_emitter,
batch_action_model,
service_model,
collection_model,
include_signature=True,
):
"""Documents a collection's batch action
:param section: The section to write to
:param resource_name: The name of the resource
:param action_name: The name of collection action. Currently only
can be all, filter, limit, or page_size
:param event_emitter: The event emitter to use to emit events
:param batch_action_model: The model of the batch action
:param collection_model: The model of the collection
:param service_model: The model of the service
:param include_signature: Whether or not to include the signature.
It is useful for generating docstrings.
"""
operation_model = service_model.operation_model(
batch_action_model.request.operation
)
ignore_params = get_resource_ignore_params(
batch_action_model.request.params
)
example_return_value = 'response'
if batch_action_model.resource:
example_return_value = xform_name(batch_action_model.resource.type)
example_resource_name = xform_name(resource_name)
if service_model.service_name == resource_name:
example_resource_name = resource_name
example_prefix = '{} = {}.{}.{}'.format(
example_return_value,
example_resource_name,
collection_model.name,
batch_action_model.name,
)
document_model_driven_resource_method(
section=section,
method_name=batch_action_model.name,
operation_model=operation_model,
event_emitter=event_emitter,
method_description=operation_model.documentation,
example_prefix=example_prefix,
exclude_input=ignore_params,
resource_action_model=batch_action_model,
include_signature=include_signature,
)
def document_collection_method(
section,
resource_name,
action_name,
event_emitter,
collection_model,
service_model,
include_signature=True,
):
"""Documents a collection method
:param section: The section to write to
:param resource_name: The name of the resource
:param action_name: The name of collection action. Currently only
can be all, filter, limit, or page_size
:param event_emitter: The event emitter to use to emit events
:param collection_model: The model of the collection
:param service_model: The model of the service
:param include_signature: Whether or not to include the signature.
It is useful for generating docstrings.
"""
operation_model = service_model.operation_model(
collection_model.request.operation
)
underlying_operation_members = []
if operation_model.input_shape:
underlying_operation_members = operation_model.input_shape.members
example_resource_name = xform_name(resource_name)
if service_model.service_name == resource_name:
example_resource_name = resource_name
custom_action_info_dict = {
'all': {
'method_description': (
f'Creates an iterable of all {collection_model.resource.type} '
f'resources in the collection.'
),
'example_prefix': '{}_iterator = {}.{}.all'.format(
xform_name(collection_model.resource.type),
example_resource_name,
collection_model.name,
),
'exclude_input': underlying_operation_members,
},
'filter': {
'method_description': (
f'Creates an iterable of all {collection_model.resource.type} '
f'resources in the collection filtered by kwargs passed to '
f'method. A {collection_model.resource.type} collection will '
f'include all resources by default if no filters are provided, '
f'and extreme caution should be taken when performing actions '
f'on all resources.'
),
'example_prefix': '{}_iterator = {}.{}.filter'.format(
xform_name(collection_model.resource.type),
example_resource_name,
collection_model.name,
),
'exclude_input': get_resource_ignore_params(
collection_model.request.params
),
},
'limit': {
'method_description': (
f'Creates an iterable up to a specified amount of '
f'{collection_model.resource.type} resources in the collection.'
),
'example_prefix': '{}_iterator = {}.{}.limit'.format(
xform_name(collection_model.resource.type),
example_resource_name,
collection_model.name,
),
'include_input': [
DocumentedShape(
name='count',
type_name='integer',
documentation=(
'The limit to the number of resources '
'in the iterable.'
),
)
],
'exclude_input': underlying_operation_members,
},
'page_size': {
'method_description': (
f'Creates an iterable of all {collection_model.resource.type} '
f'resources in the collection, but limits the number of '
f'items returned by each service call by the specified amount.'
),
'example_prefix': '{}_iterator = {}.{}.page_size'.format(
xform_name(collection_model.resource.type),
example_resource_name,
collection_model.name,
),
'include_input': [
DocumentedShape(
name='count',
type_name='integer',
documentation=(
'The number of items returned by each ' 'service call'
),
)
],
'exclude_input': underlying_operation_members,
},
}
if action_name in custom_action_info_dict:
action_info = custom_action_info_dict[action_name]
document_model_driven_resource_method(
section=section,
method_name=action_name,
operation_model=operation_model,
event_emitter=event_emitter,
resource_action_model=collection_model,
include_signature=include_signature,
**action_info,
)

View File

@@ -0,0 +1,77 @@
# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
# https://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.
from botocore.docs.docstring import LazyLoadedDocstring
from boto3.docs.action import document_action, document_load_reload_action
from boto3.docs.attr import (
document_attribute,
document_identifier,
document_reference,
)
from boto3.docs.collection import (
document_batch_action,
document_collection_method,
document_collection_object,
)
from boto3.docs.subresource import document_sub_resource
from boto3.docs.waiter import document_resource_waiter
class ActionDocstring(LazyLoadedDocstring):
def _write_docstring(self, *args, **kwargs):
document_action(*args, **kwargs)
class LoadReloadDocstring(LazyLoadedDocstring):
def _write_docstring(self, *args, **kwargs):
document_load_reload_action(*args, **kwargs)
class SubResourceDocstring(LazyLoadedDocstring):
def _write_docstring(self, *args, **kwargs):
document_sub_resource(*args, **kwargs)
class AttributeDocstring(LazyLoadedDocstring):
def _write_docstring(self, *args, **kwargs):
document_attribute(*args, **kwargs)
class IdentifierDocstring(LazyLoadedDocstring):
def _write_docstring(self, *args, **kwargs):
document_identifier(*args, **kwargs)
class ReferenceDocstring(LazyLoadedDocstring):
def _write_docstring(self, *args, **kwargs):
document_reference(*args, **kwargs)
class CollectionDocstring(LazyLoadedDocstring):
def _write_docstring(self, *args, **kwargs):
document_collection_object(*args, **kwargs)
class CollectionMethodDocstring(LazyLoadedDocstring):
def _write_docstring(self, *args, **kwargs):
document_collection_method(*args, **kwargs)
class BatchActionDocstring(LazyLoadedDocstring):
def _write_docstring(self, *args, **kwargs):
document_batch_action(*args, **kwargs)
class ResourceWaiterDocstring(LazyLoadedDocstring):
def _write_docstring(self, *args, **kwargs):
document_resource_waiter(*args, **kwargs)

View File

@@ -0,0 +1,77 @@
# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
# https://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.
from botocore.docs.method import document_model_driven_method
def document_model_driven_resource_method(
section,
method_name,
operation_model,
event_emitter,
method_description=None,
example_prefix=None,
include_input=None,
include_output=None,
exclude_input=None,
exclude_output=None,
document_output=True,
resource_action_model=None,
include_signature=True,
):
document_model_driven_method(
section=section,
method_name=method_name,
operation_model=operation_model,
event_emitter=event_emitter,
method_description=method_description,
example_prefix=example_prefix,
include_input=include_input,
include_output=include_output,
exclude_input=exclude_input,
exclude_output=exclude_output,
document_output=document_output,
include_signature=include_signature,
)
# If this action returns a resource modify the return example to
# appropriately reflect that.
if resource_action_model.resource:
if 'return' in section.available_sections:
section.delete_section('return')
resource_type = resource_action_model.resource.type
new_return_section = section.add_new_section('return')
return_resource_type = '{}.{}'.format(
operation_model.service_model.service_name, resource_type
)
return_type = f':py:class:`{return_resource_type}`'
return_description = f'{resource_type} resource'
if _method_returns_resource_list(resource_action_model.resource):
return_type = f'list({return_type})'
return_description = f'A list of {resource_type} resources'
new_return_section.style.new_line()
new_return_section.write(f':rtype: {return_type}')
new_return_section.style.new_line()
new_return_section.write(f':returns: {return_description}')
new_return_section.style.new_line()
def _method_returns_resource_list(resource):
for identifier in resource.identifiers:
if identifier.path and '[]' in identifier.path:
return True
return False

View File

@@ -0,0 +1,364 @@
# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
# https://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.
import os
from botocore import xform_name
from botocore.docs.bcdoc.restdoc import DocumentStructure
from botocore.docs.utils import get_official_service_name
from boto3.docs.action import ActionDocumenter
from boto3.docs.attr import (
document_attribute,
document_identifier,
document_reference,
)
from boto3.docs.base import BaseDocumenter
from boto3.docs.collection import CollectionDocumenter
from boto3.docs.subresource import SubResourceDocumenter
from boto3.docs.utils import (
add_resource_type_overview,
get_identifier_args_for_signature,
get_identifier_description,
get_identifier_values_for_example,
)
from boto3.docs.waiter import WaiterResourceDocumenter
class ResourceDocumenter(BaseDocumenter):
def __init__(self, resource, botocore_session, root_docs_path):
super().__init__(resource)
self._botocore_session = botocore_session
self._root_docs_path = root_docs_path
self._resource_sub_path = self._resource_name.lower()
if self._resource_name == self._service_name:
self._resource_sub_path = 'service-resource'
def document_resource(self, section):
self._add_title(section)
self._add_resource_note(section)
self._add_intro(section)
self._add_identifiers(section)
self._add_attributes(section)
self._add_references(section)
self._add_actions(section)
self._add_sub_resources(section)
self._add_collections(section)
self._add_waiters(section)
def _add_title(self, section):
title_section = section.add_new_section('title')
title_section.style.h2(self._resource_name)
def _add_intro(self, section):
identifier_names = []
if self._resource_model.identifiers:
for identifier in self._resource_model.identifiers:
identifier_names.append(identifier.name)
# Write out the class signature.
class_args = get_identifier_args_for_signature(identifier_names)
start_class = section.add_new_section('start_class')
start_class.style.start_sphinx_py_class(
class_name=f'{self.class_name}({class_args})'
)
# Add as short description about the resource
description_section = start_class.add_new_section('description')
self._add_description(description_section)
# Add an example of how to instantiate the resource
example_section = start_class.add_new_section('example')
self._add_example(example_section, identifier_names)
# Add the description for the parameters to instantiate the
# resource.
param_section = start_class.add_new_section('params')
self._add_params_description(param_section, identifier_names)
end_class = section.add_new_section('end_class')
end_class.style.end_sphinx_py_class()
def _add_description(self, section):
official_service_name = get_official_service_name(self._service_model)
section.write(
'A resource representing an {} {}'.format(
official_service_name, self._resource_name
)
)
def _add_example(self, section, identifier_names):
section.style.start_codeblock()
section.style.new_line()
section.write('import boto3')
section.style.new_line()
section.style.new_line()
section.write(
'{} = boto3.resource(\'{}\')'.format(
self._service_name, self._service_name
)
)
section.style.new_line()
example_values = get_identifier_values_for_example(identifier_names)
section.write(
'{} = {}.{}({})'.format(
xform_name(self._resource_name),
self._service_name,
self._resource_name,
example_values,
)
)
section.style.end_codeblock()
def _add_params_description(self, section, identifier_names):
for identifier_name in identifier_names:
description = get_identifier_description(
self._resource_name, identifier_name
)
section.write(f':type {identifier_name}: string')
section.style.new_line()
section.write(f':param {identifier_name}: {description}')
section.style.new_line()
def _add_overview_of_member_type(self, section, resource_member_type):
section.style.new_line()
section.write(
f'These are the resource\'s available {resource_member_type}:'
)
section.style.new_line()
section.style.toctree()
for member in self.member_map[resource_member_type]:
section.style.tocitem(f'{member}')
def _add_identifiers(self, section):
identifiers = self._resource.meta.resource_model.identifiers
section = section.add_new_section('identifiers')
member_list = []
if identifiers:
self.member_map['identifiers'] = member_list
add_resource_type_overview(
section=section,
resource_type='Identifiers',
description=(
'Identifiers are properties of a resource that are '
'set upon instantiation of the resource.'
),
intro_link='identifiers_attributes_intro',
)
for identifier in identifiers:
member_list.append(identifier.name)
# Create a new DocumentStructure for each identifier and add contents.
identifier_doc = DocumentStructure(identifier.name, target='html')
breadcrumb_section = identifier_doc.add_new_section('breadcrumb')
breadcrumb_section.style.ref(self._resource_class_name, 'index')
breadcrumb_section.write(f' / Identifier / {identifier.name}')
identifier_doc.add_title_section(identifier.name)
identifier_section = identifier_doc.add_new_section(
identifier.name,
context={'qualifier': f'{self.class_name}.'},
)
document_identifier(
section=identifier_section,
resource_name=self._resource_name,
identifier_model=identifier,
)
# Write identifiers in individual/nested files.
# Path: <root>/reference/services/<service>/<resource_name>/<identifier_name>.rst
identifiers_dir_path = os.path.join(
self._root_docs_path,
f'{self._service_name}',
f'{self._resource_sub_path}',
)
identifier_doc.write_to_file(identifiers_dir_path, identifier.name)
if identifiers:
self._add_overview_of_member_type(section, 'identifiers')
def _add_attributes(self, section):
service_model = self._resource.meta.client.meta.service_model
attributes = {}
if self._resource.meta.resource_model.shape:
shape = service_model.shape_for(
self._resource.meta.resource_model.shape
)
attributes = self._resource.meta.resource_model.get_attributes(
shape
)
section = section.add_new_section('attributes')
attribute_list = []
if attributes:
add_resource_type_overview(
section=section,
resource_type='Attributes',
description=(
'Attributes provide access'
' to the properties of a resource. Attributes are lazy-'
'loaded the first time one is accessed via the'
' :py:meth:`load` method.'
),
intro_link='identifiers_attributes_intro',
)
self.member_map['attributes'] = attribute_list
for attr_name in sorted(attributes):
_, attr_shape = attributes[attr_name]
attribute_list.append(attr_name)
# Create a new DocumentStructure for each attribute and add contents.
attribute_doc = DocumentStructure(attr_name, target='html')
breadcrumb_section = attribute_doc.add_new_section('breadcrumb')
breadcrumb_section.style.ref(self._resource_class_name, 'index')
breadcrumb_section.write(f' / Attribute / {attr_name}')
attribute_doc.add_title_section(attr_name)
attribute_section = attribute_doc.add_new_section(
attr_name,
context={'qualifier': f'{self.class_name}.'},
)
document_attribute(
section=attribute_section,
service_name=self._service_name,
resource_name=self._resource_name,
attr_name=attr_name,
event_emitter=self._resource.meta.client.meta.events,
attr_model=attr_shape,
)
# Write attributes in individual/nested files.
# Path: <root>/reference/services/<service>/<resource_name>/<attribute_name>.rst
attributes_dir_path = os.path.join(
self._root_docs_path,
f'{self._service_name}',
f'{self._resource_sub_path}',
)
attribute_doc.write_to_file(attributes_dir_path, attr_name)
if attributes:
self._add_overview_of_member_type(section, 'attributes')
def _add_references(self, section):
section = section.add_new_section('references')
references = self._resource.meta.resource_model.references
reference_list = []
if references:
add_resource_type_overview(
section=section,
resource_type='References',
description=(
'References are related resource instances that have '
'a belongs-to relationship.'
),
intro_link='references_intro',
)
self.member_map['references'] = reference_list
self._add_overview_of_member_type(section, 'references')
for reference in references:
reference_list.append(reference.name)
# Create a new DocumentStructure for each reference and add contents.
reference_doc = DocumentStructure(reference.name, target='html')
breadcrumb_section = reference_doc.add_new_section('breadcrumb')
breadcrumb_section.style.ref(self._resource_class_name, 'index')
breadcrumb_section.write(f' / Reference / {reference.name}')
reference_doc.add_title_section(reference.name)
reference_section = reference_doc.add_new_section(
reference.name,
context={'qualifier': f'{self.class_name}.'},
)
document_reference(
section=reference_section,
reference_model=reference,
)
# Write references in individual/nested files.
# Path: <root>/reference/services/<service>/<resource_name>/<reference_name>.rst
references_dir_path = os.path.join(
self._root_docs_path,
f'{self._service_name}',
f'{self._resource_sub_path}',
)
reference_doc.write_to_file(references_dir_path, reference.name)
if references:
self._add_overview_of_member_type(section, 'references')
def _add_actions(self, section):
section = section.add_new_section('actions')
actions = self._resource.meta.resource_model.actions
if actions:
documenter = ActionDocumenter(self._resource, self._root_docs_path)
documenter.member_map = self.member_map
documenter.document_actions(section)
self._add_overview_of_member_type(section, 'actions')
def _add_sub_resources(self, section):
section = section.add_new_section('sub-resources')
sub_resources = self._resource.meta.resource_model.subresources
if sub_resources:
documenter = SubResourceDocumenter(
self._resource, self._root_docs_path
)
documenter.member_map = self.member_map
documenter.document_sub_resources(section)
self._add_overview_of_member_type(section, 'sub-resources')
def _add_collections(self, section):
section = section.add_new_section('collections')
collections = self._resource.meta.resource_model.collections
if collections:
documenter = CollectionDocumenter(
self._resource, self._root_docs_path
)
documenter.member_map = self.member_map
documenter.document_collections(section)
self._add_overview_of_member_type(section, 'collections')
def _add_waiters(self, section):
section = section.add_new_section('waiters')
waiters = self._resource.meta.resource_model.waiters
if waiters:
service_waiter_model = self._botocore_session.get_waiter_model(
self._service_name
)
documenter = WaiterResourceDocumenter(
self._resource, service_waiter_model, self._root_docs_path
)
documenter.member_map = self.member_map
documenter.document_resource_waiters(section)
self._add_overview_of_member_type(section, 'waiters')
def _add_resource_note(self, section):
section = section.add_new_section('feature-freeze')
section.style.start_note()
section.write(
"Before using anything on this page, please refer to the resources "
":doc:`user guide <../../../../guide/resources>` for the most recent "
"guidance on using resources."
)
section.style.end_note()
class ServiceResourceDocumenter(ResourceDocumenter):
@property
def class_name(self):
return f'{self._service_docs_name}.ServiceResource'
def _add_title(self, section):
title_section = section.add_new_section('title')
title_section.style.h2('Service Resource')
def _add_description(self, section):
official_service_name = get_official_service_name(self._service_model)
section.write(f'A resource representing {official_service_name}')
def _add_example(self, section, identifier_names):
section.style.start_codeblock()
section.style.new_line()
section.write('import boto3')
section.style.new_line()
section.style.new_line()
section.write(
f'{self._service_name} = boto3.resource(\'{self._service_name}\')'
)
section.style.end_codeblock()

View File

@@ -0,0 +1,202 @@
# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
# https://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.
import os
from botocore.docs.bcdoc.restdoc import DocumentStructure
from botocore.docs.service import ServiceDocumenter as BaseServiceDocumenter
from botocore.exceptions import DataNotFoundError
import boto3
from boto3.docs.client import Boto3ClientDocumenter
from boto3.docs.resource import ResourceDocumenter, ServiceResourceDocumenter
from boto3.utils import ServiceContext
class ServiceDocumenter(BaseServiceDocumenter):
# The path used to find examples
EXAMPLE_PATH = os.path.join(os.path.dirname(boto3.__file__), 'examples')
def __init__(self, service_name, session, root_docs_path):
super().__init__(
service_name=service_name,
# I know that this is an internal attribute, but the botocore session
# is needed to load the paginator and waiter models.
session=session._session,
root_docs_path=root_docs_path,
)
self._boto3_session = session
self._client = self._boto3_session.client(service_name)
self._service_resource = None
if self._service_name in self._boto3_session.get_available_resources():
self._service_resource = self._boto3_session.resource(service_name)
self.sections = [
'title',
'client',
'paginators',
'waiters',
'resources',
'examples',
'context-params',
]
self._root_docs_path = root_docs_path
self._USER_GUIDE_LINK = (
'https://boto3.amazonaws.com/'
'v1/documentation/api/latest/guide/resources.html'
)
def document_service(self):
"""Documents an entire service.
:returns: The reStructured text of the documented service.
"""
doc_structure = DocumentStructure(
self._service_name, section_names=self.sections, target='html'
)
self.title(doc_structure.get_section('title'))
self.client_api(doc_structure.get_section('client'))
self.paginator_api(doc_structure.get_section('paginators'))
self.waiter_api(doc_structure.get_section('waiters'))
if self._service_resource:
self.resource_section(doc_structure.get_section('resources'))
self._document_examples(doc_structure.get_section('examples'))
context_params_section = doc_structure.get_section('context-params')
self.client_context_params(context_params_section)
return doc_structure.flush_structure()
def client_api(self, section):
examples = None
try:
examples = self.get_examples(self._service_name)
except DataNotFoundError:
pass
Boto3ClientDocumenter(
self._client, self._root_docs_path, examples
).document_client(section)
def resource_section(self, section):
section.style.h2('Resources')
section.style.new_line()
section.write(
'Resources are available in boto3 via the '
'``resource`` method. For more detailed instructions '
'and examples on the usage of resources, see the '
'resources '
)
section.style.external_link(
title='user guide',
link=self._USER_GUIDE_LINK,
)
section.write('.')
section.style.new_line()
section.style.new_line()
section.write('The available resources are:')
section.style.new_line()
section.style.toctree()
self._document_service_resource(section)
self._document_resources(section)
def _document_service_resource(self, section):
# Create a new DocumentStructure for each Service Resource and add contents.
service_resource_doc = DocumentStructure(
'service-resource', target='html'
)
breadcrumb_section = service_resource_doc.add_new_section('breadcrumb')
breadcrumb_section.style.ref(
self._client.__class__.__name__, f'../../{self._service_name}'
)
breadcrumb_section.write(' / Resource / ServiceResource')
ServiceResourceDocumenter(
self._service_resource, self._session, self._root_docs_path
).document_resource(service_resource_doc)
# Write collections in individual/nested files.
# Path: <root>/reference/services/<service>/<resource_name>/<collection_name>.rst
resource_name = self._service_resource.meta.resource_model.name
if resource_name == self._service_name:
resource_name = 'service-resource'
service_resource_dir_path = os.path.join(
self._root_docs_path,
f'{self._service_name}',
f'{resource_name.lower()}',
)
service_resource_doc.write_to_file(service_resource_dir_path, 'index')
section.style.tocitem(f'{self._service_name}/{resource_name}/index')
def _document_resources(self, section):
temp_identifier_value = 'foo'
loader = self._session.get_component('data_loader')
json_resource_model = loader.load_service_model(
self._service_name, 'resources-1'
)
service_model = self._service_resource.meta.client.meta.service_model
for resource_name in json_resource_model['resources']:
resource_model = json_resource_model['resources'][resource_name]
resource_cls = (
self._boto3_session.resource_factory.load_from_definition(
resource_name=resource_name,
single_resource_json_definition=resource_model,
service_context=ServiceContext(
service_name=self._service_name,
resource_json_definitions=json_resource_model[
'resources'
],
service_model=service_model,
service_waiter_model=None,
),
)
)
identifiers = resource_cls.meta.resource_model.identifiers
args = []
for _ in identifiers:
args.append(temp_identifier_value)
resource = resource_cls(*args, client=self._client)
# Create a new DocumentStructure for each Resource and add contents.
resource_name = resource.meta.resource_model.name.lower()
resource_doc = DocumentStructure(resource_name, target='html')
breadcrumb_section = resource_doc.add_new_section('breadcrumb')
breadcrumb_section.style.ref(
self._client.__class__.__name__, f'../../{self._service_name}'
)
breadcrumb_section.write(
f' / Resource / {resource.meta.resource_model.name}'
)
ResourceDocumenter(
resource, self._session, self._root_docs_path
).document_resource(
resource_doc.add_new_section(resource.meta.resource_model.name)
)
# Write collections in individual/nested files.
# Path: <root>/reference/services/<service>/<resource_name>/<index>.rst
service_resource_dir_path = os.path.join(
self._root_docs_path,
f'{self._service_name}',
f'{resource_name}',
)
resource_doc.write_to_file(service_resource_dir_path, 'index')
section.style.tocitem(
f'{self._service_name}/{resource_name}/index'
)
def _get_example_file(self):
return os.path.realpath(
os.path.join(self.EXAMPLE_PATH, self._service_name + '.rst')
)
def _document_examples(self, section):
examples_file = self._get_example_file()
if os.path.isfile(examples_file):
section.style.h2('Examples')
section.style.new_line()
with open(examples_file) as f:
section.write(f.read())

View File

@@ -0,0 +1,153 @@
# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
# https://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.
import os
from botocore import xform_name
from botocore.docs.bcdoc.restdoc import DocumentStructure
from botocore.utils import get_service_module_name
from boto3.docs.base import NestedDocumenter
from boto3.docs.utils import (
add_resource_type_overview,
get_identifier_args_for_signature,
get_identifier_description,
get_identifier_values_for_example,
)
class SubResourceDocumenter(NestedDocumenter):
def document_sub_resources(self, section):
add_resource_type_overview(
section=section,
resource_type='Sub-resources',
description=(
'Sub-resources are methods that create a new instance of a'
' child resource. This resource\'s identifiers get passed'
' along to the child.'
),
intro_link='subresources_intro',
)
sub_resources = sorted(
self._resource.meta.resource_model.subresources,
key=lambda sub_resource: sub_resource.name,
)
sub_resources_list = []
self.member_map['sub-resources'] = sub_resources_list
for sub_resource in sub_resources:
sub_resources_list.append(sub_resource.name)
# Create a new DocumentStructure for each sub_resource and add contents.
sub_resource_doc = DocumentStructure(
sub_resource.name, target='html'
)
breadcrumb_section = sub_resource_doc.add_new_section('breadcrumb')
breadcrumb_section.style.ref(self._resource_class_name, 'index')
breadcrumb_section.write(f' / Sub-Resource / {sub_resource.name}')
sub_resource_doc.add_title_section(sub_resource.name)
sub_resource_section = sub_resource_doc.add_new_section(
sub_resource.name,
context={'qualifier': f'{self.class_name}.'},
)
document_sub_resource(
section=sub_resource_section,
resource_name=self._resource_name,
sub_resource_model=sub_resource,
service_model=self._service_model,
)
# Write sub_resources in individual/nested files.
# Path: <root>/reference/services/<service>/<resource_name>/<sub_resource_name>.rst
sub_resources_dir_path = os.path.join(
self._root_docs_path,
f'{self._service_name}',
f'{self._resource_sub_path}',
)
sub_resource_doc.write_to_file(
sub_resources_dir_path, sub_resource.name
)
def document_sub_resource(
section,
resource_name,
sub_resource_model,
service_model,
include_signature=True,
):
"""Documents a resource action
:param section: The section to write to
:param resource_name: The name of the resource
:param sub_resource_model: The model of the subresource
:param service_model: The model of the service
:param include_signature: Whether or not to include the signature.
It is useful for generating docstrings.
"""
identifiers_needed = []
for identifier in sub_resource_model.resource.identifiers:
if identifier.source == 'input':
identifiers_needed.append(xform_name(identifier.target))
if include_signature:
signature_args = get_identifier_args_for_signature(identifiers_needed)
full_sub_resource_name = (
f"{section.context.get('qualifier', '')}{sub_resource_model.name}"
)
section.style.start_sphinx_py_method(
full_sub_resource_name, signature_args
)
method_intro_section = section.add_new_section('method-intro')
description = f'Creates a {sub_resource_model.resource.type} resource.'
method_intro_section.include_doc_string(description)
example_section = section.add_new_section('example')
example_values = get_identifier_values_for_example(identifiers_needed)
example_resource_name = xform_name(resource_name)
if service_model.service_name == resource_name:
example_resource_name = resource_name
example = '{} = {}.{}({})'.format(
xform_name(sub_resource_model.resource.type),
example_resource_name,
sub_resource_model.name,
example_values,
)
example_section.style.start_codeblock()
example_section.write(example)
example_section.style.end_codeblock()
param_section = section.add_new_section('params')
for identifier in identifiers_needed:
description = get_identifier_description(
sub_resource_model.name, identifier
)
param_section.write(f':type {identifier}: string')
param_section.style.new_line()
param_section.write(f':param {identifier}: {description}')
param_section.style.new_line()
return_section = section.add_new_section('return')
return_section.style.new_line()
return_section.write(
':rtype: :py:class:`{}.{}`'.format(
get_service_module_name(service_model),
sub_resource_model.resource.type,
)
)
return_section.style.new_line()
return_section.write(
f':returns: A {sub_resource_model.resource.type} resource'
)
return_section.style.new_line()

View File

@@ -0,0 +1,146 @@
# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
# https://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.
import inspect
import jmespath
def get_resource_ignore_params(params):
"""Helper method to determine which parameters to ignore for actions
:returns: A list of the parameter names that does not need to be
included in a resource's method call for documentation purposes.
"""
ignore_params = []
for param in params:
result = jmespath.compile(param.target)
current = result.parsed
# Use JMESPath to find the left most element in the target expression
# which will be the parameter to ignore in the action call.
while current['children']:
current = current['children'][0]
# Make sure the parameter we are about to ignore is a field.
# If it is not, we should ignore the result to avoid false positives.
if current['type'] == 'field':
ignore_params.append(current['value'])
return ignore_params
def is_resource_action(action_handle):
return inspect.isfunction(action_handle)
def get_resource_public_actions(resource_class):
resource_class_members = inspect.getmembers(resource_class)
resource_methods = {}
for name, member in resource_class_members:
if not name.startswith('_'):
if not name[0].isupper():
if not name.startswith('wait_until'):
if is_resource_action(member):
resource_methods[name] = member
return resource_methods
def get_identifier_values_for_example(identifier_names):
return ','.join([f'\'{identifier}\'' for identifier in identifier_names])
def get_identifier_args_for_signature(identifier_names):
return ','.join(identifier_names)
def get_identifier_description(resource_name, identifier_name):
return (
f"The {resource_name}'s {identifier_name} identifier. "
f"This **must** be set."
)
def add_resource_type_overview(
section, resource_type, description, intro_link=None
):
section.style.new_line()
section.style.h3(resource_type)
section.style.new_line()
section.style.new_line()
section.write(description)
section.style.new_line()
if intro_link is not None:
section.write(
f'For more information about {resource_type.lower()} refer to the '
f':ref:`Resources Introduction Guide<{intro_link}>`.'
)
section.style.new_line()
class DocumentModifiedShape:
def __init__(
self, shape_name, new_type, new_description, new_example_value
):
self._shape_name = shape_name
self._new_type = new_type
self._new_description = new_description
self._new_example_value = new_example_value
def replace_documentation_for_matching_shape(
self, event_name, section, **kwargs
):
if self._shape_name == section.context.get('shape'):
self._replace_documentation(event_name, section)
for section_name in section.available_sections:
sub_section = section.get_section(section_name)
if self._shape_name == sub_section.context.get('shape'):
self._replace_documentation(event_name, sub_section)
else:
self.replace_documentation_for_matching_shape(
event_name, sub_section
)
def _replace_documentation(self, event_name, section):
if event_name.startswith(
'docs.request-example'
) or event_name.startswith('docs.response-example'):
section.remove_all_sections()
section.clear_text()
section.write(self._new_example_value)
if event_name.startswith(
'docs.request-params'
) or event_name.startswith('docs.response-params'):
allowed_sections = (
'param-name',
'param-documentation',
'end-structure',
'param-type',
'end-param',
)
for section_name in section.available_sections:
# Delete any extra members as a new shape is being
# used.
if section_name not in allowed_sections:
section.delete_section(section_name)
# Update the documentation
description_section = section.get_section('param-documentation')
description_section.clear_text()
description_section.write(self._new_description)
# Update the param type
type_section = section.get_section('param-type')
if type_section.getvalue().decode('utf-8').startswith(':type'):
type_section.clear_text()
type_section.write(f':type {section.name}: {self._new_type}')
else:
type_section.clear_text()
type_section.style.italics(f'({self._new_type}) -- ')

View File

@@ -0,0 +1,130 @@
# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
# https://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.
import os
from botocore import xform_name
from botocore.docs.bcdoc.restdoc import DocumentStructure
from botocore.docs.method import document_model_driven_method
from botocore.utils import get_service_module_name
from boto3.docs.base import NestedDocumenter
from boto3.docs.utils import (
add_resource_type_overview,
get_resource_ignore_params,
)
class WaiterResourceDocumenter(NestedDocumenter):
def __init__(self, resource, service_waiter_model, root_docs_path):
super().__init__(resource, root_docs_path)
self._service_waiter_model = service_waiter_model
def document_resource_waiters(self, section):
waiters = self._resource.meta.resource_model.waiters
add_resource_type_overview(
section=section,
resource_type='Waiters',
description=(
'Waiters provide an interface to wait for a resource'
' to reach a specific state.'
),
intro_link='waiters_intro',
)
waiter_list = []
self.member_map['waiters'] = waiter_list
for waiter in waiters:
waiter_list.append(waiter.name)
# Create a new DocumentStructure for each waiter and add contents.
waiter_doc = DocumentStructure(waiter.name, target='html')
breadcrumb_section = waiter_doc.add_new_section('breadcrumb')
breadcrumb_section.style.ref(self._resource_class_name, 'index')
breadcrumb_section.write(f' / Waiter / {waiter.name}')
waiter_doc.add_title_section(waiter.name)
waiter_section = waiter_doc.add_new_section(
waiter.name,
context={'qualifier': f'{self.class_name}.'},
)
document_resource_waiter(
section=waiter_section,
resource_name=self._resource_name,
event_emitter=self._resource.meta.client.meta.events,
service_model=self._service_model,
resource_waiter_model=waiter,
service_waiter_model=self._service_waiter_model,
)
# Write waiters in individual/nested files.
# Path: <root>/reference/services/<service>/<resource_name>/<waiter_name>.rst
waiters_dir_path = os.path.join(
self._root_docs_path,
f'{self._service_name}',
f'{self._resource_sub_path}',
)
waiter_doc.write_to_file(waiters_dir_path, waiter.name)
def document_resource_waiter(
section,
resource_name,
event_emitter,
service_model,
resource_waiter_model,
service_waiter_model,
include_signature=True,
):
waiter_model = service_waiter_model.get_waiter(
resource_waiter_model.waiter_name
)
operation_model = service_model.operation_model(waiter_model.operation)
ignore_params = get_resource_ignore_params(resource_waiter_model.params)
service_module_name = get_service_module_name(service_model)
description = (
'Waits until this {} is {}. This method calls '
':py:meth:`{}.Waiter.{}.wait` which polls '
':py:meth:`{}.Client.{}` every {} seconds until '
'a successful state is reached. An error is returned '
'after {} failed checks.'.format(
resource_name,
' '.join(resource_waiter_model.name.split('_')[2:]),
service_module_name,
xform_name(resource_waiter_model.waiter_name),
service_module_name,
xform_name(waiter_model.operation),
waiter_model.delay,
waiter_model.max_attempts,
)
)
example_prefix = '{}.{}'.format(
xform_name(resource_name), resource_waiter_model.name
)
full_waiter_name = (
f"{section.context.get('qualifier', '')}{resource_waiter_model.name}"
)
document_model_driven_method(
section=section,
method_name=full_waiter_name,
operation_model=operation_model,
event_emitter=event_emitter,
example_prefix=example_prefix,
method_description=description,
exclude_input=ignore_params,
include_signature=include_signature,
)
if 'return' in section.available_sections:
# Waiters do not return anything so we should remove
# any sections that may document the underlying return
# value of the client method.
return_section = section.get_section('return')
return_section.clear_text()
return_section.remove_all_sections()
return_section.write(':returns: None')

View File

@@ -0,0 +1,12 @@
# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
# https://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.

View File

@@ -0,0 +1,461 @@
# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
# https://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.
import re
from collections import namedtuple
from boto3.exceptions import (
DynamoDBNeedsConditionError,
DynamoDBNeedsKeyConditionError,
DynamoDBOperationNotSupportedError,
)
ATTR_NAME_REGEX = re.compile(r'[^.\[\]]+(?![^\[]*\])')
class ConditionBase:
expression_format = ''
expression_operator = ''
has_grouped_values = False
def __init__(self, *values):
self._values = values
def __and__(self, other):
if not isinstance(other, ConditionBase):
raise DynamoDBOperationNotSupportedError('AND', other)
return And(self, other)
def __or__(self, other):
if not isinstance(other, ConditionBase):
raise DynamoDBOperationNotSupportedError('OR', other)
return Or(self, other)
def __invert__(self):
return Not(self)
def get_expression(self):
return {
'format': self.expression_format,
'operator': self.expression_operator,
'values': self._values,
}
def __eq__(self, other):
if isinstance(other, type(self)):
if self._values == other._values:
return True
return False
def __ne__(self, other):
return not self.__eq__(other)
class AttributeBase:
def __init__(self, name):
self.name = name
def __and__(self, value):
raise DynamoDBOperationNotSupportedError('AND', self)
def __or__(self, value):
raise DynamoDBOperationNotSupportedError('OR', self)
def __invert__(self):
raise DynamoDBOperationNotSupportedError('NOT', self)
def eq(self, value):
"""Creates a condition where the attribute is equal to the value.
:param value: The value that the attribute is equal to.
"""
return Equals(self, value)
def lt(self, value):
"""Creates a condition where the attribute is less than the value.
:param value: The value that the attribute is less than.
"""
return LessThan(self, value)
def lte(self, value):
"""Creates a condition where the attribute is less than or equal to the
value.
:param value: The value that the attribute is less than or equal to.
"""
return LessThanEquals(self, value)
def gt(self, value):
"""Creates a condition where the attribute is greater than the value.
:param value: The value that the attribute is greater than.
"""
return GreaterThan(self, value)
def gte(self, value):
"""Creates a condition where the attribute is greater than or equal to
the value.
:param value: The value that the attribute is greater than or equal to.
"""
return GreaterThanEquals(self, value)
def begins_with(self, value):
"""Creates a condition where the attribute begins with the value.
:param value: The value that the attribute begins with.
"""
return BeginsWith(self, value)
def between(self, low_value, high_value):
"""Creates a condition where the attribute is greater than or equal
to the low value and less than or equal to the high value.
:param low_value: The value that the attribute is greater than or equal to.
:param high_value: The value that the attribute is less than or equal to.
"""
return Between(self, low_value, high_value)
def __eq__(self, other):
return isinstance(other, type(self)) and self.name == other.name
def __ne__(self, other):
return not self.__eq__(other)
class ConditionAttributeBase(ConditionBase, AttributeBase):
"""This base class is for conditions that can have attribute methods.
One example is the Size condition. To complete a condition, you need
to apply another AttributeBase method like eq().
"""
def __init__(self, *values):
ConditionBase.__init__(self, *values)
# This is assuming the first value to the condition is the attribute
# in which can be used to generate its attribute base.
AttributeBase.__init__(self, values[0].name)
def __eq__(self, other):
return ConditionBase.__eq__(self, other) and AttributeBase.__eq__(
self, other
)
def __ne__(self, other):
return not self.__eq__(other)
class ComparisonCondition(ConditionBase):
expression_format = '{0} {operator} {1}'
class Equals(ComparisonCondition):
expression_operator = '='
class NotEquals(ComparisonCondition):
expression_operator = '<>'
class LessThan(ComparisonCondition):
expression_operator = '<'
class LessThanEquals(ComparisonCondition):
expression_operator = '<='
class GreaterThan(ComparisonCondition):
expression_operator = '>'
class GreaterThanEquals(ComparisonCondition):
expression_operator = '>='
class In(ComparisonCondition):
expression_operator = 'IN'
has_grouped_values = True
class Between(ConditionBase):
expression_operator = 'BETWEEN'
expression_format = '{0} {operator} {1} AND {2}'
class BeginsWith(ConditionBase):
expression_operator = 'begins_with'
expression_format = '{operator}({0}, {1})'
class Contains(ConditionBase):
expression_operator = 'contains'
expression_format = '{operator}({0}, {1})'
class Size(ConditionAttributeBase):
expression_operator = 'size'
expression_format = '{operator}({0})'
class AttributeType(ConditionBase):
expression_operator = 'attribute_type'
expression_format = '{operator}({0}, {1})'
class AttributeExists(ConditionBase):
expression_operator = 'attribute_exists'
expression_format = '{operator}({0})'
class AttributeNotExists(ConditionBase):
expression_operator = 'attribute_not_exists'
expression_format = '{operator}({0})'
class And(ConditionBase):
expression_operator = 'AND'
expression_format = '({0} {operator} {1})'
class Or(ConditionBase):
expression_operator = 'OR'
expression_format = '({0} {operator} {1})'
class Not(ConditionBase):
expression_operator = 'NOT'
expression_format = '({operator} {0})'
class Key(AttributeBase):
pass
class Attr(AttributeBase):
"""Represents an DynamoDB item's attribute."""
def ne(self, value):
"""Creates a condition where the attribute is not equal to the value
:param value: The value that the attribute is not equal to.
"""
return NotEquals(self, value)
def is_in(self, value):
"""Creates a condition where the attribute is in the value,
:type value: list
:param value: The value that the attribute is in.
"""
return In(self, value)
def exists(self):
"""Creates a condition where the attribute exists."""
return AttributeExists(self)
def not_exists(self):
"""Creates a condition where the attribute does not exist."""
return AttributeNotExists(self)
def contains(self, value):
"""Creates a condition where the attribute contains the value.
:param value: The value the attribute contains.
"""
return Contains(self, value)
def size(self):
"""Creates a condition for the attribute size.
Note another AttributeBase method must be called on the returned
size condition to be a valid DynamoDB condition.
"""
return Size(self)
def attribute_type(self, value):
"""Creates a condition for the attribute type.
:param value: The type of the attribute.
"""
return AttributeType(self, value)
BuiltConditionExpression = namedtuple(
'BuiltConditionExpression',
[
'condition_expression',
'attribute_name_placeholders',
'attribute_value_placeholders',
],
)
class ConditionExpressionBuilder:
"""This class is used to build condition expressions with placeholders"""
def __init__(self):
self._name_count = 0
self._value_count = 0
self._name_placeholder = 'n'
self._value_placeholder = 'v'
def _get_name_placeholder(self):
return '#' + self._name_placeholder + str(self._name_count)
def _get_value_placeholder(self):
return ':' + self._value_placeholder + str(self._value_count)
def reset(self):
"""Resets the placeholder name and values"""
self._name_count = 0
self._value_count = 0
def build_expression(self, condition, is_key_condition=False):
"""Builds the condition expression and the dictionary of placeholders.
:type condition: ConditionBase
:param condition: A condition to be built into a condition expression
string with any necessary placeholders.
:type is_key_condition: Boolean
:param is_key_condition: True if the expression is for a
KeyConditionExpression. False otherwise.
:rtype: (string, dict, dict)
:returns: Will return a string representing the condition with
placeholders inserted where necessary, a dictionary of
placeholders for attribute names, and a dictionary of
placeholders for attribute values. Here is a sample return value:
('#n0 = :v0', {'#n0': 'myattribute'}, {':v1': 'myvalue'})
"""
if not isinstance(condition, ConditionBase):
raise DynamoDBNeedsConditionError(condition)
attribute_name_placeholders = {}
attribute_value_placeholders = {}
condition_expression = self._build_expression(
condition,
attribute_name_placeholders,
attribute_value_placeholders,
is_key_condition=is_key_condition,
)
return BuiltConditionExpression(
condition_expression=condition_expression,
attribute_name_placeholders=attribute_name_placeholders,
attribute_value_placeholders=attribute_value_placeholders,
)
def _build_expression(
self,
condition,
attribute_name_placeholders,
attribute_value_placeholders,
is_key_condition,
):
expression_dict = condition.get_expression()
replaced_values = []
for value in expression_dict['values']:
# Build the necessary placeholders for that value.
# Placeholders are built for both attribute names and values.
replaced_value = self._build_expression_component(
value,
attribute_name_placeholders,
attribute_value_placeholders,
condition.has_grouped_values,
is_key_condition,
)
replaced_values.append(replaced_value)
# Fill out the expression using the operator and the
# values that have been replaced with placeholders.
return expression_dict['format'].format(
*replaced_values, operator=expression_dict['operator']
)
def _build_expression_component(
self,
value,
attribute_name_placeholders,
attribute_value_placeholders,
has_grouped_values,
is_key_condition,
):
# Continue to recurse if the value is a ConditionBase in order
# to extract out all parts of the expression.
if isinstance(value, ConditionBase):
return self._build_expression(
value,
attribute_name_placeholders,
attribute_value_placeholders,
is_key_condition,
)
# If it is not a ConditionBase, we can recurse no further.
# So we check if it is an attribute and add placeholders for
# its name
elif isinstance(value, AttributeBase):
if is_key_condition and not isinstance(value, Key):
raise DynamoDBNeedsKeyConditionError(
f'Attribute object {value.name} is of type {type(value)}. '
f'KeyConditionExpression only supports Attribute objects '
f'of type Key'
)
return self._build_name_placeholder(
value, attribute_name_placeholders
)
# If it is anything else, we treat it as a value and thus placeholders
# are needed for the value.
else:
return self._build_value_placeholder(
value, attribute_value_placeholders, has_grouped_values
)
def _build_name_placeholder(self, value, attribute_name_placeholders):
attribute_name = value.name
# Figure out which parts of the attribute name that needs replacement.
attribute_name_parts = ATTR_NAME_REGEX.findall(attribute_name)
# Add a temporary placeholder for each of these parts.
placeholder_format = ATTR_NAME_REGEX.sub('%s', attribute_name)
str_format_args = []
for part in attribute_name_parts:
name_placeholder = self._get_name_placeholder()
self._name_count += 1
str_format_args.append(name_placeholder)
# Add the placeholder and value to dictionary of name placeholders.
attribute_name_placeholders[name_placeholder] = part
# Replace the temporary placeholders with the designated placeholders.
return placeholder_format % tuple(str_format_args)
def _build_value_placeholder(
self, value, attribute_value_placeholders, has_grouped_values=False
):
# If the values are grouped, we need to add a placeholder for
# each element inside of the actual value.
if has_grouped_values:
placeholder_list = []
for v in value:
value_placeholder = self._get_value_placeholder()
self._value_count += 1
placeholder_list.append(value_placeholder)
attribute_value_placeholders[value_placeholder] = v
# Assuming the values are grouped by parenthesis.
# IN is the currently the only one that uses this so it maybe
# needed to be changed in future.
return '(' + ', '.join(placeholder_list) + ')'
# Otherwise, treat the value as a single value that needs only
# one placeholder.
else:
value_placeholder = self._get_value_placeholder()
self._value_count += 1
attribute_value_placeholders[value_placeholder] = value
return value_placeholder

View File

@@ -0,0 +1,167 @@
# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
# https://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.
import logging
logger = logging.getLogger(__name__)
def register_table_methods(base_classes, **kwargs):
base_classes.insert(0, TableResource)
# This class can be used to add any additional methods we want
# onto a table resource. Ideally to avoid creating a new
# base class for every method we can just update this
# class instead. Just be sure to move the bulk of the
# actual method implementation to another class.
class TableResource:
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def batch_writer(self, overwrite_by_pkeys=None):
"""Create a batch writer object.
This method creates a context manager for writing
objects to Amazon DynamoDB in batch.
The batch writer will automatically handle buffering and sending items
in batches. In addition, the batch writer will also automatically
handle any unprocessed items and resend them as needed. All you need
to do is call ``put_item`` for any items you want to add, and
``delete_item`` for any items you want to delete.
Example usage::
with table.batch_writer() as batch:
for _ in range(1000000):
batch.put_item(Item={'HashKey': '...',
'Otherstuff': '...'})
# You can also delete_items in a batch.
batch.delete_item(Key={'HashKey': 'SomeHashKey'})
:type overwrite_by_pkeys: list(string)
:param overwrite_by_pkeys: De-duplicate request items in buffer
if match new request item on specified primary keys. i.e
``["partition_key1", "sort_key2", "sort_key3"]``
"""
return BatchWriter(
self.name, self.meta.client, overwrite_by_pkeys=overwrite_by_pkeys
)
class BatchWriter:
"""Automatically handle batch writes to DynamoDB for a single table."""
def __init__(
self, table_name, client, flush_amount=25, overwrite_by_pkeys=None
):
"""
:type table_name: str
:param table_name: The name of the table. The class handles
batch writes to a single table.
:type client: ``botocore.client.Client``
:param client: A botocore client. Note this client
**must** have the dynamodb customizations applied
to it for transforming AttributeValues into the
wire protocol. What this means in practice is that
you need to use a client that comes from a DynamoDB
resource if you're going to instantiate this class
directly, i.e
``boto3.resource('dynamodb').Table('foo').meta.client``.
:type flush_amount: int
:param flush_amount: The number of items to keep in
a local buffer before sending a batch_write_item
request to DynamoDB.
:type overwrite_by_pkeys: list(string)
:param overwrite_by_pkeys: De-duplicate request items in buffer
if match new request item on specified primary keys. i.e
``["partition_key1", "sort_key2", "sort_key3"]``
"""
self._table_name = table_name
self._client = client
self._items_buffer = []
self._flush_amount = flush_amount
self._overwrite_by_pkeys = overwrite_by_pkeys
def put_item(self, Item):
self._add_request_and_process({'PutRequest': {'Item': Item}})
def delete_item(self, Key):
self._add_request_and_process({'DeleteRequest': {'Key': Key}})
def _add_request_and_process(self, request):
if self._overwrite_by_pkeys:
self._remove_dup_pkeys_request_if_any(request)
self._items_buffer.append(request)
self._flush_if_needed()
def _remove_dup_pkeys_request_if_any(self, request):
pkey_values_new = self._extract_pkey_values(request)
for item in self._items_buffer:
if self._extract_pkey_values(item) == pkey_values_new:
self._items_buffer.remove(item)
logger.debug(
"With overwrite_by_pkeys enabled, skipping " "request:%s",
item,
)
def _extract_pkey_values(self, request):
if request.get('PutRequest'):
return [
request['PutRequest']['Item'][key]
for key in self._overwrite_by_pkeys
]
elif request.get('DeleteRequest'):
return [
request['DeleteRequest']['Key'][key]
for key in self._overwrite_by_pkeys
]
return None
def _flush_if_needed(self):
if len(self._items_buffer) >= self._flush_amount:
self._flush()
def _flush(self):
items_to_send = self._items_buffer[: self._flush_amount]
self._items_buffer = self._items_buffer[self._flush_amount :]
response = self._client.batch_write_item(
RequestItems={self._table_name: items_to_send}
)
unprocessed_items = response['UnprocessedItems']
if not unprocessed_items:
unprocessed_items = {}
item_list = unprocessed_items.get(self._table_name, [])
# Any unprocessed_items are immediately added to the
# next batch we send.
self._items_buffer.extend(item_list)
logger.debug(
"Batch write sent %s, unprocessed: %s",
len(items_to_send),
len(self._items_buffer),
)
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, tb):
# When we exit, we need to keep flushing whatever's left
# until there's nothing left in our items buffer.
while self._items_buffer:
self._flush()

View File

@@ -0,0 +1,343 @@
# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
# https://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.
import copy
from boto3.compat import collections_abc
from boto3.docs.utils import DocumentModifiedShape
from boto3.dynamodb.conditions import ConditionBase, ConditionExpressionBuilder
from boto3.dynamodb.types import TypeDeserializer, TypeSerializer
def register_high_level_interface(base_classes, **kwargs):
base_classes.insert(0, DynamoDBHighLevelResource)
class _ForgetfulDict(dict):
"""A dictionary that discards any items set on it. For use as `memo` in
`copy.deepcopy()` when every instance of a repeated object in the deepcopied
data structure should result in a separate copy.
"""
def __setitem__(self, key, value):
pass
def copy_dynamodb_params(params, **kwargs):
return copy.deepcopy(params, memo=_ForgetfulDict())
class DynamoDBHighLevelResource:
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Apply handler that creates a copy of the user provided dynamodb
# item such that it can be modified.
self.meta.client.meta.events.register(
'provide-client-params.dynamodb',
copy_dynamodb_params,
unique_id='dynamodb-create-params-copy',
)
self._injector = TransformationInjector()
# Apply the handler that generates condition expressions including
# placeholders.
self.meta.client.meta.events.register(
'before-parameter-build.dynamodb',
self._injector.inject_condition_expressions,
unique_id='dynamodb-condition-expression',
)
# Apply the handler that serializes the request from python
# types to dynamodb types.
self.meta.client.meta.events.register(
'before-parameter-build.dynamodb',
self._injector.inject_attribute_value_input,
unique_id='dynamodb-attr-value-input',
)
# Apply the handler that deserializes the response from dynamodb
# types to python types.
self.meta.client.meta.events.register(
'after-call.dynamodb',
self._injector.inject_attribute_value_output,
unique_id='dynamodb-attr-value-output',
)
# Apply the documentation customizations to account for
# the transformations.
attr_value_shape_docs = DocumentModifiedShape(
'AttributeValue',
new_type='valid DynamoDB type',
new_description=(
'- The value of the attribute. The valid value types are '
'listed in the '
':ref:`DynamoDB Reference Guide<ref_valid_dynamodb_types>`.'
),
new_example_value=(
'\'string\'|123|Binary(b\'bytes\')|True|None|set([\'string\'])'
'|set([123])|set([Binary(b\'bytes\')])|[]|{}'
),
)
key_expression_shape_docs = DocumentModifiedShape(
'KeyExpression',
new_type=(
'condition from :py:class:`boto3.dynamodb.conditions.Key` '
'method'
),
new_description=(
'The condition(s) a key(s) must meet. Valid conditions are '
'listed in the '
':ref:`DynamoDB Reference Guide<ref_dynamodb_conditions>`.'
),
new_example_value='Key(\'mykey\').eq(\'myvalue\')',
)
con_expression_shape_docs = DocumentModifiedShape(
'ConditionExpression',
new_type=(
'condition from :py:class:`boto3.dynamodb.conditions.Attr` '
'method'
),
new_description=(
'The condition(s) an attribute(s) must meet. Valid conditions '
'are listed in the '
':ref:`DynamoDB Reference Guide<ref_dynamodb_conditions>`.'
),
new_example_value='Attr(\'myattribute\').eq(\'myvalue\')',
)
self.meta.client.meta.events.register(
'docs.*.dynamodb.*.complete-section',
attr_value_shape_docs.replace_documentation_for_matching_shape,
unique_id='dynamodb-attr-value-docs',
)
self.meta.client.meta.events.register(
'docs.*.dynamodb.*.complete-section',
key_expression_shape_docs.replace_documentation_for_matching_shape,
unique_id='dynamodb-key-expression-docs',
)
self.meta.client.meta.events.register(
'docs.*.dynamodb.*.complete-section',
con_expression_shape_docs.replace_documentation_for_matching_shape,
unique_id='dynamodb-cond-expression-docs',
)
class TransformationInjector:
"""Injects the transformations into the user provided parameters."""
def __init__(
self,
transformer=None,
condition_builder=None,
serializer=None,
deserializer=None,
):
self._transformer = transformer
if transformer is None:
self._transformer = ParameterTransformer()
self._condition_builder = condition_builder
if condition_builder is None:
self._condition_builder = ConditionExpressionBuilder()
self._serializer = serializer
if serializer is None:
self._serializer = TypeSerializer()
self._deserializer = deserializer
if deserializer is None:
self._deserializer = TypeDeserializer()
def inject_condition_expressions(self, params, model, **kwargs):
"""Injects the condition expression transformation into the parameters
This injection includes transformations for ConditionExpression shapes
and KeyExpression shapes. It also handles any placeholder names and
values that are generated when transforming the condition expressions.
"""
self._condition_builder.reset()
generated_names = {}
generated_values = {}
# Create and apply the Condition Expression transformation.
transformation = ConditionExpressionTransformation(
self._condition_builder,
placeholder_names=generated_names,
placeholder_values=generated_values,
is_key_condition=False,
)
self._transformer.transform(
params, model.input_shape, transformation, 'ConditionExpression'
)
# Create and apply the Key Condition Expression transformation.
transformation = ConditionExpressionTransformation(
self._condition_builder,
placeholder_names=generated_names,
placeholder_values=generated_values,
is_key_condition=True,
)
self._transformer.transform(
params, model.input_shape, transformation, 'KeyExpression'
)
expr_attr_names_input = 'ExpressionAttributeNames'
expr_attr_values_input = 'ExpressionAttributeValues'
# Now that all of the condition expression transformation are done,
# update the placeholder dictionaries in the request.
if expr_attr_names_input in params:
params[expr_attr_names_input].update(generated_names)
else:
if generated_names:
params[expr_attr_names_input] = generated_names
if expr_attr_values_input in params:
params[expr_attr_values_input].update(generated_values)
else:
if generated_values:
params[expr_attr_values_input] = generated_values
def inject_attribute_value_input(self, params, model, **kwargs):
"""Injects DynamoDB serialization into parameter input"""
self._transformer.transform(
params,
model.input_shape,
self._serializer.serialize,
'AttributeValue',
)
def inject_attribute_value_output(self, parsed, model, **kwargs):
"""Injects DynamoDB deserialization into responses"""
if model.output_shape is not None:
self._transformer.transform(
parsed,
model.output_shape,
self._deserializer.deserialize,
'AttributeValue',
)
class ConditionExpressionTransformation:
"""Provides a transformation for condition expressions
The ``ParameterTransformer`` class can call this class directly
to transform the condition expressions in the parameters provided.
"""
def __init__(
self,
condition_builder,
placeholder_names,
placeholder_values,
is_key_condition=False,
):
self._condition_builder = condition_builder
self._placeholder_names = placeholder_names
self._placeholder_values = placeholder_values
self._is_key_condition = is_key_condition
def __call__(self, value):
if isinstance(value, ConditionBase):
# Create a conditional expression string with placeholders
# for the provided condition.
built_expression = self._condition_builder.build_expression(
value, is_key_condition=self._is_key_condition
)
self._placeholder_names.update(
built_expression.attribute_name_placeholders
)
self._placeholder_values.update(
built_expression.attribute_value_placeholders
)
return built_expression.condition_expression
# Use the user provided value if it is not a ConditonBase object.
return value
class ParameterTransformer:
"""Transforms the input to and output from botocore based on shape"""
def transform(self, params, model, transformation, target_shape):
"""Transforms the dynamodb input to or output from botocore
It applies a specified transformation whenever a specific shape name
is encountered while traversing the parameters in the dictionary.
:param params: The parameters structure to transform.
:param model: The operation model.
:param transformation: The function to apply the parameter
:param target_shape: The name of the shape to apply the
transformation to
"""
self._transform_parameters(model, params, transformation, target_shape)
def _transform_parameters(
self, model, params, transformation, target_shape
):
type_name = model.type_name
if type_name in ('structure', 'map', 'list'):
getattr(self, f'_transform_{type_name}')(
model, params, transformation, target_shape
)
def _transform_structure(
self, model, params, transformation, target_shape
):
if not isinstance(params, collections_abc.Mapping):
return
for param in params:
if param in model.members:
member_model = model.members[param]
member_shape = member_model.name
if member_shape == target_shape:
params[param] = transformation(params[param])
else:
self._transform_parameters(
member_model,
params[param],
transformation,
target_shape,
)
def _transform_map(self, model, params, transformation, target_shape):
if not isinstance(params, collections_abc.Mapping):
return
value_model = model.value
value_shape = value_model.name
for key, value in params.items():
if value_shape == target_shape:
params[key] = transformation(value)
else:
self._transform_parameters(
value_model, params[key], transformation, target_shape
)
def _transform_list(self, model, params, transformation, target_shape):
if not isinstance(params, collections_abc.MutableSequence):
return
member_model = model.member
member_shape = member_model.name
for i, item in enumerate(params):
if member_shape == target_shape:
params[i] = transformation(item)
else:
self._transform_parameters(
member_model, params[i], transformation, target_shape
)

View File

@@ -0,0 +1,310 @@
# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
# https://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.
from decimal import (
Clamped,
Context,
Decimal,
Inexact,
Overflow,
Rounded,
Underflow,
)
from boto3.compat import collections_abc
STRING = 'S'
NUMBER = 'N'
BINARY = 'B'
STRING_SET = 'SS'
NUMBER_SET = 'NS'
BINARY_SET = 'BS'
NULL = 'NULL'
BOOLEAN = 'BOOL'
MAP = 'M'
LIST = 'L'
DYNAMODB_CONTEXT = Context(
Emin=-128,
Emax=126,
prec=38,
traps=[Clamped, Overflow, Inexact, Rounded, Underflow],
)
BINARY_TYPES = (bytearray, bytes)
class Binary:
"""A class for representing Binary in dynamodb
Especially for Python 2, use this class to explicitly specify
binary data for item in DynamoDB. It is essentially a wrapper around
binary. Unicode and Python 3 string types are not allowed.
"""
def __init__(self, value):
if not isinstance(value, BINARY_TYPES):
types = ', '.join([str(t) for t in BINARY_TYPES])
raise TypeError(f'Value must be of the following types: {types}')
self.value = value
def __eq__(self, other):
if isinstance(other, Binary):
return self.value == other.value
return self.value == other
def __ne__(self, other):
return not self.__eq__(other)
def __repr__(self):
return f'Binary({self.value!r})'
def __str__(self):
return self.value
def __bytes__(self):
return self.value
def __hash__(self):
return hash(self.value)
class TypeSerializer:
"""This class serializes Python data types to DynamoDB types."""
def serialize(self, value):
"""The method to serialize the Python data types.
:param value: A python value to be serialized to DynamoDB. Here are
the various conversions:
Python DynamoDB
------ --------
None {'NULL': True}
True/False {'BOOL': True/False}
int/Decimal {'N': str(value)}
string {'S': string}
Binary/bytearray/bytes (py3 only) {'B': bytes}
set([int/Decimal]) {'NS': [str(value)]}
set([string]) {'SS': [string])
set([Binary/bytearray/bytes]) {'BS': [bytes]}
list {'L': list}
dict {'M': dict}
For types that involve numbers, it is recommended that ``Decimal``
objects are used to be able to round-trip the Python type.
For types that involve binary, it is recommended that ``Binary``
objects are used to be able to round-trip the Python type.
:rtype: dict
:returns: A dictionary that represents a dynamoDB data type. These
dictionaries can be directly passed to botocore methods.
"""
dynamodb_type = self._get_dynamodb_type(value)
serializer = getattr(self, f'_serialize_{dynamodb_type}'.lower())
return {dynamodb_type: serializer(value)}
def _get_dynamodb_type(self, value):
dynamodb_type = None
if self._is_null(value):
dynamodb_type = NULL
elif self._is_boolean(value):
dynamodb_type = BOOLEAN
elif self._is_number(value):
dynamodb_type = NUMBER
elif self._is_string(value):
dynamodb_type = STRING
elif self._is_binary(value):
dynamodb_type = BINARY
elif self._is_type_set(value, self._is_number):
dynamodb_type = NUMBER_SET
elif self._is_type_set(value, self._is_string):
dynamodb_type = STRING_SET
elif self._is_type_set(value, self._is_binary):
dynamodb_type = BINARY_SET
elif self._is_map(value):
dynamodb_type = MAP
elif self._is_listlike(value):
dynamodb_type = LIST
else:
msg = f'Unsupported type "{type(value)}" for value "{value}"'
raise TypeError(msg)
return dynamodb_type
def _is_null(self, value):
if value is None:
return True
return False
def _is_boolean(self, value):
if isinstance(value, bool):
return True
return False
def _is_number(self, value):
if isinstance(value, (int, Decimal)):
return True
elif isinstance(value, float):
raise TypeError(
'Float types are not supported. Use Decimal types instead.'
)
return False
def _is_string(self, value):
if isinstance(value, str):
return True
return False
def _is_binary(self, value):
if isinstance(value, (Binary, bytearray, bytes)):
return True
return False
def _is_set(self, value):
if isinstance(value, collections_abc.Set):
return True
return False
def _is_type_set(self, value, type_validator):
if self._is_set(value):
if False not in map(type_validator, value):
return True
return False
def _is_map(self, value):
if isinstance(value, collections_abc.Mapping):
return True
return False
def _is_listlike(self, value):
if isinstance(value, (list, tuple)):
return True
return False