Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Injecting examples into docs at build-time #402

Merged
merged 4 commits into from
Dec 14, 2015
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion boto3/docs/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
# 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

import boto3
from botocore.exceptions import DataNotFoundError
from botocore.docs.paginator import PaginatorDocumenter
from botocore.docs.waiter import WaiterDocumenter
Expand All @@ -22,6 +25,9 @@


class ServiceDocumenter(object):
# The path used to find examples
EXAMPLE_PATH = os.sep.join([os.path.dirname(boto3.__file__), 'examples'])

def __init__(self, service_name, session):
self._service_name = service_name
self._session = session
Expand All @@ -39,7 +45,8 @@ def __init__(self, service_name, session):
'paginators',
'waiters',
'service-resource',
'resources'
'resources',
'examples'
]

def document_service(self):
Expand All @@ -60,6 +67,7 @@ def document_service(self):
self._document_service_resource(
doc_structure.get_section('service-resource'))
self._document_resources(doc_structure.get_section('resources'))
self._document_examples(doc_structure.get_section('examples'))
return doc_structure.flush_structure()

def _document_title(self, section):
Expand Down Expand Up @@ -120,3 +128,16 @@ def _document_resources(self, section):
ResourceDocumenter(
resource, self._botocore_session).document_resource(
section.add_new_section(resource.meta.resource_model.name))

def _get_example_file(self):
return os.path.realpath(
os.sep.join([self.EXAMPLE_PATH,
self._service_name + '.rst']))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW, I would prefer to use os.path.join(..., ..., ...) in favor of os.sep.join([..., ..., ...]), because it is conceptually a high level API.

This is NOT a must-fix. I will still give out a "ship-it" for this PR based on its current implementation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, yes. That's much nicer. Thanks!


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, 'r') as f:
section.write(f.read())
14 changes: 14 additions & 0 deletions boto3/examples/s3.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
List objects in an Amazon S3 bucket
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The following example shows how to use an Amazon S3 bucket resource to list
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This technically is to get attributes that are not located on the ObjectSummary but it is located on the Object resource as ObjectSummary is instantiated from ListObjects while Object is instantiated from HeadObject. I say we simplify the example or reword the sentence to be specific to the glacier example that this encapsulates or simplify the example to not have the switching from ObjectSummary to Object in order to list objects in a bucket.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, my bad :8 I'll fix this.

the objects in the bucket.

.. code-block:: python

import boto3

s3 = boto3.resource('s3')
bucket = s3.Bucket('my-bucket')
for obj in bucket.objects.all():
print(obj.key)
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ def get_version():
package_data={
'boto3': [
'data/aws/resources/*.json',
'examples/*.rst'
]
},
include_package_data=True,
Expand Down
3 changes: 3 additions & 0 deletions tests/unit/data/examples/myservice.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
** This is an example **

This is the contents!
3 changes: 3 additions & 0 deletions tests/unit/data/examples/otherservice.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
**Other example**

This is for another service
20 changes: 20 additions & 0 deletions tests/unit/docs/test_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.
import os
import mock

import boto3
from tests.unit.docs import BaseDocsTest
from boto3.docs.service import ServiceDocumenter

Expand Down Expand Up @@ -111,3 +113,21 @@ def test_document_service_no_waiter(self):
service_documenter = ServiceDocumenter('myservice', self.session)
contents = service_documenter.document_service().decode('utf-8')
self.assertNotIn('Waiters', contents)

def test_creates_correct_path_to_examples_based_on_service_name(self):
path = os.sep.join([os.path.dirname(boto3.__file__), 'boto3',
'examples', 'myservice.rst'])
path = os.path.realpath(path)
with mock.patch('os.path.isfile') as patch:
ServiceDocumenter('myservice', self.session)
patch.assert_has_call_with(path)

def test_injects_examples_when_found(self):
examples_path = os.sep.join([os.path.dirname(__file__), '..', 'data',
'examples'])
service_documenter = ServiceDocumenter(
'myservice', self.session)
service_documenter.EXAMPLE_PATH = examples_path
contents = service_documenter.document_service().decode('utf-8')
self.assertIn('This is an example', contents)
self.assertNotIn('This is for another service', contents)