Title: netrc module does not handle multiple entries for a single host
Type: enhancement Stage: patch review
Components: Library (Lib) Versions: Python 3.5, Python 2.7
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: Frew Schmidt, Yuri.Bochkarev, berker.peksag, brian.curtin, eric.araujo, luc, r.david.murray, rhettinger, saffroy
Priority: normal Keywords: needs review, patch

Created on 2011-03-06 21:14 by saffroy, last changed 2018-01-16 13:17 by Yuri.Bochkarev.

File name Uploaded Description Edit
netrc.patch saffroy, 2011-05-30 15:09 review
netrc-doc.patch saffroy, 2011-05-30 22:51
issue11416.diff berker.peksag, 2014-03-18 10:56 review
netrc.patch Frew Schmidt, 2016-04-18 22:42
Messages (25)
msg130193 - (view) Author: Jean-Marc Saffroy (saffroy) Date: 2011-03-06 21:14
I have a netrc file with two entries for the same host. The netrc module only returns the last entry.

$ cat > .netrc
        login foo
        password foo

        login bar
        password bar

$ python -c 'import netrc; print netrc.netrc()'
        login 'bar'
        password 'bar'

My Linux ftp clients (ftp, lftp) always use the first entry for a given host. Also lftp can use the password from the proper entry if I supply the host and login.

With the netrc module in Python 2.6.6 (as tested on Debian Squeeze), I can only retrieve the last entry.
msg130618 - (view) Author: Éric Araujo (eric.araujo) * (Python committer) Date: 2011-03-11 21:43
Thanks for the report.  Would you like to work on a patch?  Guidelines are found at
msg130686 - (view) Author: Jean-Marc Saffroy (saffroy) Date: 2011-03-12 16:04
I could spend some time on a small patch.

Here are the changes I would consider:
- netrc.authenticators() could return the first entry matching a given host (now it returns the last)
- it could also accept an extra parameter (with default=None), a login to select the right account/password for a given host
- it could also return a list of entries (tuples) matching the supplied host (now it always returns only one tuple), or a new method could do that

IMHO the last change would make the overall behaviour more consistent (BTW note the plural for "authenticators"), but it could also break existing apps.

msg130689 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2011-03-12 16:45
I think whatever change we make it should be backward compatible, even though that is a bit annoying (especially given the current method name).  So either a parameter that selects the new behavior and defaults to the old, or a new method that returns a list, IMO.
msg132421 - (view) Author: Jean-Marc Saffroy (saffroy) Date: 2011-03-28 21:15
So I finally cooked a little patch for in python 2.6.
The patch extends netrc.authenticators() with an extra parameter to select a login name, but otherwise the behaviour remains the same (still returns the last entry for a given host).

Lightly tested, works for me.
msg132422 - (view) Author: Brian Curtin (brian.curtin) * (Python committer) Date: 2011-03-28 21:20
Can you add your tests to Lib/test/
msg132434 - (view) Author: Jean-Marc Saffroy (saffroy) Date: 2011-03-28 22:27
Good that you mentioned the official tests, they let me see that netrc.hosts is actually part of the API, and my first patch broke it.

Here is an updated patch, with extra tests.
msg137218 - (view) Author: Jean-Marc Saffroy (saffroy) Date: 2011-05-29 20:58
Ping? A patch is available for review.
msg137267 - (view) Author: Éric Araujo (eric.araujo) * (Python committer) Date: 2011-05-30 13:30
Looks good.  One remark: instead of using keys() + getitem in Lib/, you can use this: for host, attrs in self.allhosts.items()
msg137276 - (view) Author: Jean-Marc Saffroy (saffroy) Date: 2011-05-30 14:04
You are suggesting something like this, I suppose?

--- a/Lib/
+++ b/Lib/
@@ -105,8 +105,8 @@ class netrc:
     def __repr__(self):
         """Dump the class data in the format of a .netrc file."""
         rep = ""
-        for host in self.allhosts.keys():
-            for attrs in self.allhosts[host]:
+        for (host, attrlist) in self.allhosts.items():
+            for attrs in attrlist:
                 rep = rep + "machine "+ host + "\n\tlogin " + repr(attrs[0]) + "\n"
                 if attrs[1]:
                     rep = rep + "account " + repr(attrs[1])
