Skip to content

Commit 84a5a2c

Browse files
committed
Add netrc.py from CPython 3.8
1 parent e6d9af4 commit 84a5a2c

1 file changed

Lines changed: 139 additions & 0 deletions

File tree

Lib/netrc.py

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
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, stat
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+
default_netrc = file is None
25+
if file is None:
26+
file = os.path.join(os.path.expanduser("~"), ".netrc")
27+
self.hosts = {}
28+
self.macros = {}
29+
with open(file) as fp:
30+
self._parse(file, fp, default_netrc)
31+
32+
def _parse(self, file, fp, default_netrc):
33+
lexer = shlex.shlex(fp)
34+
lexer.wordchars += r"""!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~"""
35+
lexer.commenters = lexer.commenters.replace('#', '')
36+
while 1:
37+
# Look for a machine, default, or macdef top-level keyword
38+
saved_lineno = lexer.lineno
39+
toplevel = tt = lexer.get_token()
40+
if not tt:
41+
break
42+
elif tt[0] == '#':
43+
if lexer.lineno == saved_lineno and len(tt) == 1:
44+
lexer.instream.readline()
45+
continue
46+
elif tt == 'machine':
47+
entryname = lexer.get_token()
48+
elif tt == 'default':
49+
entryname = 'default'
50+
elif tt == 'macdef': # Just skip to end of macdefs
51+
entryname = lexer.get_token()
52+
self.macros[entryname] = []
53+
lexer.whitespace = ' \t'
54+
while 1:
55+
line = lexer.instream.readline()
56+
if not line or line == '\012':
57+
lexer.whitespace = ' \t\r\n'
58+
break
59+
self.macros[entryname].append(line)
60+
continue
61+
else:
62+
raise NetrcParseError(
63+
"bad toplevel token %r" % tt, file, lexer.lineno)
64+
65+
# We're looking at start of an entry for a named machine or default.
66+
login = ''
67+
account = password = None
68+
self.hosts[entryname] = {}
69+
while 1:
70+
tt = lexer.get_token()
71+
if (tt.startswith('#') or
72+
tt in {'', 'machine', 'default', 'macdef'}):
73+
if password:
74+
self.hosts[entryname] = (login, account, password)
75+
lexer.push_token(tt)
76+
break
77+
else:
78+
raise NetrcParseError(
79+
"malformed %s entry %s terminated by %s"
80+
% (toplevel, entryname, repr(tt)),
81+
file, lexer.lineno)
82+
elif tt == 'login' or tt == 'user':
83+
login = lexer.get_token()
84+
elif tt == 'account':
85+
account = lexer.get_token()
86+
elif tt == 'password':
87+
if os.name == 'posix' and default_netrc:
88+
prop = os.fstat(fp.fileno())
89+
if prop.st_uid != os.getuid():
90+
import pwd
91+
try:
92+
fowner = pwd.getpwuid(prop.st_uid)[0]
93+
except KeyError:
94+
fowner = 'uid %s' % prop.st_uid
95+
try:
96+
user = pwd.getpwuid(os.getuid())[0]
97+
except KeyError:
98+
user = 'uid %s' % os.getuid()
99+
raise NetrcParseError(
100+
("~/.netrc file owner (%s) does not match"
101+
" current user (%s)") % (fowner, user),
102+
file, lexer.lineno)
103+
if (prop.st_mode & (stat.S_IRWXG | stat.S_IRWXO)):
104+
raise NetrcParseError(
105+
"~/.netrc access too permissive: access"
106+
" permissions must restrict access to only"
107+
" the owner", file, lexer.lineno)
108+
password = lexer.get_token()
109+
else:
110+
raise NetrcParseError("bad follower token %r" % tt,
111+
file, lexer.lineno)
112+
113+
def authenticators(self, host):
114+
"""Return a (user, account, password) tuple for given host."""
115+
if host in self.hosts:
116+
return self.hosts[host]
117+
elif 'default' in self.hosts:
118+
return self.hosts['default']
119+
else:
120+
return None
121+
122+
def __repr__(self):
123+
"""Dump the class data in the format of a .netrc file."""
124+
rep = ""
125+
for host in self.hosts.keys():
126+
attrs = self.hosts[host]
127+
rep += f"machine {host}\n\tlogin {attrs[0]}\n"
128+
if attrs[1]:
129+
rep += f"\taccount {attrs[1]}\n"
130+
rep += f"\tpassword {attrs[2]}\n"
131+
for macro in self.macros.keys():
132+
rep += f"macdef {macro}\n"
133+
for line in self.macros[macro]:
134+
rep += line
135+
rep += "\n"
136+
return rep
137+
138+
if __name__ == '__main__':
139+
print(netrc())

0 commit comments

Comments
 (0)