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_--