Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
17 changes: 9 additions & 8 deletions Lib/netrc.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,12 @@ def _read_char(self):
def get_token(self):
if self.pushback:
return self.pushback.pop(0)
token = ""
token = None
fiter = iter(self._read_char, "")
for ch in fiter:
if ch in self.whitespace:
continue
token = ""
if ch == '"':
for ch in fiter:
if ch == '"':
Expand Down Expand Up @@ -96,9 +97,9 @@ def _parse(self, file, fp, default_netrc):
# Look for a machine, default, or macdef top-level keyword
saved_lineno = lexer.lineno
tt = lexer.get_token()
if not tt:
if tt is None:
break
elif tt[0] == '#':
elif tt.startswith('#'):
if lexer.lineno == saved_lineno and len(tt) == 1:
lexer.instream.readline()
continue
Expand Down Expand Up @@ -135,20 +136,20 @@ def _parse(self, file, fp, default_netrc):
while 1:
prev_lineno = lexer.lineno
tt = lexer.get_token()
if tt.startswith('#'):
if tt is not None and tt.startswith('#'):
if lexer.lineno == prev_lineno:
lexer.instream.readline()
continue
if tt in {'', 'machine', 'default', 'macdef'}:
if tt in {None, 'machine', 'default', 'macdef'}:
self.hosts[entryname] = (login, account, password)
lexer.push_token(tt)
break
elif tt == 'login' or tt == 'user':
login = lexer.get_token()
login = lexer.get_token() or ''
elif tt == 'account':
account = lexer.get_token()
account = lexer.get_token() or ''
elif tt == 'password':
password = lexer.get_token()
password = lexer.get_token() or ''
else:
raise NetrcParseError("bad follower token %r" % tt,
file, lexer.lineno)
Expand Down
15 changes: 15 additions & 0 deletions Lib/test/test_netrc.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ def test_optional_tokens(self):
"machine host.domain.com login",
"machine host.domain.com account",
"machine host.domain.com password",
"machine host.domain.com login \"\"",
"machine host.domain.com account \"\"",
"machine host.domain.com password \"\"",
"machine host.domain.com login \"\" account",
"machine host.domain.com login \"\" password",
"machine host.domain.com account \"\" password"
Expand All @@ -74,6 +77,9 @@ def test_optional_tokens(self):
"default login",
"default account",
"default password",
"default login \"\"",
"default account \"\"",
"default password \"\"",
"default login \"\" account",
"default login \"\" password",
"default account \"\" password"
Expand All @@ -82,6 +88,15 @@ def test_optional_tokens(self):
nrc = self.make_nrc(item)
self.assertEqual(nrc.hosts['default'], ('', '', ''))

def test_empty_quoted_token_is_not_eof(self):
data = (
'"" invalid',
'machine host.domain.com "" invalid',

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

also test what happens when you put "" at the end (e.g., machine host.domain.com invalid "") Those should be legitimate cases but they are not tested.

)
for item in data:
with self.subTest(item=item):
self.assertRaises(netrc.NetrcParseError, self.make_nrc, item)

def test_invalid_tokens(self):
data = (
"invalid host.domain.com",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix :mod:`netrc` to distinguish empty quoted tokens from end-of-file, so
malformed files no longer cause the remaining content to be silently ignored.
Loading