msg137277 - (view) Author: Éric Araujo (eric.araujo) * (Python committer) Date: 2011-05-30 14:05
Exactly, minus the parens (not needed).
msg137278 - (view) Author: Jean-Marc Saffroy (saffroy) Date: 2011-05-30 14:14
Patch slightly updated after Eric's comments.
msg137279 - (view) Author: Éric Araujo (eric.araujo) * (Python committer) Date: 2011-05-30 14:18
I’m surprised self.assert_ does not produce a DeprecationWarning (in favor of assertTrue).  The tests should use assertEqual(expected, computed) anyway, to let developers see a useful diff when the test fails.  Could you change this in the tests?
msg137280 - (view) Author: Jean-Marc Saffroy (saffroy) Date: 2011-05-30 14:19
Patch formatting changed to be more review-friendly (looks like MQ-style patch isn't?), otherwise same as 2011-05-30 16:14.
msg137282 - (view) Author: Jean-Marc Saffroy (saffroy) Date: 2011-05-30 14:24
Eric: yes I can look into the asserts, but note I generated and tested my patch from a checkout of 2.6 (see my first report), so maybe that's why I didn't see any warning.
msg137283 - (view) Author: Éric Araujo (eric.araujo) * (Python committer) Date: 2011-05-30 14:32
Ah, okay.  Your patch needs to apply to 2.7 or 3.2, which use assertEqual and not assert_.
msg137288 - (view) Author: Jean-Marc Saffroy (saffroy) Date: 2011-05-30 15:09
Here is a patch against 2.7.
msg137289 - (view) Author: Éric Araujo (eric.araujo) * (Python committer) Date: 2011-05-30 15:11
Great!  Could you expand the docstrings and reST documentation to mention the new behavior?
msg137341 - (view) Author: Jean-Marc Saffroy (saffroy) Date: 2011-05-30 22:51
Additional patch for docstrings and documentation. Applies on top of previous patch.
msg137449 - (view) Author: Éric Araujo (eric.araujo) * (Python committer) Date: 2011-06-01 17:49
Looks good, thanks.
msg138071 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2011-06-10 10:30
This should not be applied until after the patch for #12009 is applied, at which point the test will need to be adjusted.  #12009 completely refactors the netrc test suite.

I wonder if the 'hosts' attribute should be deprecated, but let's leave that discussion for another day :)

Since this requires an API change, it is a feature and can only go into 3.3.  I've adjusted 'versions' accordingly.
msg213229 - (view) Author: Lucas Hoffmann (luc) Date: 2014-03-12 09:21
What is the status of this issue?

On version 3.3 I still can not get more than one entry per host.  The stopping issue #12009 seems to be closed.

(Sorry, if I violate some etiquette by bumping this, just tell me.)
msg213247 - (view) Author: Éric Araujo (eric.araujo) * (Python committer) Date: 2014-03-12 11:02
This patch hasn’t been committed.  The comment before yours says that it needs to be edited a bit.
msg213955 - (view) Author: Berker Peksag (berker.peksag) * (Python committer) Date: 2014-03-18 10:56
I've updated the tests to match the changes in issue 12009 and documentation a bit.
msg263699 - (view) Author: Frew Schmidt (Frew Schmidt) Date: 2016-04-18 22:42
This issue is causing a problem in OfflineIMAP.  I wrote a patch that I think is a little simpler than the existing one, including tests and docs, though honestly I don't care one way or another.  See attached.  Note that the patch is for Python 2.7 but it mostly applies to 3.5 just fine.  Any chance we could get mine or the other one applied?

Date User Action Args
2018-01-16 13:17:37Yuri.Bochkarevsetnosy: + Yuri.Bochkarev
2016-04-18 22:42:57Frew Schmidtsetfiles: + netrc.patch
versions: + Python 2.7
nosy: + Frew Schmidt

messages: + msg263699
2014-03-18 10:56:31berker.peksagsetfiles: + issue11416.diff
versions: + Python 3.5, - Python 3.3
nosy: + berker.peksag

messages: + msg213955
2014-03-12 11:02:02eric.araujosetmessages: + msg213247
2014-03-12 09:21:13lucsetnosy: + luc
messages: + msg213229
2011-06-10 10:30:56r.david.murraysettype: behavior -> enhancement
messages: + msg138071
versions: - Python 2.7, Python 3.2
2011-06-01 17:49:16eric.araujosetmessages: + msg137449
2011-05-30 22:51:07saffroysetfiles: + netrc-doc.patch

messages: + msg137341
2011-05-30 15:11:24eric.araujosetmessages: + msg137289
2011-05-30 15:10:53eric.araujosetfiles: - netrc.patch
2011-05-30 15:10:51eric.araujosetfiles: - netrc.patch
2011-05-30 15:10:48eric.araujosetfiles: - netrc.patch
2011-05-30 15:09:39saffroysetfiles: + netrc.patch

messages: + msg137288
2011-05-30 14:32:18eric.araujosetmessages: + msg137283
2011-05-30 14:24:29saffroysetmessages: + msg137282
2011-05-30 14:19:34saffroysetfiles: + netrc.patch

messages: + msg137280
2011-05-30 14:18:37eric.araujosetmessages: + msg137279
2011-05-30 14:14:01saffroysetfiles: + netrc.patch

messages: + msg137278
2011-05-30 14:05:53eric.araujosetmessages: + msg137277
versions: - Python 3.1
2011-05-30 14:04:35saffroysetmessages: + msg137276
2011-05-30 13:30:57eric.araujosetmessages: + msg137267
2011-05-29 21:10:05brian.curtinsetkeywords: + needs review
stage: patch review
2011-05-29 20:58:17saffroysetmessages: + msg137218
2011-05-29 20:57:47saffroysetfiles: - netrc.patch
2011-03-28 22:27:28saffroysetfiles: + netrc.patch

messages: + msg132434
2011-03-28 21:20:35brian.curtinsetnosy: + brian.curtin
messages: + msg132422
2011-03-28 21:15:41saffroysetfiles: + netrc.patch
keywords: + patch
messages: + msg132421
2011-03-12 16:45:06r.david.murraysetmessages: + msg130689
2011-03-12 16:04:08saffroysetmessages: + msg130686
2011-03-11 21:43:14eric.araujosetnosy: + rhettinger, eric.araujo

messages: + msg130618
versions: + Python 3.1, Python 2.7, Python 3.2, Python 3.3, - Python 2.6
2011-03-06 23:29:05r.david.murraysetnosy: + r.david.murray
2011-03-06 21:14:07saffroycreate