From 84a5a2c8d62339002964adc8ae2367a6115d36cb Mon Sep 17 00:00:00 2001 From: Noah <33094578+coolreader18@users.noreply.github.com> Date: Tue, 16 Feb 2021 18:37:53 -0600 Subject: [PATCH] Add netrc.py from CPython 3.8 --- Lib/netrc.py | 139 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 Lib/netrc.py diff --git a/Lib/netrc.py b/Lib/netrc.py new file mode 100644 index 000000000..f0ae48cfe --- /dev/null +++ b/Lib/netrc.py @@ -0,0 +1,139 @@ +"""An object-oriented interface to .netrc files.""" + +# Module and documentation by Eric S. Raymond, 21 Dec 1998 + +import os, shlex, stat + +__all__ = ["netrc", "NetrcParseError"] + + +class NetrcParseError(Exception): + """Exception raised on syntax errors in the .netrc file.""" + def __init__(self, msg, filename=None, lineno=None): + self.filename = filename + self.lineno = lineno + self.msg = msg + Exception.__init__(self, msg) + + def __str__(self): + return "%s (%s, line %s)" % (self.msg, self.filename, self.lineno) + + +class netrc: + def __init__(self, file=None): + default_netrc = file is None + if file is None: + file = os.path.join(os.path.expanduser("~"), ".netrc") + self.hosts = {} + self.macros = {} + with open(file) as fp: + self._parse(file, fp, default_netrc) + + def _parse(self, file, fp, default_netrc): + lexer = shlex.shlex(fp) + lexer.wordchars += r"""!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~""" + lexer.commenters = lexer.commenters.replace('#', '') + while 1: + # Look for a machine, default, or macdef top-level keyword + saved_lineno = lexer.lineno + toplevel = tt = lexer.get_token() + if not tt: + break + elif tt[0] == '#': + if lexer.lineno == saved_lineno and len(tt) == 1: + lexer.instream.readline() + continue + elif tt == 'machine': + entryname = lexer.get_token() + elif tt == 'default': + entryname = 'default' + elif tt == 'macdef': # Just skip to end of macdefs + entryname = lexer.get_token() + self.macros[entryname] = [] + lexer.whitespace = ' \t' + while 1: + line = lexer.instream.readline() + if not line or line == '\012': + lexer.whitespace = ' \t\r\n' + break + self.macros[entryname].append(line) + continue + else: + raise NetrcParseError( + "bad toplevel token %r" % tt, file, lexer.lineno) + + # We're looking at start of an entry for a named machine or default. + login = '' + account = password = None + self.hosts[entryname] = {} + while 1: + tt = lexer.get_token() + if (tt.startswith('#') or + tt in {'', 'machine', 'default', 'macdef'}): + if password: + self.hosts[entryname] = (login, account, password) + lexer.push_token(tt) + break + else: + raise NetrcParseError( + "malformed %s entry %s terminated by %s" + % (toplevel, entryname, repr(tt)), + file, lexer.lineno) + elif tt == 'login' or tt == 'user': + login = lexer.get_token() + elif tt == 'account': + account = lexer.get_token() + elif tt == 'password': + if os.name == 'posix' and default_netrc: + prop = os.fstat(fp.fileno()) + if prop.st_uid != os.getuid(): + import pwd + try: + fowner = pwd.getpwuid(prop.st_uid)[0] + except KeyError: + fowner = 'uid %s' % prop.st_uid + try: + user = pwd.getpwuid(os.getuid())[0] + except KeyError: + user = 'uid %s' % os.getuid() + raise NetrcParseError( + ("~/.netrc file owner (%s) does not match" + " current user (%s)") % (fowner, user), + file, lexer.lineno) + if (prop.st_mode & (stat.S_IRWXG | stat.S_IRWXO)): + raise NetrcParseError( + "~/.netrc access too permissive: access" + " permissions must restrict access to only" + " the owner", file, lexer.lineno) + password = lexer.get_token() + else: + raise NetrcParseError("bad follower token %r" % tt, + file, lexer.lineno) + + def authenticators(self, host): + """Return a (user, account, password) tuple for given host.""" + if host in self.hosts: + return self.hosts[host] + elif 'default' in self.hosts: + return self.hosts['default'] + else: + return None + + def __repr__(self): + """Dump the class data in the format of a .netrc file.""" + rep = "" + for host in self.hosts.keys(): + attrs = self.hosts[host] + rep += f"machine {host}\n\tlogin {attrs[0]}\n" + if attrs[1]: + rep += f"\taccount {attrs[1]}\n" + rep += f"\tpassword {attrs[2]}\n" + for macro in self.macros.keys(): + rep += f"macdef {macro}\n" + for line in self.macros[macro]: + rep += line + rep += "\n" + return rep + +if __name__ == '__main__': + print(netrc())