Home | History | Annotate | Download | only in python2.7
      1 """An object-oriented interface to .netrc files."""
      2 
      3 # Module and documentation by Eric S. Raymond, 21 Dec 1998
      4 
      5 import os, shlex
      6 
      7 __all__ = ["netrc", "NetrcParseError"]
      8 
      9 
     10 class NetrcParseError(Exception):
     11     """Exception raised on syntax errors in the .netrc file."""
     12     def __init__(self, msg, filename=None, lineno=None):
     13         self.filename = filename
     14         self.lineno = lineno
     15         self.msg = msg
     16         Exception.__init__(self, msg)
     17 
     18     def __str__(self):
     19         return "%s (%s, line %s)" % (self.msg, self.filename, self.lineno)
     20 
     21 
     22 class netrc:
     23     def __init__(self, file=None):
     24         if file is None:
     25             try:
     26                 file = os.path.join(os.environ['HOME'], ".netrc")
     27             except KeyError:
     28                 raise IOError("Could not find .netrc: $HOME is not set")
     29         self.hosts = {}
     30         self.macros = {}
     31         with open(file) as fp:
     32             self._parse(file, fp)
     33 
     34     def _parse(self, file, fp):
     35         lexer = shlex.shlex(fp)
     36         lexer.wordchars += r"""!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~"""
     37         lexer.commenters = lexer.commenters.replace('#', '')
     38         while 1:
     39             # Look for a machine, default, or macdef top-level keyword
     40             toplevel = tt = lexer.get_token()
     41             if not tt:
     42                 break
     43             elif tt[0] == '#':
     44                 # seek to beginning of comment, in case reading the token put
     45                 # us on a new line, and then skip the rest of the line.
     46                 pos = len(tt) + 1
     47                 lexer.instream.seek(-pos, 1)
     48                 lexer.instream.readline()
     49                 continue
     50             elif tt == 'machine':
     51                 entryname = lexer.get_token()
     52             elif tt == 'default':
     53                 entryname = 'default'
     54             elif tt == 'macdef':                # Just skip to end of macdefs
     55                 entryname = lexer.get_token()
     56                 self.macros[entryname] = []
     57                 lexer.whitespace = ' \t'
     58                 while 1:
     59                     line = lexer.instream.readline()
     60                     if not line or line == '\012':
     61                         lexer.whitespace = ' \t\r\n'
     62                         break
     63                     self.macros[entryname].append(line)
     64                 continue
     65             else:
     66                 raise NetrcParseError(
     67                     "bad toplevel token %r" % tt, file, lexer.lineno)
     68 
     69             # We're looking at start of an entry for a named machine or default.
     70             login = ''
     71             account = password = None
     72             self.hosts[entryname] = {}
     73             while 1:
     74                 tt = lexer.get_token()
     75                 if (tt.startswith('#') or
     76                     tt in {'', 'machine', 'default', 'macdef'}):
     77                     if password:
     78                         self.hosts[entryname] = (login, account, password)
     79                         lexer.push_token(tt)
     80                         break
     81                     else:
     82                         raise NetrcParseError(
     83                             "malformed %s entry %s terminated by %s"
     84                             % (toplevel, entryname, repr(tt)),
     85                             file, lexer.lineno)
     86                 elif tt == 'login' or tt == 'user':
     87                     login = lexer.get_token()
     88                 elif tt == 'account':
     89                     account = lexer.get_token()
     90                 elif tt == 'password':
     91                     password = lexer.get_token()
     92                 else:
     93                     raise NetrcParseError("bad follower token %r" % tt,
     94                                           file, lexer.lineno)
     95 
     96     def authenticators(self, host):
     97         """Return a (user, account, password) tuple for given host."""
     98         if host in self.hosts:
     99             return self.hosts[host]
    100         elif 'default' in self.hosts:
    101             return self.hosts['default']
    102         else:
    103             return None
    104 
    105     def __repr__(self):
    106         """Dump the class data in the format of a .netrc file."""
    107         rep = ""
    108         for host in self.hosts.keys():
    109             attrs = self.hosts[host]
    110             rep = rep + "machine "+ host + "\n\tlogin " + repr(attrs[0]) + "\n"
    111             if attrs[1]:
    112                 rep = rep + "account " + repr(attrs[1])
    113             rep = rep + "\tpassword " + repr(attrs[2]) + "\n"
    114         for macro in self.macros.keys():
    115             rep = rep + "macdef " + macro + "\n"
    116             for line in self.macros[macro]:
    117                 rep = rep + line
    118             rep = rep + "\n"
    119         return rep
    120 
    121 if __name__ == '__main__':
    122     print netrc()
    123