Merge lp:~mbp/bzr/whoami into lp:bzr/2.3

Proposed by Martin Pool
Status: Merged
Approved by: Vincent Ladeuil
Approved revision: no longer in the source branch.
Merged at revision: 5636
Proposed branch: lp:~mbp/bzr/whoami
Merge into: lp:bzr/2.3
Diff against target: 180 lines (+115/-5)
3 files modified
bzrlib/config.py (+86/-5)
bzrlib/tests/test_config.py (+23/-0)
doc/en/release-notes/bzr-2.3.txt (+6/-0)
To merge this branch: bzr merge lp:~mbp/bzr/whoami
Reviewer Review Type Date Requested Status
Vincent Ladeuil Needs Fixing
Review via email: mp+55679@code.launchpad.net

Commit message

infer default user name from /etc/mailname on unix (bug 616878)

Description of the change

This addresses bug 616878 by making bzr automatically infer an email address if /etc/mailname is set. This tries to get a balance between on the one hand saving people from committing lots of work as joe@localhost, and on the other hand letting them commit without needing manual setup. Basically it's a partial reversion and then readjustment for the fix for bug 549310.

To post a comment you must log in.
Revision history for this message
Vincent Ladeuil (vila) wrote :

You just got a lot of positive karma from admins all over the planet ;-D

94 + # we try utf-8 first, because on many variants (like Linux),
95 + # /etc/passwd "should" be in utf-8, and because it's unlikely to give
96 + # false positives. (many users will have their user encoding set to
97 + # latin-1, which cannot raise UnicodeError.)

You will make savannah users happy with that, I think there is even a bug for that https://bugs.launchpad.net/bzr/+bug/659763 .

I think you could easily add more tests for _auto_user_id() and _get_default_mail_domain() by adding optional parameters to inject whatever test files you need (/etc/passwd (or getuid(), pwd.getpwuid() whatever), /etc/mailname, etc)

That's a tweak

review: Needs Fixing
Revision history for this message
Martin Pool (mbp) wrote :

The unicode code is not originally mine; it's resurrected from I think
mgz. So if that bug existed before, it probably still exists with
this patch...

Revision history for this message
Vincent Ladeuil (vila) wrote :

@poolie: As I understood it from bug #659763, the issue was that we were using an ASCII encoding to interpret /etc/passwd instead of using utf-8, so I think your patch *addresses* the bug.

Revision history for this message
Martin Pool (mbp) wrote :

sent to pqm by email

Revision history for this message
Martin Pool (mbp) wrote :

