From a078115434c58b2dce4cec82f3c2a90711166d60 Mon Sep 17 00:00:00 2001
From: Antoine Pitrou <solipsis@pitrou.net>
Date: Fri, 5 Nov 2010 19:16:37 +0000
Subject: Issue #10282: Add a `nntp_implementation` attribute to NNTP objects.

---
 Doc/library/nntplib.rst  | 46 ++++++++++++++++++++++++++++++++++------------
 Lib/nntplib.py           |  3 +++
 Lib/test/test_nntplib.py |  2 ++
 Misc/NEWS                |  2 ++
 4 files changed, 41 insertions(+), 12 deletions(-)

diff --git a/Doc/library/nntplib.rst b/Doc/library/nntplib.rst
index 3f3995f..555d735 100644
--- a/Doc/library/nntplib.rst
+++ b/Doc/library/nntplib.rst
@@ -54,7 +54,7 @@ The module itself defines the following classes:
 
 .. class:: NNTP(host, port=119, user=None, password=None, readermode=None, usenetrc=True, [timeout])
 
-   Return a new instance of the :class:`NNTP` class, representing a connection
+   Return a new :class:`NNTP` object, representing a connection
    to the NNTP server running on host *host*, listening at port *port*.
    An optional *timeout* can be specified for the socket connection.
    If the optional *user* and *password* are provided, or if suitable
@@ -111,19 +111,41 @@ The module itself defines the following classes:
 NNTP Objects
 ------------
 
-:class:`NNTP` instances have the following methods.  The *response* that is
-returned as the first item in the return tuple of almost all methods is the
-server's response: a string beginning with a three-digit code. If the server's
-response indicates an error, the method raises one of the above exceptions.
+When connected, :class:`NNTP` objects support the following methods and
+attributes.
 
-.. note::
-   Many of the following methods take an optional keyword-only argument *file*.
-   When the *file* argument is supplied, it must be either a :term:`file object`
-   opened for binary writing, or the name of an on-disk file to be written to.
-   The method will then write any data returned by the server (except for the
-   response line and the terminating dot) to the file; any list of lines,
-   tuples or objects that the method normally returns will be empty.
+Attributes
+^^^^^^^^^^
 
+.. attribute:: NNTP.nntp_version
+
+   An integer representing the version of the NNTP protocol supported by the
+   server.  In practice, this should be ``2`` for servers advertising
+   :rfc:`3977` compliance and ``1`` for others.
+
+   .. versionadded:: 3.2
+
+.. attribute:: NNTP.nntp_implementation
+
+   A string describing the software name and version of the NNTP server,
+   or :const:`None` if not advertised by the server.
+
+   .. versionadded:: 3.2
+
+Methods
+^^^^^^^
+
+The *response* that is returned as the first item in the return tuple of almost
+all methods is the server's response: a string beginning with a three-digit
+code.  If the server's response indicates an error, the method raises one of
+the above exceptions.
+
+Many of the following methods take an optional keyword-only argument *file*.
+When the *file* argument is supplied, it must be either a :term:`file object`
+opened for binary writing, or the name of an on-disk file to be written to.
+The method will then write any data returned by the server (except for the
+response line and the terminating dot) to the file; any list of lines,
+tuples or objects that the method normally returns will be empty.
 
 .. versionchanged:: 3.2
    Many of the following methods have been reworked and fixed, which makes
diff --git a/Lib/nntplib.py b/Lib/nntplib.py
index d5786e2..a09c065 100644
--- a/Lib/nntplib.py
+++ b/Lib/nntplib.py
@@ -354,6 +354,7 @@ class _NNTPBase:
 
         # Inquire about capabilities (RFC 3977)
         self.nntp_version = 1
+        self.nntp_implementation = None
         try:
             resp, caps = self.capabilities()
         except NNTPPermanentError:
@@ -365,6 +366,8 @@ class _NNTPBase:
                 # The server can advertise several supported versions,
                 # choose the highest.
                 self.nntp_version = max(map(int, caps['VERSION']))
+            if 'IMPLEMENTATION' in caps:
+                self.nntp_implementation = ' '.join(caps['IMPLEMENTATION'])
 
     def getwelcome(self):
         """Get the welcome message from the server
diff --git a/Lib/test/test_nntplib.py b/Lib/test/test_nntplib.py
index f9a4cdc..6380084 100644
--- a/Lib/test/test_nntplib.py
+++ b/Lib/test/test_nntplib.py
@@ -949,6 +949,7 @@ class NNTPv1Tests(NNTPv1v2TestsMixin, MockedNNTPTestsMixin, unittest.TestCase):
         caps = self.server.getcapabilities()
         self.assertEqual(caps, {})
         self.assertEqual(self.server.nntp_version, 1)
+        self.assertEqual(self.server.nntp_implementation, None)
 
 
 class NNTPv2Tests(NNTPv1v2TestsMixin, MockedNNTPTestsMixin, unittest.TestCase):
@@ -971,6 +972,7 @@ class NNTPv2Tests(NNTPv1v2TestsMixin, MockedNNTPTestsMixin, unittest.TestCase):
             'READER': [],
             })
         self.assertEqual(self.server.nntp_version, 3)
+        self.assertEqual(self.server.nntp_implementation, 'INN 2.5.1')
 
 
 class MiscTests(unittest.TestCase):
diff --git a/Misc/NEWS b/Misc/NEWS
index 1910651..4608b8d 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -65,6 +65,8 @@ Core and Builtins
 Library
 -------
 
+- Issue #10282: Add a ``nntp_implementation`` attribute to NNTP objects.
+
 - Issue #10283: Add a ``group_pattern`` argument to NNTP.list().
 
 - Issue #10155: Add IISCGIHandler to wsgiref.handlers to support IIS
-- 
cgit v0.12