Package: ibmcloudant
IBM Cloudant Python SDK Version 0.7.3IBM Cloudant Python SDK is a client library that interacts with theIBM Cloudant APIs.Disclaimer: This library is still a 0.x release. We do consider this
library production-ready and capable, but there are still some
limitations we’re working to resolve, and refinements we want to
deliver. We are working really hard to minimise the disruption from
now until the 1.0 release, but there may still be some changes that
impact applications using this SDK. For now, be sure to pin versions
to avoid surprises.Table of ContentsOverviewFeaturesPrerequisitesInstallationUsing the SDKAuthenticationAuthentication with environment variablesIAM API key authenticationIAM Trusted profile (container) authenticationIAM Trusted profile (VPC) authenticationSession cookie authenticationAuthentication with external configurationProgrammatic authenticationRequest timeout configurationCode examples1. Create a database and add a document2. Retrieve information from an existing database3. Update your previously created document4. Delete your previously created documentFurther code examplesError handlingRaw IOModel classes vs dictionariesFurther resourcesChanges feed follower (beta)IntroductionModes of operationConfiguring the changes followerError suppressionFollower operationCheckpointingCode examplesInitializing a changes followerStarting the changes followerStart mode for continuous listeningStart mode for one-off fetchingProcessing changesProcess continuous changesProcess one-off changesStopping the changes followerQuestionsIssuesVersioning and LTS supportOpen source at IBMContributingLicenseOverviewThe IBM Cloudant Python SDK allows developers to programmatically
interact withIBM Cloudantwith the help of theibmcloudantpackage.FeaturesThe purpose of this Python SDK is to wrap most of the HTTP request APIs
provided by Cloudant and supply other functions to ease the usage of Cloudant.
This SDK should make life easier for programmers to do what’s really important
to them: developing software.Reasons why you should consider using Cloudant Python SDK in your
project:Supported by IBM Cloudant.Server compatibility with:IBM Cloudant.Apache CouchDB 3.xfor data operations.Includes all the most popular and latest supported endpoints for
applications.Handles the authentication.Familiar user experience with IBM Cloud SDKs.Flexibility to use either built-in models or byte-based requests and responses for documents.Built-inChanges feed follower(beta)Instances of the client are unconditionally thread-safe.PrerequisitesACloudantservice instance or aCouchDBserver.Python 3.8 or above.InstallationTo install, usepiporeasy_install:pipinstall--upgrade"ibmcloudant>=0.7.3"oreasy_install--upgrade"ibmcloudant>=0.7.3"Using the SDKFor fundamental SDK usage information and config options, please see the commonIBM Cloud SDKdocumentation.This library requires configuration with a service URL andCloudant service credentialsto authenticate with your
account.There are several ways tosetthese authentication properties:Asenvironment variablesTheprogrammatic approachWith anexternal credentials fileThe following section describes the different authentication types and provides environment variable examples.
Examples for other configuration methods are available by following the provided links.AuthenticationThis library requires credentials to authenticate with IBM Cloudant. These credentials may be:IBM Cloud IAM credentials (can be used with authentication typesCONTAINER,VPCandIAM)IBM Cloud accountuser, service ID or trusted profile credentials
that have been granted access to the IBM Cloud Cloudant resource instance.IBM Cloudant service credentialsgenerated by the IBM Cloud Cloudant resource instance.Username and password credentials (can be used with authentication typesCOUCHDB_SESSIONandBASIC)IBM Cloudant service credentialsgenerated for an IBM Cloud Cloudant resource instance not configured asIAM only.IBM Cloudantlegacy credentials(i.e. username and password) for instances not in IBM Cloud.IBM Cloudantlegacy API keys.For other compatible APIs that are not Cloudant accounts (e.g. Apache CouchDB) non-IAM based authentication types
must be used.This table summarizes the available authentication types.
The authentication types are listed in order of recommendation, preferably use the authentication type
from the first row in the table that is compatible with your environment.Authentication typeRecommended forAUTH_TYPEDescriptionIAM Trusted Profiles compute resource (container)Cloudant(SDK running in IBM Cloud IKS)CONTAINERObtains a compute resource (CR) token from the container.Exchanges the CR token for an IAMaccess_token.Adds anAuthorization: Bearer <access_token>header to each HTTP request.Automatically renews the access token when needed.IAM Trusted Profiles compute resource (VPC)Cloudant(SDK running in IBM Cloud VPC)VPCObtains an identity token from the VPC instance metadata.Exchanges the identity token for an IAMaccess_token.Adds anAuthorization: Bearer <access_token>header to each HTTP request.Automatically renews the access token when needed.IAM API keyCloudantIAMExchanges an IAM API key for an IAMaccess_token.Adds anAuthorization: Bearer <access_token>header to each HTTP request.Automatically renews the access token when needed.Session cookieCloudant(legacy credentials & instances without IAM)Apache CouchDBCOUCHDB_SESSIONExchanges credentials with/_sessionendpoint to retrieve a cookie.AddsCookieheader and content to each HTTP request.Automatically renews session when needed.Bearer tokenApache CouchDB(using JWT authentication)BEARERTOKENAdds anAuthorization: Bearer <token>to each HTTP request.No token management or renewal.Also compatible with IAM access tokens managed independently of the SDK.BasicApache CouchDB(if cookies are not enabled)BASICAdds anAuthorization: Basic <encoded username and password>header to each HTTP request.None-NOAUTHNote that this authentication type only works for operations against a database allowing access for unauthenticated users.The default authentication type for the SDK isCONTAINERunlessAPIKEYconfiguration is supplied, which changes the default authentication type toIAM.Authentication with environment variablesThe default service name isCLOUDANTsoCLOUDANT_prefixed names are used in these examples.Any custom service name prefix can be used as long as the matching name is used to instantiate the SDK client
and the same prefix is used for all configuration options.IAM API key authenticationFor CloudantIAM API key authentication, set the following environmental variables by
replacing the<url>and<apikey>with your properservice credentials. There is no need to setCLOUDANT_AUTH_TYPEtoIAMbecause it is the default when anAPIKEYis set.CLOUDANT_URL=<url>CLOUDANT_APIKEY=<apikey>IAM Trusted profile (container) authenticationFor CloudantIAM Trusted profile compute resource container authentication, set the following environmental variables by
replacing the<url>and<id>with your values. There is no need to setCLOUDANT_AUTH_TYPEtoCONTAINERbecause it is the default.CLOUDANT_URL=<url>CLOUDANT_IAM_PROFILE_ID=<id>Alternatively a profile name may be used instead of an ID by replacingCLOUDANT_IAM_PROFILE_IDwithCLOUDANT_IAM_PROFILE_NAME.IAM Trusted profile (VPC) authenticationFor CloudantIAM Trusted profile compute resource vpc authentication, set the following environmental variables by
replacing the<url>and<id>with your values.CLOUDANT_AUTH_TYPE=VPCCLOUDANT_URL=<url>CLOUDANT_IAM_PROFILE_ID=<id>Alternatively a profile CRN may be used instead of an ID by replacingCLOUDANT_IAM_PROFILE_IDwithCLOUDANT_IAM_PROFILE_CRN.Session cookie authenticationForCOUCHDB_SESSIONauthentication, set the following environmental variables
by replacing the<url>,<username>and<password>with your properservice credentials.CLOUDANT_AUTH_TYPE=COUCHDB_SESSIONCLOUDANT_URL=<url>CLOUDANT_USERNAME=<username>CLOUDANT_PASSWORD=<password>Authentication with external configurationTo use an external configuration file, theCloudant API docs,
or thegeneral SDK usage informationwill guide you.Programmatic authenticationTo learn more about how to use programmatic authentication, see the related
documentation in theCloudant API docsor in thePython SDK Core documentabout authentication.Request timeout configurationNo request timeout is defined, but a 2.5m read and a 60s connect timeout are set by default. Be sure to set a request timeout appropriate to your application usage and environment.
Therequest timeoutsection contains details on how to change the value.Note:System settings may take precedence over configured timeout values.Code examplesThe following code examplesauthenticate with the environment variables.1. Create a database and add a documentNote:This example code assumes thatordersdatabase does not exist in your account.This example code createsordersdatabase and adds a new document "example"
into it. To connect, you must set your environment variables with
theservice url,authentication typeandauthentication credentialsof your Cloudant service.Cloudant environment variable naming starts with aservice nameprefix that identifies your service.
By default, this isCLOUDANT, see the settings in theauthentication with environment variables section.If you would like to rename your Cloudant service fromCLOUDANT,
you must use your defined service name as the prefix for all Cloudant related environment variables.Once the environment variables are set, you can try out the code examples.fromibm_cloud_sdk_coreimportApiExceptionfromibmcloudant.cloudant_v1importCloudantV1,Document# 1. Create a client with `CLOUDANT` default service name =============client=CloudantV1.new_instance()# 2. Create a database ================================================example_db_name="orders"# Try to create database if it doesn't existtry:put_database_result=client.put_database(db=example_db_name).get_result()ifput_database_result["ok"]:print(f'"{example_db_name}" database created.')exceptApiExceptionasae:ifae.status_code==412:print(f'Cannot create "{example_db_name}" database, '+'it already exists.')# 3. Create a document ================================================# Create a document object with "example" idexample_doc_id="example"# Setting `id` for the document is optional when "post_document"# function is used for CREATE. When `id` is not provided the server# will generate one for your document.example_document:Document=Document(id=example_doc_id)# Add "name" and "joined" fields to the documentexample_document.name="Bob Smith"example_document.joined="2019-01-24T10:42:59.000Z"# Save the document in the database with "post_document" functioncreate_document_response=client.post_document(db=example_db_name,document=example_document).get_result()# =====================================================================# Note: saving the document can also be done with the "put_document"# function. In this case `doc_id` is required for a CREATE operation:"""create_document_response = client.put_document(db=example_db_name,doc_id=example_doc_id,document=example_document).get_result()"""# =====================================================================# Keeping track of the revision number of the document object# is necessary for further UPDATE/DELETE operations:example_document.rev=create_document_response["rev"]print(f'You have created the document:\n{example_document}')When you run the code, you see a result similar to the following output."orders" database created.
You have created the document:
{
  "_id": "example",
  "_rev": "1-1b403633540686aa32d013fda9041a5d",
  "name": "Bob Smith",
  "joined": "2019-01-24T10:42:99.000Z"
}2. Retrieve information from an existing databaseNote: This example code assumes that you have created both theordersdatabase and theexampledocument byrunning the previous example codesuccessfully. Otherwise, the following error message occurs, "Cannot delete document because either 'orders'
database or 'example' document was not found."Gather database information exampleimportjsonfromibmcloudant.cloudant_v1importCloudantV1# 1. Create a client with `CLOUDANT` default service name ============client=CloudantV1.new_instance()# 2. Get server information ===========================================server_information=client.get_server_information().get_result()print(f'Server Version:{server_information["version"]}')# 3. Get database information for "orders" ==========================db_name="orders"db_information=client.get_database_information(db=db_name).get_result()# 4. Show document count in database ==================================document_count=db_information["doc_count"]print(f'Document count in\"{db_information["db_name"]}\"'f'database is{document_count}.')# 5. Get "example" document out of the database by document id ============document_example=client.get_document(db=db_name,doc_id="example").get_result()print(f'Document retrieved from database:\n'f'{json.dumps(document_example,indent=2)}')When you run the code, you see a result similar to the following output.Server Version: 2.1.1
Document count in "orders" database is 1.
Document retrieved from database:
{
  "_id": "example",
  "_rev": "1-1b403633540686aa32d013fda9041a5d",
  "name": "Bob Smith",
  "joined": "2019-01-24T10:42:99.000Z"
}3. Update your previously created documentNote: This example code assumes that you have created both theordersdatabase and theexampledocument byrunning the previous example codesuccessfully. Otherwise, the following error message occurs, "Cannot update document because either 'orders'
database or 'example' document was not found."Update code exampleimportjsonfromibm_cloud_sdk_coreimportApiExceptionfromibmcloudant.cloudant_v1importCloudantV1# 1. Create a client with `CLOUDANT` default service name =============client=CloudantV1.new_instance()# 2. Update the document ==============================================example_db_name="orders"example_doc_id="example"# Try to get the document if it previously existed in the databasetry:document=client.get_document(db=example_db_name,doc_id=example_doc_id).get_result()# =================================================================# Note: for response byte stream use:"""document_as_byte_stream = client.get_document_as_stream(db=example_db_name,doc_id=example_doc_id).get_result()"""# =================================================================#  Add Bob Smith's address to the documentdocument["address"]="19 Front Street, Darlington, DL5 1TY"#  Remove the joined property from document objectif"joined"indocument:document.pop("joined")# Update the document in the databaseupdate_document_response=client.post_document(db=example_db_name,document=document).get_result()# =================================================================# Note 1: for request byte stream use:"""update_document_response = client.post_document(db=example_db_name,document=document_as_byte_stream).get_result()"""# =================================================================# =================================================================# Note 2: updating the document can also be done with the# "put_document" function. `doc_id` and `rev` are required for an# UPDATE operation, but `rev` can be provided in the document# object as `_rev` too:"""update_document_response = client.put_document(db=example_db_name,doc_id=example_doc_id,  # doc_id is a required parameterrev=document["_rev"],document=document  # _rev in the document object CAN replace above `rev` parameter).get_result()"""# =================================================================# Keeping track of the latest revision number of the document# object is necessary for further UPDATE/DELETE operations:document["_rev"]=update_document_response["rev"]print(f'You have updated the document:\n'+json.dumps(document,indent=2))exceptApiExceptionasae:ifae.status_code==404:print('Cannot delete document because either '+f'"{example_db_name}" database or "{example_doc_id}" '+'document was not found.')When you run the code, you see a result similar to the following output.{
  "_id": "example",
  "_rev": "2-4e2178e85cffb32d38ba4e451f6ca376",
  "name": "Bob Smith",
  "address": "19 Front Street, Darlington, DL5 1TY"
}4. Delete your previously created documentNote: This example code assumes that you have created both theordersdatabase and theexampledocument byrunning the previous example codesuccessfully. Otherwise, the following error message occurs, "Cannot delete document because either 'orders'
database or 'example' document was not found."Delete code examplefromibm_cloud_sdk_coreimportApiExceptionfromibmcloudant.cloudant_v1importCloudantV1# 1. Create a client with `CLOUDANT` default service name =============client=CloudantV1.new_instance()# 2. Delete the document ==============================================example_db_name="orders"example_doc_id="example"# Try to get the document if it previously existed in the databasetry:document=client.get_document(db=example_db_name,doc_id=example_doc_id).get_result()delete_document_response=client.delete_document(db=example_db_name,doc_id=example_doc_id,# `doc_id` is required for DELETErev=document["_rev"]# `rev` is required for DELETE).get_result()ifdelete_document_response["ok"]:print('You have deleted the document.')exceptApiExceptionasae:ifae.status_code==404:print('Cannot delete document because either '+f'"{example_db_name}" database or "{example_doc_id}"'+'document was not found.')When you run the code, you see the following output.You have deleted the document.Further code examplesFor a complete list of code examples, see theexamples directory.Error handlingFor sample code on handling errors, seeCloudant API docs.Raw IOFor endpoints that read or write document content it is possible to bypass
usage of the built-in object with byte streams.Depending on the specific SDK operation it may be possible to:accept a user-provided byte stream to send to the server as a request bodyreturn a byte stream of the server response body to the userRequest byte stream can be supplied for arguments that accept theBinaryIOtype.
For these cases you can pass this byte stream directly to the HTTP request body.Response byte stream is supported in functions with the suffix of_as_stream.
The returned byte stream allows the response body to be consumed
without triggering JSON unmarshalling that is typically performed by the SDK.Theupdate documentsection
contains examples for both request and response byte stream cases.The API reference contains further examples of using byte streams.
They are titled "Example request as stream" and are initially collapsed.
Expand them to see examples of:Byte requests:Bulk modify multiple documents in a databaseByte responses:Query a list of all documents in a databaseQuery the database document changes feedModel classes vs dictionariesThis SDK supports two possible formats to define an HTTP request. One approach uses only model classes and the other only dictionaries.Example using model class structurefromibmcloudant.cloudant_v1importDesignDocument,CloudantV1,DesignDocumentOptions,SearchIndexDefinitionclient=CloudantV1.new_instance()price_index=SearchIndexDefinition(index='function (doc) { index("price", doc.price); }')design_document_options=DesignDocumentOptions(partitioned=True)partitioned_design_doc=DesignDocument(indexes={'findByPrice':price_index},options=design_document_options)response=client.put_design_document(db='products',design_document=partitioned_design_doc,ddoc='appliances').get_result()print(response)Same example using dictionary structurefromibmcloudant.cloudant_v1importCloudantV1client=CloudantV1.new_instance()price_index={'index':'function (doc) { index("price", doc.price); }'}partitioned_design_doc={'indexes':{'findByPrice':price_index},'options':{'partitioned':True},}response=client.put_design_document(db='products',design_document=partitioned_design_doc,ddoc='appliances').get_result()print(response)Since model classes and dicts are different data structures, they cannot be combined.This solution will be invalidfromibmcloudant.cloudant_v1importCloudantV1,DesignDocumentclient=CloudantV1.new_instance()price_index={'index':'function (doc) { index("price", doc.price); }'}partitioned_design_doc=DesignDocument(indexes={'findByPrice':price_index},options={'partitioned':True})response=client.put_design_document(db='products',design_document=partitioned_design_doc,ddoc='appliances').get_result()print(response)Further resourcesCloudant API docs:
API reference including usage examples for Cloudant Python SDK API.Pydoc:
Cloudant Python SDK API Documentation.Cloudant docs:
The official documentation page for Cloudant.Cloudant blog:
Many useful articles about how to optimize Cloudant for common problems.Changes feed follower (beta)IntroductionThe SDK provides a changes feed follower utility (currently beta).
This helper utility connects to the_changesendpoint and returns the individual change items.
It removes some of the complexity of using the_changesendpoint by setting some options automatically
and providing error suppression and retries.Tip: the changes feed often does not meet user expectations or assumptions.Consult theCloudant changes feed FAQto get a better understanding of the limitations and suitable use-cases before using the changes feed in your application.Modes of operationThere are two modes of operation:Start modeFetches the changes from the suppliedsincesequence (by default the feed will start fromnow).Fetches all available changes and then continues listening for new changes indefinitely unless encountering an end condition.An example use case for this mode is event driven workloads.Start one-off modeFetches the changes from the suppliedsincesequence (by default the feed will start from the beginning).Fetches all available changes and then stops when either there are no further changes pending or encountering an end condition.An example use case for this mode is ETL style workloads.Configuring the changes followerThe SDK's model of changes feed options is also used to configure the follower.
However, a subset of the options are invalid as they are configured internally by the implementation.
Supplying these options when instantiating the follower causes an error.
The invalid options are:descendingfeedheartbeatlastEventId- usesinceinsteadtimeoutOnly the value of_selectoris permitted for thefilteroption. This restriction is because selector
based filters perform better than JavaScript backed filters. Configuring a non-selector based filter will
cause the follower to error.Note that thelimitparameter will terminate the follower at the given number of changes in either
operating mode.The changes follower requires the client to have HTTP timeouts of at least 1 minute and will error during
instantiation if it is insufficient. The default client configuration has sufficiently long timeouts.For use-cases where these configuration limitations are deemed too restrictive then it is recommended to
write code to use the SDK'sPOST_changesAPIinstead of the follower.Error suppressionBy default, the changes follower will suppress transient errors indefinitely and attempt to run to completion or listen forever as
dictated by the operating mode.
For applications where that is not desirable an optional error tolerance duration may be specified to control the time since
the last successful response that transient errors will be suppressed. This can be used, for example,  by applications as a grace period
before reporting an error and requiring intervention.There are some additional points to consider for error suppression:Errors considered terminal, for example, the database not existing or invalid credentials are never suppressed and will error immediately.The error suppression duration is not guaranteed to fire immediately after lapsing and should be considered a minimum suppression time.The changes follower will back-off between retries and as such may remain paused for a short while after the transient errors have resolved.If the underlying SDK client used to initialize the follower also has retries configured then errors could be suppressed for significantly
longer than the follower's configured error tolerance duration depending on the configuration options.Follower operationFor both modes:The end conditions are:A terminal error (HTTP codes400,401,403404).Transient errors occur for longer than the error tolerance duration. Transient errors are all other HTTP status codes and connection errors.The number of changes received reaches the configuredlimit.The feed is terminated early by calling stop.As is true for the_changesendpoint change items haveat least oncedelivery and an individual item
may be received multiple times. When using the follower change items may be repeated even within a limited
number of changes (i.e. using thelimitoption) this is a minor difference from usinglimiton the HTTP native API.The follower is not optimized for some use cases and it is not recommended to use it in cases where:Settinginclude_docsand larger document sizes (for example > 10 kiB).The volume of changes is very high (if the rate of changes in the database exceeds the follower's rate of pulling them it will never catch-up).In these cases use-case specific control over the number of change requests made and the content size of the responses
may be achieved by using the SDK'sPOST_changesAPI.CheckpointingThe changes follower does not checkpoint since it has no information about whether a change item has been
processed by the consuming application after being received. It is the application developer's responsibility
to store the sequence IDs to have appropriate checkpoints and to re-initialize the follower with the requiredsincevalue after, for example, the application restarts.The frequency and conditions for checkpointing are application specific and some applications may be tolerant
of dropped changes. This section is intended only to provide general guidance on how to avoid missing changes.To guarantee processing of all changes the sequence ID from a change item must not be persisted untilafterthe processing of the change item by the application has completed. As indicated previously change items are
deliveredat least onceso application code must be able to handle repeated changes already and it is
preferable to restart from an oldersincevalue and receive changes again than risk missing them.The sequence IDs are available on each change item by default, but may be omitted from some change items when
using theseq_intervalconfiguration option. Infrequent sequence IDs may improve performance by reducing
the amount of data that needs to be transferred, but the trade-off is that more changes will be repeated if
it is necessary to resume the changes follower.Extreme care should be taken with persisting sequences if choosing to process change items in parallel as there
is a considerable risk of missing changes on a restart if the sequence is recorded out of order.Code examplesInitializing a changes followerimportChangesFollowerfromibmcloudant.cloudant_v1importCloudantV1client=CloudantV1.new_instance()cf_params={'db':'example',# Required: the database name.'limit':100,# Optional: return only 100 changes (including duplicates).'since':'3-g1AG3...'# Optional: start from this sequence ID (e.g. with a value read from persistent storage).}changes_follower=ChangesFollower(service=client,# Required: the Cloudant service client instance.error_tolerance=10000,# Optional: suppress transient errors for at least 10 seconds before terminating.**cf_params# Required: changes feed configuration options dict.)Starting the changes followerStart mode for continuous listeningimportIterablefromibmcloudantimportChangesFollowerfromibmcloudant.cloudant_v1importCloudantV1,ChangesResultItemclient=CloudantV1.new_instance()changes_follower=ChangesFollower(service=client,**{'db':'example'})changes_items:Iterable[ChangesResultItem]=changes_follower.start()# Note: iterable will not do anything until it is iterated# Create a for loop to iterate over the flow of changes# for changes_item in changes_items: ...Start mode for one-off fetchingimportIterablefromibmcloudantimportChangesFollowerfromibmcloudant.cloudant_v1importCloudantV1,ChangesResultItemclient=CloudantV1.new_instance()changes_follower=ChangesFollower(service=client,**{'db':'example'})changes_items:Iterable[ChangesResultItem]=changes_follower.start_one_off()# Note: iterable will not do anything until it is iterated# Create a for loop to iterate over the flow of changes# for changes_item in changes_items: ...Processing changesProcess continuous changesimportChangesFollowerfromibmcloudant.cloudant_v1importCloudantV1client=CloudantV1.new_instance()# Start from a previously persisted seq# Normally this would be read by the app from persistent storage# e.g. previously_persisted_seq = your_app_persistence_read_func()previously_persisted_seq='3-g1AG3...'changes_follower=ChangesFollower(service=client,**{'db':'example','since':previously_persisted_seq})changes_items=changes_follower.start()forchanges_iteminchanges_items:# do something with changesprint(changes_item.id)forchangeinchanges_item.changes:print(change.rev)# when change item processing is complete app can store seqseq=changes_item.seq# write seq to persistent storage for use as since if required to resume later# e.g. your_app_persistence_write_func(seq)# keep processing changes until the application is terminated or some other stop condition is reached# Note: iterator above is blocking, code here will be unreachable# until the iteration is stopped or another stop condition is reached.# For long running followers careful consideration should be made of where to call stop on the iterator.Process one-off changesimportChangesFollowerfromibmcloudant.cloudant_v1importCloudantV1client=CloudantV1.new_instance()# Start from a previously persisted seq# Normally this would be read by the app from persistent storage# e.g. previously_persisted_seq = your_app_persistence_read_func()previously_persisted_seq='3-g1AG3...'changes_follower=ChangesFollower(service=client,**{'db':'example','since':previously_persisted_seq})changes_items=changes_follower.start_one_off()forchanges_iteminchanges_items:# do something with changesprint(changes_item.id)forchangeinchanges_item.changes:print(change.rev)# when change item processing is complete app can store seqseq=changes_item.seq# write seq to persistent storage for use as since if required to resume later# e.g. your_app_persistence_write_func(seq)# Note: iterator above is blocking, code here will be unreachable# until all changes are processed (or another stop condition is reached).Stopping the changes followerimportChangesFollowerfromibmcloudant.cloudant_v1importCloudantV1client=CloudantV1.new_instance()changes_follower=ChangesFollower(service=client,**{'db':'example'})changes_items=changes_follower.start()forchanges_iteminchanges_items:# Option 1: call stop after some condition# Note that since the iterator is blocking at least one item# must be returned from it to reach to this point.# Additional changes may be processed before the iterator stops.changes_follower.stop()# Option 2: call stop method when you want to end the continuous loop from# outside the iterator.  For example, you've put the changes follower in a# separate thread and need to call stop on the main thread.# Note: in this context the call must be made from a different thread because# code immediately following the iterator is unreachable until the iterator# has stopped.changes_follower.stop()QuestionsIf you are having difficulties using this SDK or have a question about the
IBM Cloud services, ask a question onStack Overflow.IssuesIf you encounter an issue with the project, you are welcome to submit abug report.Before you submit a bug report, search forsimilar issuesand review theKNOWN_ISSUES fileto verify that your issue hasn't been reported yet.Please consult thesecurity policybefore opening security related issues.Versioning and LTS supportThis SDK follows semantic versioning with respect to the definition of user facing APIs.
This means under some circumstances breaking changes may occur within a major or minor version
of the SDK related to changes in supported language platforms.The SDK is supported on the available LTS releases of the language platform.
The LTS language versions are listed in the prerequisites:LTS versions currently supported by the SDKLTS versions for this release of the SDKIncompatible changes from new language versions are not added to the SDK
until they are available in the minimum supported language version.When language LTS versions move out of support the following will happen:Existing SDK releases will continue to run on obsolete language versions, but will no longer be supported.The minimum language version supported by the SDK will be updated to the next available LTS.New language features may be added in subsequent SDK releases that will cause breaking changes
if the new releases of the SDK are used with older, now unsupported, language levels.Open source at IBMFind more open source projects on theIBM GitHubpage.ContributingFor more information, seeCONTRIBUTING.LicenseThis SDK is released under the Apache 2.0 license. To read the full text of the license, seeLICENSE.