diff --git a/doc/api/pymongo/ismaster.rst b/doc/api/pymongo/ismaster.rst new file mode 100644 index 000000000..881e874e1 --- /dev/null +++ b/doc/api/pymongo/ismaster.rst @@ -0,0 +1,10 @@ +:orphan: + +:mod:`ismaster` -- A wrapper for ismaster command responses. +============================================================ + +.. automodule:: pymongo.ismaster + + .. autoclass:: pymongo.ismaster.IsMaster(doc) + + .. autoattribute:: document diff --git a/doc/api/pymongo/server_description.rst b/doc/api/pymongo/server_description.rst new file mode 100644 index 000000000..2d354fca6 --- /dev/null +++ b/doc/api/pymongo/server_description.rst @@ -0,0 +1,13 @@ +:orphan: + +:mod:`server_description` -- An object representation of a server the driver is connected to. +============================================================================================= + +.. automodule:: pymongo.server_description + + .. autoclass:: pymongo.server_description.ServerDescription() + + .. autoattribute:: address + .. autoattribute:: all_hosts + .. autoattribute:: server_type + .. autoattribute:: server_type_name diff --git a/doc/api/pymongo/topology_description.rst b/doc/api/pymongo/topology_description.rst new file mode 100644 index 000000000..b14f1bf2c --- /dev/null +++ b/doc/api/pymongo/topology_description.rst @@ -0,0 +1,14 @@ +:orphan: + +:mod:`topology_description` -- An object representation of a deployment of MongoDB servers. +=========================================================================================== + +.. automodule:: pymongo.topology_description + + .. autoclass:: pymongo.topology_description.TopologyDescription() + + .. automethod:: has_readable_server(read_preference=ReadPreference.PRIMARY) + .. automethod:: has_writable_server + .. automethod:: server_descriptions + .. autoattribute:: topology_type + .. autoattribute:: topology_type_name diff --git a/pymongo/monitoring.py b/pymongo/monitoring.py index 64b227617..99eb2d51f 100644 --- a/pymongo/monitoring.py +++ b/pymongo/monitoring.py @@ -14,6 +14,8 @@ """Tools to monitor driver events. +.. versionadded:: 3.1 + Use :func:`register` to register global listeners for specific events. Listeners must inherit from one of the abstract classes below and implement the correct functions for that class. @@ -45,6 +47,74 @@ For example, a simple command logger might be implemented like this:: monitoring.register(CommandLogger()) +Server discovery and monitoring events are also available. For example:: + + class ServerLogger(monitoring.ServerListener): + + def opened(self, event): + logging.info("Server {0.server_address} added to topology " + "{0.topology_id}".format(event)) + + def description_changed(self, event): + previous_server_type = event.previous_description.server_type + new_server_type = event.new_description.server_type + if new_server_type != previous_server_type: + # server_type_name was added in PyMongo 3.4 + logging.info( + "Server {0.server_address} changed type from " + "{0.previous_description.server_type_name} to " + "{0.new_description.server_type_name}".format(event)) + + def closed(self, event): + logging.warning("Server {0.server_address} removed from topology " + "{0.topology_id}".format(event)) + + + class HeartbeatLogger(monitoring.ServerHeartbeatListener): + + def started(self, event): + logging.info("Heartbeat sent to server " + "{0.connection_id}".format(event)) + + def succeeded(self, event): + # The reply.document attribute was added in PyMongo 3.4. + logging.info("Heartbeat to server {0.connection_id} " + "succeeded with reply " + "{0.reply.document}".format(event)) + + def failed(self, event): + logging.warning("Heartbeat to server {0.connection_id} " + "failed with error {0.reply}".format(event)) + + class TopologyLogger(monitoring.TopologyListener): + + def opened(self, event): + logging.info("Topology with id {0.topology_id} " + "opened".format(event)) + + def description_changed(self, event): + logging.info("Topology description updated for " + "topology id {0.topology_id}".format(event)) + previous_topology_type = event.previous_description.topology_type + new_topology_type = event.new_description.topology_type + if new_topology_type != previous_topology_type: + # topology_type_name was added in PyMongo 3.4 + logging.info( + "Topology {0.topology_id} changed type from " + "{0.previous_description.topology_type_name} to " + "{0.new_description.topology_type_name}".format(event)) + # The has_writable_server and has_readable_server methods + # were added in PyMongo 3.4. + if not event.new_description.has_writable_server(): + logging.warning("No writable servers available.") + if not event.new_description.has_readable_server(): + logging.warning("No readable servers available.") + + def closed(self, event): + logging.info("Topology with id {0.topology_id} " + "closed".format(event)) + + Event listeners can also be registered per instance of :class:`~pymongo.mongo_client.MongoClient`:: @@ -78,13 +148,13 @@ _LISTENERS = _Listeners([], [], [], []) class _EventListener(object): - """Abstract base class for all event listeners. """ + """Abstract base class for all event listeners.""" class CommandListener(_EventListener): """Abstract base class for command listeners. Handles `CommandStartedEvent`, `CommandSucceededEvent`, - and `CommandFailedEvent`""" + and `CommandFailedEvent`.""" def started(self, event): """Abstract method to handle a `CommandStartedEvent`. @@ -114,7 +184,10 @@ class CommandListener(_EventListener): class ServerHeartbeatListener(_EventListener): """Abstract base class for server heartbeat listeners. Handles `ServerHeartbeatStartedEvent`, `ServerHeartbeatSucceededEvent`, - and `ServerHeartbeatFailedEvent`.""" + and `ServerHeartbeatFailedEvent`. + + .. versionadded:: 3.3 + """ def started(self, event): """Abstract method to handle a `ServerHeartbeatStartedEvent`. @@ -144,7 +217,10 @@ class ServerHeartbeatListener(_EventListener): class TopologyListener(_EventListener): """Abstract base class for topology monitoring listeners. Handles `TopologyOpenedEvent`, `TopologyDescriptionChangedEvent`, and - `TopologyClosedEvent`.""" + `TopologyClosedEvent`. + + .. versionadded:: 3.3 + """ def opened(self, event): """Abstract method to handle a `TopologyOpenedEvent`. @@ -174,7 +250,10 @@ class TopologyListener(_EventListener): class ServerListener(_EventListener): """Abstract base class for server listeners. Handles `ServerOpeningEvent`, `ServerDescriptionChangedEvent`, and - `ServerClosedEvent`.""" + `ServerClosedEvent`. + + .. versionadded:: 3.3 + """ def opened(self, event): """Abstract method to handle a `ServerOpeningEvent`. @@ -405,7 +484,10 @@ class _ServerEvent(object): class ServerDescriptionChangedEvent(_ServerEvent): - """Published when server description changes.""" + """Published when server description changes. + + .. versionadded:: 3.3 + """ __slots__ = ('__previous_description', '__new_description') @@ -416,25 +498,33 @@ class ServerDescriptionChangedEvent(_ServerEvent): @property def previous_description(self): - """The previous server description.""" + """The previous + :class:`~pymongo.server_description.ServerDescription`.""" return self.__previous_description @property def new_description(self): - """The new server description.""" + """The new + :class:`~pymongo.server_description.ServerDescription`.""" return self.__new_description class ServerOpeningEvent(_ServerEvent): - """Published when server is initialized.""" + """Published when server is initialized. + + .. versionadded:: 3.3 + """ class ServerClosedEvent(_ServerEvent): - """Published when server is closed.""" + """Published when server is closed. + + .. versionadded:: 3.3 + """ class TopologyEvent(object): - """Base class for topology description events""" + """Base class for topology description events.""" __slots__ = ('__topology_id') @@ -448,7 +538,10 @@ class TopologyEvent(object): class TopologyDescriptionChangedEvent(TopologyEvent): - """Published when the topology description changes.""" + """Published when the topology description changes. + + .. versionadded:: 3.3 + """ __slots__ = ('__previous_description', '__new_description') @@ -459,25 +552,33 @@ class TopologyDescriptionChangedEvent(TopologyEvent): @property def previous_description(self): - """The old topology description.""" + """The previous + :class:`~pymongo.topology_description.TopologyDescription`.""" return self.__previous_description @property def new_description(self): - """The new topology description.""" + """The new + :class:`~pymongo.topology_description.TopologyDescription`.""" return self.__new_description class TopologyOpenedEvent(TopologyEvent): - """Published when the topology is initialized.""" + """Published when the topology is initialized. + + .. versionadded:: 3.3 + """ class TopologyClosedEvent(TopologyEvent): - """Published when the topology is closed.""" + """Published when the topology is closed. + + .. versionadded:: 3.3 + """ class _ServerHeartbeatEvent(object): - """Base class for server heartbeat events""" + """Base class for server heartbeat events.""" __slots__ = ('__connection_id') @@ -486,17 +587,23 @@ class _ServerHeartbeatEvent(object): @property def connection_id(self): - """"The address (host, port) of the server this heartbeat was sent - to.""" + """The address (host, port) of the server this heartbeat was sent + to.""" return self.__connection_id class ServerHeartbeatStartedEvent(_ServerHeartbeatEvent): - """"Published when a heartbeat is started.""" + """Published when a heartbeat is started. + + .. versionadded:: 3.3 + """ class ServerHeartbeatSucceededEvent(_ServerHeartbeatEvent): - """Fired when the server heartbeat succeeds.""" + """Fired when the server heartbeat succeeds. + + .. versionadded:: 3.3 + """ __slots__ = ('__duration', '__reply') @@ -512,13 +619,16 @@ class ServerHeartbeatSucceededEvent(_ServerHeartbeatEvent): @property def reply(self): - """The command reply.""" + """An instance of :class:`~pymongo.ismaster.IsMaster`.""" return self.__reply class ServerHeartbeatFailedEvent(_ServerHeartbeatEvent): """Fired when the server heartbeat fails, either with an "ok: 0" - or a socket exception.""" + or a socket exception. + + .. versionadded:: 3.3 + """ __slots__ = ('__duration', '__reply') @@ -534,7 +644,7 @@ class ServerHeartbeatFailedEvent(_ServerHeartbeatEvent): @property def reply(self): - """The command reply.""" + """A subclass of :exc:`Exception`.""" return self.__reply diff --git a/pymongo/server_description.py b/pymongo/server_description.py index ba762ec91..e2b29bcaf 100644 --- a/pymongo/server_description.py +++ b/pymongo/server_description.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -"""Represent one server in the topology.""" +"""Represent one server the driver is connected to.""" from bson import EPOCH_NAIVE from pymongo.server_type import SERVER_TYPE @@ -85,10 +85,12 @@ class ServerDescription(object): @property def address(self): + """The address (host, port) of this server.""" return self._address @property def server_type(self): + """The type of this server.""" return self._server_type @property diff --git a/pymongo/topology_description.py b/pymongo/topology_description.py index 7ab0d5954..cc2196795 100644 --- a/pymongo/topology_description.py +++ b/pymongo/topology_description.py @@ -12,7 +12,7 @@ # implied. See the License for the specific language governing # permissions and limitations under the License. -"""Represent the topology of servers.""" +"""Represent a deployment of MongoDB servers.""" from collections import namedtuple @@ -37,7 +37,7 @@ class TopologyDescription(object): max_set_version, max_election_id, topology_settings): - """Represent a topology of servers. + """Representation of a deployment of MongoDB servers. :Parameters: - `topology_type`: initial type @@ -121,11 +121,13 @@ class TopologyDescription(object): self._topology_settings) def server_descriptions(self): - """Dict of (address, ServerDescription).""" + """Dict of (address, + :class:`~pymongo.server_description.ServerDescription`).""" return self._server_descriptions.copy() @property def topology_type(self): + """The type of this topology.""" return self._topology_type @property @@ -215,7 +217,7 @@ class TopologyDescription(object): :Parameters: - `read_preference`: an instance of a read preference from :mod:`~pymongo.read_preferences`. Defaults to - :attr:`~pymongo.ReadPreference.PRIMARY`. + :attr:`~pymongo.read_preferences.ReadPreference.PRIMARY`. .. note:: When connected directly to a single server this method always returns ``True``.