sent to pqm by email

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'bzrlib/config.py'
2--- bzrlib/config.py 2011-01-12 18:12:58 +0000
3+++ bzrlib/config.py 2011-04-01 03:15:39 +0000
4@@ -63,6 +63,7 @@
5 """
6
7 import os
8+import string
9 import sys
10
11 from bzrlib import commands
12@@ -271,21 +272,21 @@
13 the concrete policy type is checked, and finally
14 $EMAIL is examined.
15 If no username can be found, errors.NoWhoami exception is raised.
16-
17- TODO: Check it's reasonably well-formed.
18 """
19 v = os.environ.get('BZR_EMAIL')
20 if v:
21 return v.decode(osutils.get_user_encoding())
22-
23 v = self._get_user_id()
24 if v:
25 return v
26-
27 v = os.environ.get('EMAIL')
28 if v:
29 return v.decode(osutils.get_user_encoding())
30-
31+ name, email = _auto_user_id()
32+ if name and email:
33+ return '%s <%s>' % (name, email)
34+ elif email:
35+ return email
36 raise errors.NoWhoami()
37
38 def ensure_username(self):
39@@ -1213,6 +1214,86 @@
40 return os.path.expanduser('~/.cache')
41
42
43+def _get_default_mail_domain():
44+ """If possible, return the assumed default email domain.
45+
46+ :returns: string mail domain, or None.
47+ """
48+ if sys.platform == 'win32':
49+ # No implementation yet; patches welcome
50+ return None
51+ try:
52+ f = open('/etc/mailname')
53+ except (IOError, OSError), e:
54+ return None
55+ try:
56+ domain = f.read().strip()
57+ return domain
58+ finally:
59+ f.close()
60+
61+
62+def _auto_user_id():
63+ """Calculate automatic user identification.
64+
65+ :returns: (realname, email), either of which may be None if they can't be
66+ determined.
67+
68+ Only used when none is set in the environment or the id file.
69+
70+ This only returns an email address if we can be fairly sure the
71+ address is reasonable, ie if /etc/mailname is set on unix.
72+
73+ This doesn't use the FQDN as the default domain because that may be
74+ slow, and it doesn't use the hostname alone because that's not normally
75+ a reasonable address.
76+ """
77+ if sys.platform == 'win32':
78+ # No implementation to reliably determine Windows default mail
79+ # address; please add one.
80+ return None, None
81+
82+ default_mail_domain = _get_default_mail_domain()
83+ if not default_mail_domain:
84+ return None, None
85+
86+ import pwd
87+ uid = os.getuid()
88+ try:
89+ w = pwd.getpwuid(uid)
90+ except KeyError:
91+ mutter('no passwd entry for uid %d?' % uid)
92+ return None, None
93+
94+ # we try utf-8 first, because on many variants (like Linux),
95+ # /etc/passwd "should" be in utf-8, and because it's unlikely to give
96+ # false positives. (many users will have their user encoding set to
97+ # latin-1, which cannot raise UnicodeError.)
98+ try:
99+ gecos = w.pw_gecos.decode('utf-8')
100+ encoding = 'utf-8'
101+ except UnicodeError:
102+ try:
103+ encoding = osutils.get_user_encoding()
104+ gecos = w.pw_gecos.decode(encoding)
105+ except UnicodeError, e:
106+ mutter("cannot decode passwd entry %s" % w)
107+ return None, None
108+ try:
109+ username = w.pw_name.decode(encoding)
110+ except UnicodeError, e:
111+ mutter("cannot decode passwd entry %s" % w)
112+ return None, None
113+
114+ comma = gecos.find(',')
115+ if comma == -1:
116+ realname = gecos
117+ else:
118+ realname = gecos[:comma]
119+
120+ return realname, (username + '@' + default_mail_domain)
121+
122+
123 def parse_username(username):
124 """Parse e-mail username and return a (name, address) tuple."""
125 match = re.match(r'(.*?)\s*<?([\w+.-]+@[\w+.-]+)>?', username)
126
127=== modified file 'bzrlib/tests/test_config.py'
128--- bzrlib/tests/test_config.py 2011-02-22 09:49:45 +0000
129+++ bzrlib/tests/test_config.py 2011-04-01 03:15:39 +0000
130@@ -41,6 +41,7 @@
131 )
132 from bzrlib.tests import (
133 features,
134+ TestSkipped,
135 scenarios,
136 )
137 from bzrlib.util.configobj import configobj
138@@ -2237,3 +2238,25 @@
139 # test_user_prompted ?
140 class TestAuthenticationRing(tests.TestCaseWithTransport):
141 pass
142+
143+
144+class TestAutoUserId(tests.TestCase):
145+ """Test inferring an automatic user name."""
146+
147+ def test_auto_user_id(self):
148+ """Automatic inference of user name.
149+
150+ This is a bit hard to test in an isolated way, because it depends on
151+ system functions that go direct to /etc or perhaps somewhere else.
152+ But it's reasonable to say that on Unix, with an /etc/mailname, we ought
153+ to be able to choose a user name with no configuration.
154+ """
155+ if sys.platform == 'win32':
156+ raise TestSkipped("User name inference not implemented on win32")
157+ realname, address = config._auto_user_id()
158+ if os.path.exists('/etc/mailname'):
159+ self.assertTrue(realname)
160+ self.assertTrue(address)
161+ else:
162+ self.assertEquals((None, None), (realname, address))
163+
164
165=== modified file 'doc/en/release-notes/bzr-2.3.txt'
166--- doc/en/release-notes/bzr-2.3.txt 2011-04-01 01:37:43 +0000
167+++ doc/en/release-notes/bzr-2.3.txt 2011-04-01 03:15:39 +0000
168@@ -39,6 +39,12 @@
169 .. Fixes for situations where bzr would previously crash or give incorrect
170 or undesirable results.
171
172+* Bazaar now infers the default user email address on Unix from the local
173+ account name plus the contents of ``/etc/mailname`` if that file exists.
174+ In particular, this means that committing as root through etckeeper will
175+ normally not require running ``bzr whoami`` first.
176+ (Martin Pool, #616878)
177+
178 * When reporting a crash without apport, don't print the full list of
179 plugins because it's often too long.
180 (Martin Pool, #716389)

Subscribers

People subscribed via source and target branches