Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
cpython
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
cpython
Commits
0bcbfa43
Commit
0bcbfa43
authored
Sep 26, 2019
by
Michael Felt
Committed by
Tal Einat
Sep 26, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
bpo-28009: Fix uuid.uuid1() and uuid.get_node() on AIX (GH-8672)
parent
9f77268f
Changes
3
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
177 additions
and
93 deletions
+177
-93
Lib/test/test_uuid.py
Lib/test/test_uuid.py
+50
-21
Lib/uuid.py
Lib/uuid.py
+123
-72
Misc/NEWS.d/next/Library/2018-08-04-12-26-11.bpo-28009.4JcHZb.rst
...S.d/next/Library/2018-08-04-12-26-11.bpo-28009.4JcHZb.rst
+4
-0
No files found.
Lib/test/test_uuid.py
View file @
0bcbfa43
import
unittest
.mock
import
unittest
from
test
import
support
from
test
import
support
import
builtins
import
builtins
import
contextlib
import
contextlib
...
@@ -15,7 +15,6 @@ from unittest import mock
...
@@ -15,7 +15,6 @@ from unittest import mock
py_uuid
=
support
.
import_fresh_module
(
'uuid'
,
blocked
=
[
'_uuid'
])
py_uuid
=
support
.
import_fresh_module
(
'uuid'
,
blocked
=
[
'_uuid'
])
c_uuid
=
support
.
import_fresh_module
(
'uuid'
,
fresh
=
[
'_uuid'
])
c_uuid
=
support
.
import_fresh_module
(
'uuid'
,
fresh
=
[
'_uuid'
])
def
importable
(
name
):
def
importable
(
name
):
try
:
try
:
__import__
(
name
)
__import__
(
name
)
...
@@ -459,7 +458,7 @@ class BaseTestUUID:
...
@@ -459,7 +458,7 @@ class BaseTestUUID:
# uuid.getnode to fall back on uuid._random_getnode, which will
# uuid.getnode to fall back on uuid._random_getnode, which will
# generate a valid value.
# generate a valid value.
too_large_getter
=
lambda
:
1
<<
48
too_large_getter
=
lambda
:
1
<<
48
with
unittest
.
mock
.
patch
.
multiple
(
with
mock
.
patch
.
multiple
(
self
.
uuid
,
self
.
uuid
,
_node
=
None
,
# Ignore any cached node value.
_node
=
None
,
# Ignore any cached node value.
_GETTERS
=
[
too_large_getter
],
_GETTERS
=
[
too_large_getter
],
...
@@ -538,7 +537,7 @@ class BaseTestUUID:
...
@@ -538,7 +537,7 @@ class BaseTestUUID:
f
=
self
.
uuid
.
_generate_time_safe
f
=
self
.
uuid
.
_generate_time_safe
if
f
is
None
:
if
f
is
None
:
self
.
skipTest
(
'need uuid._generate_time_safe'
)
self
.
skipTest
(
'need uuid._generate_time_safe'
)
with
unittest
.
mock
.
patch
.
object
(
self
.
uuid
,
'_generate_time_safe'
,
with
mock
.
patch
.
object
(
self
.
uuid
,
'_generate_time_safe'
,
lambda
:
(
f
()[
0
],
safe_value
)):
lambda
:
(
f
()[
0
],
safe_value
)):
yield
yield
...
@@ -674,26 +673,56 @@ class TestUUIDWithExtModule(BaseTestUUID, unittest.TestCase):
...
@@ -674,26 +673,56 @@ class TestUUIDWithExtModule(BaseTestUUID, unittest.TestCase):
class
BaseTestInternals
:
class
BaseTestInternals
:
_uuid
=
py_uuid
_uuid
=
py_uuid
@
unittest
.
skipUnless
(
os
.
name
==
'posix'
,
'requires Posix'
)
def
test_find_mac
(
self
):
def
test_find_under_heading
(
self
):
data
=
'''
\
Name Mtu Network Address Ipkts Ierrs Opkts Oerrs Coll
en0 1500 link#2 fe.ad.c.1.23.4 1714807956 0 711348489 0 0
01:00:5e:00:00:01
en0 1500 192.168.129 x071 1714807956 0 711348489 0 0
224.0.0.1
en0 1500 192.168.90 x071 1714807956 0 711348489 0 0
224.0.0.1
'''
def
mock_get_command_stdout
(
command
,
args
):
return
io
.
BytesIO
(
data
.
encode
())
# The above data is from AIX - with '.' as _MAC_DELIM and strings
# shorter than 17 bytes (no leading 0). (_MAC_OMITS_LEADING_ZEROES=True)
with
mock
.
patch
.
multiple
(
self
.
uuid
,
_MAC_DELIM
=
b'.'
,
_MAC_OMITS_LEADING_ZEROES
=
True
,
_get_command_stdout
=
mock_get_command_stdout
):
mac
=
self
.
uuid
.
_find_mac_under_heading
(
command
=
'netstat'
,
args
=
'-ian'
,
heading
=
b'Address'
,
)
self
.
assertEqual
(
mac
,
0xfead0c012304
)
def
test_find_mac_near_keyword
(
self
):
# key and value are on the same line
data
=
'''
data
=
'''
fake
hwaddr
fake
Link encap:UNSPEC hwaddr 00-00
cscotun0 Link encap:UNSPEC HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
cscotun0 Link encap:UNSPEC HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
eth0 Link encap:Ethernet HWaddr 12:34:56:78:90:ab
eth0 Link encap:Ethernet HWaddr 12:34:56:78:90:ab
'''
'''
popen
=
unittest
.
mock
.
MagicMock
()
def
mock_get_command_stdout
(
command
,
args
):
popen
.
stdout
=
io
.
BytesIO
(
data
.
encode
())
return
io
.
BytesIO
(
data
.
encode
())
with
unittest
.
mock
.
patch
.
object
(
shutil
,
'which'
,
# The above data will only be parsed properly on non-AIX unixes.
return_value
=
'/sbin/ifconfig'
):
with
mock
.
patch
.
multiple
(
self
.
uuid
,
with
unittest
.
mock
.
patch
.
object
(
subprocess
,
'Popen'
,
_MAC_DELIM
=
b':'
,
return_value
=
popen
):
_MAC_OMITS_LEADING_ZEROES
=
False
,
mac
=
self
.
uuid
.
_find_mac
(
_get_command_stdout
=
mock_get_command_stdout
):
mac
=
self
.
uuid
.
_find_mac_near_keyword
(
command
=
'ifconfig'
,
command
=
'ifconfig'
,
args
=
''
,
args
=
''
,
hw_identifier
s
=
[
b'hwaddr'
],
keyword
s
=
[
b'hwaddr'
],
get
_index
=
lambda
x
:
x
+
1
,
get_word
_index
=
lambda
x
:
x
+
1
,
)
)
self
.
assertEqual
(
mac
,
0x1234567890ab
)
self
.
assertEqual
(
mac
,
0x1234567890ab
)
...
...
Lib/uuid.py
View file @
0bcbfa43
...
@@ -59,6 +59,12 @@ _DARWIN = platform.system() == 'Darwin'
...
@@ -59,6 +59,12 @@ _DARWIN = platform.system() == 'Darwin'
_LINUX
=
platform
.
system
()
==
'Linux'
_LINUX
=
platform
.
system
()
==
'Linux'
_WINDOWS
=
platform
.
system
()
==
'Windows'
_WINDOWS
=
platform
.
system
()
==
'Windows'
_MAC_DELIM
=
b':'
_MAC_OMITS_LEADING_ZEROES
=
False
if
_AIX
:
_MAC_DELIM
=
b'.'
_MAC_OMITS_LEADING_ZEROES
=
True
RESERVED_NCS
,
RFC_4122
,
RESERVED_MICROSOFT
,
RESERVED_FUTURE
=
[
RESERVED_NCS
,
RFC_4122
,
RESERVED_MICROSOFT
,
RESERVED_FUTURE
=
[
'reserved for NCS compatibility'
,
'specified in RFC 4122'
,
'reserved for NCS compatibility'
,
'specified in RFC 4122'
,
'reserved for Microsoft compatibility'
,
'reserved for future definition'
]
'reserved for Microsoft compatibility'
,
'reserved for future definition'
]
...
@@ -347,12 +353,14 @@ class UUID:
...
@@ -347,12 +353,14 @@ class UUID:
if
self
.
variant
==
RFC_4122
:
if
self
.
variant
==
RFC_4122
:
return
int
((
self
.
int
>>
76
)
&
0xf
)
return
int
((
self
.
int
>>
76
)
&
0xf
)
def
_popen
(
command
,
*
args
):
import
os
,
shutil
,
subprocess
def
_get_command_stdout
(
command
,
*
args
):
executable
=
shutil
.
which
(
command
)
import
io
,
os
,
shutil
,
subprocess
if
executable
is
None
:
path
=
os
.
pathsep
.
join
((
'/sbin'
,
'/usr/sbin'
))
try
:
executable
=
shutil
.
which
(
command
,
path
=
path
)
path_dirs
=
os
.
environ
.
get
(
'PATH'
,
os
.
defpath
).
split
(
os
.
pathsep
)
path_dirs
.
extend
([
'/sbin'
,
'/usr/sbin'
])
executable
=
shutil
.
which
(
command
,
path
=
os
.
pathsep
.
join
(
path_dirs
))
if
executable
is
None
:
if
executable
is
None
:
return
None
return
None
# LC_ALL=C to ensure English output, stderr=DEVNULL to prevent output
# LC_ALL=C to ensure English output, stderr=DEVNULL to prevent output
...
@@ -364,7 +372,13 @@ def _popen(command, *args):
...
@@ -364,7 +372,13 @@ def _popen(command, *args):
stdout
=
subprocess
.
PIPE
,
stdout
=
subprocess
.
PIPE
,
stderr
=
subprocess
.
DEVNULL
,
stderr
=
subprocess
.
DEVNULL
,
env
=
env
)
env
=
env
)
return
proc
if
not
proc
:
return
None
stdout
,
stderr
=
proc
.
communicate
()
return
io
.
BytesIO
(
stdout
)
except
(
OSError
,
subprocess
.
SubprocessError
):
return
None
# For MAC (a.k.a. IEEE 802, or EUI-48) addresses, the second least significant
# For MAC (a.k.a. IEEE 802, or EUI-48) addresses, the second least significant
# bit of the first octet signifies whether the MAC address is universally (0)
# bit of the first octet signifies whether the MAC address is universally (0)
...
@@ -384,23 +398,79 @@ def _popen(command, *args):
...
@@ -384,23 +398,79 @@ def _popen(command, *args):
def
_is_universal
(
mac
):
def
_is_universal
(
mac
):
return
not
(
mac
&
(
1
<<
41
))
return
not
(
mac
&
(
1
<<
41
))
def
_find_mac
(
command
,
args
,
hw_identifiers
,
get_index
):
first_local_mac
=
None
def
_find_mac_near_keyword
(
command
,
args
,
keywords
,
get_word_index
):
try
:
"""Searches a command's output for a MAC address near a keyword.
proc
=
_popen
(
command
,
*
args
.
split
())
if
not
proc
:
Each line of words in the output is case-insensitively searched for
any of the given keywords. Upon a match, get_word_index is invoked
to pick a word from the line, given the index of the match. For
example, lambda i: 0 would get the first word on the line, while
lambda i: i - 1 would get the word preceding the keyword.
"""
stdout
=
_get_command_stdout
(
command
,
args
)
if
stdout
is
None
:
return
None
return
None
with
proc
:
for
line
in
proc
.
stdout
:
first_local_mac
=
None
for
line
in
stdout
:
words
=
line
.
lower
().
rstrip
().
split
()
words
=
line
.
lower
().
rstrip
().
split
()
for
i
in
range
(
len
(
words
)):
for
i
in
range
(
len
(
words
)):
if
words
[
i
]
in
hw_identifier
s
:
if
words
[
i
]
in
keyword
s
:
try
:
try
:
word
=
words
[
get_index
(
i
)]
word
=
words
[
get_word_index
(
i
)]
mac
=
int
(
word
.
replace
(
b':'
,
b''
),
16
)
mac
=
int
(
word
.
replace
(
_MAC_DELIM
,
b''
),
16
)
except
(
ValueError
,
IndexError
):
# Virtual interfaces, such as those provided by
# VPNs, do not have a colon-delimited MAC address
# as expected, but a 16-byte HWAddr separated by
# dashes. These should be ignored in favor of a
# real MAC address
pass
else
:
if
_is_universal
(
mac
):
if
_is_universal
(
mac
):
return
mac
return
mac
first_local_mac
=
first_local_mac
or
mac
first_local_mac
=
first_local_mac
or
mac
return
first_local_mac
or
None
def
_find_mac_under_heading
(
command
,
args
,
heading
):
"""Looks for a MAC address under a heading in a command's output.
The first line of words in the output is searched for the given
heading. Words at the same word index as the heading in subsequent
lines are then examined to see if they look like MAC addresses.
"""
stdout
=
_get_command_stdout
(
command
,
args
)
if
stdout
is
None
:
return
None
keywords
=
stdout
.
readline
().
rstrip
().
split
()
try
:
column_index
=
keywords
.
index
(
heading
)
except
ValueError
:
return
None
first_local_mac
=
None
for
line
in
stdout
:
try
:
words
=
line
.
rstrip
().
split
()
word
=
words
[
column_index
]
if
len
(
word
)
==
17
:
mac
=
int
(
word
.
replace
(
_MAC_DELIM
,
b''
),
16
)
elif
_MAC_OMITS_LEADING_ZEROES
:
# (Only) on AIX the macaddr value given is not prefixed by 0, e.g.
# en0 1500 link#2 fa.bc.de.f7.62.4 110854824 0 160133733 0 0
# not
# en0 1500 link#2 fa.bc.de.f7.62.04 110854824 0 160133733 0 0
parts
=
word
.
split
(
_MAC_DELIM
)
if
len
(
parts
)
==
6
and
all
(
0
<
len
(
p
)
<=
2
for
p
in
parts
):
hexstr
=
b''
.
join
(
p
.
rjust
(
2
,
b'0'
)
for
p
in
parts
)
mac
=
int
(
hexstr
,
16
)
else
:
continue
else
:
continue
except
(
ValueError
,
IndexError
):
except
(
ValueError
,
IndexError
):
# Virtual interfaces, such as those provided by
# Virtual interfaces, such as those provided by
# VPNs, do not have a colon-delimited MAC address
# VPNs, do not have a colon-delimited MAC address
...
@@ -408,16 +478,21 @@ def _find_mac(command, args, hw_identifiers, get_index):
...
@@ -408,16 +478,21 @@ def _find_mac(command, args, hw_identifiers, get_index):
# dashes. These should be ignored in favor of a
# dashes. These should be ignored in favor of a
# real MAC address
# real MAC address
pass
pass
except
OSError
:
else
:
pass
if
_is_universal
(
mac
):
return
mac
first_local_mac
=
first_local_mac
or
mac
return
first_local_mac
or
None
return
first_local_mac
or
None
# The following functions call external programs to 'get' a macaddr value to
# be used as basis for an uuid
def
_ifconfig_getnode
():
def
_ifconfig_getnode
():
"""Get the hardware address on Unix by running ifconfig."""
"""Get the hardware address on Unix by running ifconfig."""
# This works on Linux ('' or '-a'), Tru64 ('-av'), but not all Unixes.
# This works on Linux ('' or '-a'), Tru64 ('-av'), but not all Unixes.
keywords
=
(
b'hwaddr'
,
b'ether'
,
b'address:'
,
b'lladdr'
)
keywords
=
(
b'hwaddr'
,
b'ether'
,
b'address:'
,
b'lladdr'
)
for
args
in
(
''
,
'-a'
,
'-av'
):
for
args
in
(
''
,
'-a'
,
'-av'
):
mac
=
_find_mac
(
'ifconfig'
,
args
,
keywords
,
lambda
i
:
i
+
1
)
mac
=
_find_mac
_near_keyword
(
'ifconfig'
,
args
,
keywords
,
lambda
i
:
i
+
1
)
if
mac
:
if
mac
:
return
mac
return
mac
return
None
return
None
...
@@ -425,7 +500,7 @@ def _ifconfig_getnode():
...
@@ -425,7 +500,7 @@ def _ifconfig_getnode():
def
_ip_getnode
():
def
_ip_getnode
():
"""Get the hardware address on Unix by running ip."""
"""Get the hardware address on Unix by running ip."""
# This works on Linux with iproute2.
# This works on Linux with iproute2.
mac
=
_find_mac
(
'ip'
,
'link'
,
[
b'link/ether'
],
lambda
i
:
i
+
1
)
mac
=
_find_mac
_near_keyword
(
'ip'
,
'link'
,
[
b'link/ether'
],
lambda
i
:
i
+
1
)
if
mac
:
if
mac
:
return
mac
return
mac
return
None
return
None
...
@@ -439,17 +514,17 @@ def _arp_getnode():
...
@@ -439,17 +514,17 @@ def _arp_getnode():
return
None
return
None
# Try getting the MAC addr from arp based on our IP address (Solaris).
# Try getting the MAC addr from arp based on our IP address (Solaris).
mac
=
_find_mac
(
'arp'
,
'-an'
,
[
os
.
fsencode
(
ip_addr
)],
lambda
i
:
-
1
)
mac
=
_find_mac
_near_keyword
(
'arp'
,
'-an'
,
[
os
.
fsencode
(
ip_addr
)],
lambda
i
:
-
1
)
if
mac
:
if
mac
:
return
mac
return
mac
# This works on OpenBSD
# This works on OpenBSD
mac
=
_find_mac
(
'arp'
,
'-an'
,
[
os
.
fsencode
(
ip_addr
)],
lambda
i
:
i
+
1
)
mac
=
_find_mac
_near_keyword
(
'arp'
,
'-an'
,
[
os
.
fsencode
(
ip_addr
)],
lambda
i
:
i
+
1
)
if
mac
:
if
mac
:
return
mac
return
mac
# This works on Linux, FreeBSD and NetBSD
# This works on Linux, FreeBSD and NetBSD
mac
=
_find_mac
(
'arp'
,
'-an'
,
[
os
.
fsencode
(
'(%s)'
%
ip_addr
)],
mac
=
_find_mac
_near_keyword
(
'arp'
,
'-an'
,
[
os
.
fsencode
(
'(%s)'
%
ip_addr
)],
lambda
i
:
i
+
2
)
lambda
i
:
i
+
2
)
# Return None instead of 0.
# Return None instead of 0.
if
mac
:
if
mac
:
...
@@ -459,36 +534,12 @@ def _arp_getnode():
...
@@ -459,36 +534,12 @@ def _arp_getnode():
def
_lanscan_getnode
():
def
_lanscan_getnode
():
"""Get the hardware address on Unix by running lanscan."""
"""Get the hardware address on Unix by running lanscan."""
# This might work on HP-UX.
# This might work on HP-UX.
return
_find_mac
(
'lanscan'
,
'-ai'
,
[
b'lan0'
],
lambda
i
:
0
)
return
_find_mac
_near_keyword
(
'lanscan'
,
'-ai'
,
[
b'lan0'
],
lambda
i
:
0
)
def
_netstat_getnode
():
def
_netstat_getnode
():
"""Get the hardware address on Unix by running netstat."""
"""Get the hardware address on Unix by running netstat."""
# This might work on AIX, Tru64 UNIX.
# This works on AIX and might work on Tru64 UNIX.
first_local_mac
=
None
return
_find_mac_under_heading
(
'netstat'
,
'-ian'
,
b'Address'
)
try
:
proc
=
_popen
(
'netstat'
,
'-ia'
)
if
not
proc
:
return
None
with
proc
:
words
=
proc
.
stdout
.
readline
().
rstrip
().
split
()
try
:
i
=
words
.
index
(
b'Address'
)
except
ValueError
:
return
None
for
line
in
proc
.
stdout
:
try
:
words
=
line
.
rstrip
().
split
()
word
=
words
[
i
]
if
len
(
word
)
==
17
and
word
.
count
(
b':'
)
==
5
:
mac
=
int
(
word
.
replace
(
b':'
,
b''
),
16
)
if
_is_universal
(
mac
):
return
mac
first_local_mac
=
first_local_mac
or
mac
except
(
ValueError
,
IndexError
):
pass
except
OSError
:
pass
return
first_local_mac
or
None
def
_ipconfig_getnode
():
def
_ipconfig_getnode
():
"""Get the hardware address on Windows by running ipconfig.exe."""
"""Get the hardware address on Windows by running ipconfig.exe."""
...
...
Misc/NEWS.d/next/Library/2018-08-04-12-26-11.bpo-28009.4JcHZb.rst
0 → 100644
View file @
0bcbfa43
Fix uuid.getnode() on platforms with '.' as MAC Addr delimiter as well
fix for MAC Addr format that omits a leading 0 in MAC Addr values.
Currently, AIX is the only know platform with these settings.
Patch by Michael Felt.
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment