gpk@bell-labs.com: [Python-bugs-list] netrc module has bad error handling (PR#265)
lannert@uni-duesseldorf.de
lannert@uni-duesseldorf.de
Wed, 5 Apr 100 13:45:09 +0200 (MEST)
--ELM954935109-23611-0_
Content-Type: text/plain; charset=US-ASCII
Content-Transfer-Encoding: 7bit
Sorry to keep bothering you ...
"Eric S. Raymond" wrote:
> Zounds. You're right, this can't have worked. I guess nobody has tried to
> parse macdefs yet!
Now that I have, I get the impression that netrc.py's idea of a macdef
is quite unlike the idea ftp has. Obviously macdef's are *not* toplevel
entries (a macdef before any machine entry was simply not found when
I entered "$ macname") but belong to the preceding machine entry.
Different macros with the same name but for different machines are handled
by (my!) ftp separately. Therefore I recklessly hacked netrc.py to make
it accept macdef's (the whitespace fix, BTW, wasn't sufficient) and put
them into self.macros indexed by hostname _and_ macroname. IMHO this is
a change that doesn't break anything that did work ;-) with the previous
version.
Although it would seem more logical to put macro definitions into the
corresponding host entries, I chose not to because (a) this would be
an interface change (what did I hear about revolution? :) and (b) this
whole macdef feature is obviously rarely used (except by myself).
Detlef
--ELM954935109-23611-0_
Content-Type: text/plain; charset=ISO-8859-1
Content-Disposition: attachment; filename=netrc.py
Content-Description: /tmp/netrc.py
Content-Transfer-Encoding: 7bit
"""An object-oriented interface to .netrc files."""
# Module and documentation by Eric S. Raymond, 21 Dec 1998
# Recklessly hacked and macdefs fixed by Detlef Lannert, 05 Apr 2000
import os, shlex
import string # not from Python 1.6 onwards
class netrc:
def __init__(self, file=None):
if not file:
file = os.path.join(os.environ['HOME'], ".netrc")
fp = open(file)
self.hosts = {}
self.macros = {} # indexed by hostnames
lexer = shlex.shlex(fp)
lexer.wordchars = lexer.wordchars + '.-'
while 1:
# Look for a machine or default top-level keyword
nexttoken = lexer.get_token()
if nexttoken in ('', None):
break
elif nexttoken == 'machine':
entryname = lexer.get_token()
elif nexttoken == 'default':
entryname = 'default'
elif nexttoken == 'macdef':
# this is a toplevel macdef; what the heck is it good for??
entryname = '' # put it into self.macros['']
lexer.push_token(nexttoken)
else:
raise SyntaxError, "bad toplevel token %s, file %s, line %d" \
% (nexttoken, file, lexer.lineno)
# We're looking at start of an entry for a named machine or default.
login = account = password = None
macdefs = {}
while 1:
nexttoken = lexer.get_token()
if nexttoken in ('machine', 'default', ''):
if (login and not password) or (password and not login):
# macdef-only entries are acceptable!
raise SyntaxError(
"incomplete %s entry terminated by %s"
% (`entryname`, nexttoken or "EOF"))
if login:
self.hosts[entryname] = (login, account, password)
if macdefs:
self.macros[entryname] = macdefs
lexer.push_token(nexttoken)
break
elif nexttoken in ('login', 'user'):
login = lexer.get_token()
elif nexttoken == 'account':
account = lexer.get_token()
elif nexttoken == 'password':
password = lexer.get_token()
elif nexttoken == 'macdef':
macroname = lexer.get_token()
macro = []
while 1: # macro continues until empty line
line = lexer.instream.readline()
if not line or line == "\n":
break
macro.append(line)
macdefs[macroname] = macro
else:
raise SyntaxError(
"bad follower token %s, file %s, line %d"
% (nexttoken, file, lexer.lineno))
def authenticators(self, host):
"""Return a (user, account, password) tuple for given host."""
if self.hosts.has_key(host):
return self.hosts[host]
elif self.hosts.has_key('default'):
return self.hosts['default']
else:
return None
def __repr__(self):
"""Dump the class data in the format of a .netrc file."""
result = []
# First process the mysterious top-level macdef's:
host = "" # dummy entry
for macroname in self.macros.get(host, {}).keys():
result.append("\tmacdef %s\n" % macroname)
result.extend(self.macros[host][macroname])
result.append("\n")
# Now for the machines (and the optional default entry):
for host in self.hosts.keys():
login, account, password = self.hosts[host]
result.append("machine %s \n\tlogin %s\n" % (host, login))
if account:
result.append("\taccount %s\n" % account)
result.append("\tpassword %s\n" % password)
for macroname in self.macros.get(host, {}).keys():
result.append("\tmacdef %s\n" % macroname)
result.extend(self.macros[host][macroname])
result.append("\n")
# That's it ...
return string.join(result, "")
#return "".join(result) # Python 1.6?
def test():
import sys
if len(sys.argv) > 1:
file = sys.argv[1]
else:
file = ""
n = netrc(file)
print "hosts:", `n.hosts`
print "macros:", `n.macros`
print n
if __name__ == '__main__':
#test()
print netrc()
--ELM954935109-23611-0_--