Browse Source

SteamID inherits from int now

* can be assigned to anything that accepts int
* can be serialized
* easier to work with instead of explicitly asking for int value
pull/18/merge
Rossen Georgiev 9 years ago
parent
commit
68faaf9218
  1. 304
      steam/steamid.py
  2. 39
      tests/test_steamid.py

304
steam/steamid.py

@ -1,27 +1,35 @@
import re import re
from steam.enums.base import SteamIntEnum
from steam.enums import EType, EUniverse from steam.enums import EType, EUniverse
ETypeCharMap = { class ETypeChar(SteamIntEnum):
0: 'I', I = EType.Invalid
1: 'U', U = EType.Individual
2: 'M', M = EType.Multiseat
3: 'G', G = EType.GameServer
4: 'A', A = EType.AnonGameServer
5: 'P', P = EType.Pending
6: 'C', C = EType.ContentServer
7: 'g', g = EType.Clan
8: 'T', T = EType.Chat
8: 'c', c = EType.Chat
8: 'L', L = EType.Chat
10: 'a', a = EType.AnonUser
}
ETypeChars = ''.join(map(str, list(ETypeChar)))
class SteamID(object):
class SteamID(int):
""" """
Object for converting steamID to its' various representations Object for converting steamID to its' various representations
(immutable)
""" """
def __new__(cls, *args, **kwargs):
steam64 = make_steam64(*args, **kwargs)
return super(SteamID, cls).__new__(cls, steam64)
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
""" """
The instance can be initialized with various parameters The instance can be initialized with various parameters
@ -34,114 +42,72 @@ class SteamID(object):
SteamID('103582791429521412') SteamID('103582791429521412')
SteamID('STEAM_1:0:2') # steam2 SteamID('STEAM_1:0:2') # steam2
SteamID('[g:1:4]') # steam3 SteamID('[g:1:4]') # steam3
SteamID('http://steamcommunity.com/profiles/76561197968459473')
# WARNING: vainty url resolving is fragile
# it might break if community profile page changes
# you should use the WebAPI to resolve them reliably
SteamID('https://steamcommunity.com/id/drunkenf00l')
"""
self.id = 0
self.type = EType.Invalid
self.universe = EUniverse.Invalid
self.instance = 0
if len(args) == 1:
value = str(args[0])
# numeric input
if value.isdigit():
value = int(value)
# 32 bit account id
if 0 < value < 2**32:
self.id = value
self.type = EType.Individual
self.universe = EUniverse.Public
self.instance = 1
# 64 bit
elif value < 2**64:
self.id = value & 0xFFffFFff
self.instance = (value >> 32) & 0xFFffF
self.type = EType((value >> 52) & 0xF)
self.universe = EUniverse((value >> 56) & 0xFF)
# textual input e.g. [g:1:4]
else:
result = steam2_to_tuple(value) or steam3_to_tuple(value)
if result:
(self.id,
self.type,
self.universe,
self.instance
) = result
elif len(kwargs):
self.id = int(kwargs.get('id', 0))
value = kwargs.get('type', 1)
if type(value) in (int, EType):
self.type = EType(value)
else:
self.type = EType[value]
value = kwargs.get('universe', 1) To create a SteamID from a community url use:
if type(value) in (int, EUniverse):
self.universe = EUniverse(value)
else:
self.universe = EUniverse[value]
if 'instance' in kwargs: steam.steamid.from_url()
self.instance = kwargs['instance'] """
assert self.instance <= 0xffffF, "instance larger than 20bits" pass
else:
if self.type in (EType.Individual, EType.GameServer):
self.instance = 1
else:
self.instance = 0
def __repr__(self): def __repr__(self):
return "<%s(id=%s, type=%s, universe=%s, instance=%s)>" % ( return "<%s(id=%s, type=%s, universe=%s, instance=%s)>" % (
self.__class__.__name__, self.__class__.__name__,
self.id, self.id,
repr(self.type.name), repr(str(self.type)),
repr(self.universe.name), repr(str(self.universe)),
self.instance, self.instance,
) )
def __str__(self): # def __str__(self):
return str(self.as_64) # return str(self.as_64)
#
def __int__(self): # def __int__(self):
return self.as_64 # return self.as_64
#
# def __eq__(self, other):
# return int(self) == int(other)
#
# def __ne__(self, other):
# return int(self) != int(other)
#
# def __lt__(self, other):
# return int(self) < int(other)
#
# def __le__(self, other):
# return int(self) <= int(other)
#
# def __gt__(self, other):
# return int(self) > int(other)
#
# def __ge__(self, other):
# return int(self) >= int(other)
#
# def __hash__(self):
# return hash(self.as_64)
def __eq__(self, other): @property
return int(self) == int(other) def id(self):
return int(self) & 0xFFffFFff
def __ne__(self, other):
return int(self) != int(other)
def __lt__(self, other):
return int(self) < int(other)
def __le__(self, other): @property
return int(self) <= int(other) def instance(self):
return (int(self) >> 32) & 0xFFffF
def __gt__(self, other): @property
return int(self) > int(other) def type(self):
return EType((int(self) >> 52) & 0xF)
def __ge__(self, other): @property
return int(self) >= int(other) def universe(self):
return EUniverse((int(self) >> 56) & 0xFF)
def __hash__(self): @property
return hash(self.as_64) def as_32(self):
return self.id
def is_valid(self): @property
return (0 < self.id < 2**32 def as_64(self):
and self.type is not EType.Invalid return int(self)
and self.universe is not EUniverse.Invalid
)
@property @property
def as_steam2(self): def as_steam2(self):
@ -154,30 +120,18 @@ class SteamID(object):
def as_steam3(self): def as_steam3(self):
if self.type is EType.AnonGameServer: if self.type is EType.AnonGameServer:
return "[%s:%s:%s:%s]" % ( return "[%s:%s:%s:%s]" % (
ETypeCharMap[self.type.value], str(ETypeChar(self.type)),
self.universe.value, int(self.universe),
self.id, self.id,
self.instance self.instance
) )
else: else:
return "[%s:%s:%s]" % ( return "[%s:%s:%s]" % (
ETypeCharMap[self.type.value], str(ETypeChar(self.type)),
self.universe.value, int(self.universe),
self.id, self.id,
) )
@property
def as_64(self):
return ((self.universe.value << 56)
| (self.type.value << 52)
| (self.instance << 32)
| self.id
)
@property
def as_32(self):
return self.id
@property @property
def community_url(self): def community_url(self):
suffix = { suffix = {
@ -187,8 +141,97 @@ class SteamID(object):
if self.type in suffix: if self.type in suffix:
url = "https://steamcommunity.com/%s" % suffix[self.type] url = "https://steamcommunity.com/%s" % suffix[self.type]
return url % self.as_64 return url % self.as_64
return None return None
def is_valid(self):
return (self.id > 0
and self.type is not EType.Invalid
and self.universe is not EUniverse.Invalid
)
def make_steam64(id=0, *args, **kwargs):
"""
Returns steam64 from various other representations.
make_steam64() # invalid steamid
make_steam64(12345) # accountid
make_steam64('12345')
make_steam64(id=12345, type='Invalid', universe='Invalid', instance=0)
make_steam64(103582791429521412) # steam64
make_steam64('103582791429521412')
make_steam64('STEAM_1:0:2') # steam2
make_steam64('[g:1:4]') # steam3
"""
accountid = id
etype = EType.Invalid
universe = EUniverse.Invalid
instance = None
if len(args) == 0 and len(kwargs) == 0:
value = str(accountid)
# numeric input
if value.isdigit():
value = int(value)
# 32 bit account id
if 0 < value < 2**32:
accountid = value
etype = EType.Individual
universe = EUniverse.Public
# 64 bit
elif value < 2**64:
return value
# textual input e.g. [g:1:4]
else:
result = steam2_to_tuple(value) or steam3_to_tuple(value)
if result:
(accountid,
etype,
universe,
instance,
) = result
else:
accountid = 0
elif len(args) > 0:
length = len(args)
if length == 1:
etype, = args
elif length == 2:
etype, universe = args
elif length == 3:
etype, universe, instance = args
else:
raise TypeError("Takes at most 4 arguments (%d given)" % length)
if len(kwargs) > 0:
etype = kwargs.get('type', etype)
universe = kwargs.get('universe', universe)
instance = kwargs.get('instance', instance)
etype = (EType(etype)
if isinstance(etype, (int, EType))
else EType[etype]
)
universe = (EUniverse(universe)
if isinstance(universe, (int, EUniverse))
else EUniverse[universe]
)
if instance is None:
instance = 1 if etype in (EType.Individual, EType.GameServer) else 0
assert instance <= 0xffffF, "instance larger than 20bits"
return (universe << 56) | (etype << 52) | (instance << 32) | accountid
def steam2_to_tuple(value): def steam2_to_tuple(value):
match = re.match(r"^STEAM_(?P<universe>[01])" match = re.match(r"^STEAM_(?P<universe>[01])"
@ -203,25 +246,22 @@ def steam2_to_tuple(value):
return (steam32, EType(1), EUniverse(1), 1) return (steam32, EType(1), EUniverse(1), 1)
def steam3_to_tuple(value): def steam3_to_tuple(value):
typeChars = ''.join(ETypeCharMap.values())
match = re.match(r"^\[" match = re.match(r"^\["
r"(?P<type>[%s]):" # type char r"(?P<type>[%s]):" # type char
r"(?P<universe>\d+):" # universe r"(?P<universe>\d+):" # universe
r"(?P<id>\d+)" # accountid r"(?P<id>\d+)" # accountid
r"(:(?P<instance>\d+))?" # instance r"(:(?P<instance>\d+))?" # instance
r"\]$" % typeChars, value r"\]$" % ETypeChars,
value
) )
if not match: if not match:
return None return None
steam32 = int(match.group('id')) steam32 = int(match.group('id'))
universe = EUniverse(int(match.group('universe'))) universe = EUniverse(int(match.group('universe')))
etype = ETypeChar[match.group('type')]
inverseETypeCharMap = dict((b, a) for (a, b) in ETypeCharMap.items())
etype = EType(inverseETypeCharMap[match.group('type')])
instance = match.group('instance') instance = match.group('instance')
if instance is None: if instance is None:

39
tests/test_steamid.py

@ -3,7 +3,7 @@ import mock
import vcr import vcr
from steam import steamid from steam import steamid
from steam.steamid import SteamID, ETypeCharMap from steam.steamid import SteamID, ETypeChar
from steam.enums import EType, EUniverse from steam.enums import EType, EUniverse
@ -18,15 +18,6 @@ class SteamID_initialization(unittest.TestCase):
self.assertEqual(hash(SteamID(1)), hash(SteamID(1))) self.assertEqual(hash(SteamID(1)), hash(SteamID(1)))
self.assertNotEqual(hash(SteamID(12345)), hash(SteamID(8888))) self.assertNotEqual(hash(SteamID(12345)), hash(SteamID(8888)))
def test_rich_comperison(self):
for test_value in [SteamID(5), 5, '5']:
self.assertFalse(SteamID(10) == test_value)
self.assertTrue(SteamID(10) != test_value)
self.assertTrue(SteamID(10) > test_value)
self.assertTrue(SteamID(10) >= test_value)
self.assertFalse(SteamID(10) < test_value)
self.assertFalse(SteamID(10) <= test_value)
def test_is_valid(self): def test_is_valid(self):
self.assertTrue(SteamID(1).is_valid()) self.assertTrue(SteamID(1).is_valid())
self.assertTrue(SteamID(id=5).is_valid()) self.assertTrue(SteamID(id=5).is_valid())
@ -38,12 +29,18 @@ class SteamID_initialization(unittest.TestCase):
self.assertFalse(SteamID(id=1, universe=EUniverse.Invalid).is_valid()) self.assertFalse(SteamID(id=1, universe=EUniverse.Invalid).is_valid())
def test_arg_toomany_invalid(self): def test_arg_toomany_invalid(self):
with self.assertRaises(TypeError):
SteamID(1,2,3,4,5)
with self.assertRaises(TypeError):
SteamID(1,2,3,4,5,6)
def test_args_only(self):
self.compare(SteamID(1, 2), self.compare(SteamID(1, 2),
[0, EType.Invalid, EUniverse.Invalid, 0]) [1, 2, 0, 0])
self.compare(SteamID(1, 2, 3), self.compare(SteamID(1, 2, 3),
[0, EType.Invalid, EUniverse.Invalid, 0]) [1, 2, 3, 0])
self.compare(SteamID(1, 2, 3, 4), self.compare(SteamID(1, 2, 3, 4),
[0, EType.Invalid, EUniverse.Invalid, 0]) [1, 2, 3, 4])
###################################################### ######################################################
# 1 ARG # 1 ARG
@ -109,7 +106,7 @@ class SteamID_initialization(unittest.TestCase):
) )
def test_arg_steam3(self, steam2_to_tuple, steam3_to_tuple): def test_arg_steam3(self, steam2_to_tuple, steam3_to_tuple):
steam2_to_tuple.return_value = None steam2_to_tuple.return_value = None
steam3_to_tuple.return_value = (5, 6, 7, 8) steam3_to_tuple.return_value = (4, 3, 2, 1)
test_instance = SteamID('[g:1:4]') test_instance = SteamID('[g:1:4]')
@ -117,7 +114,7 @@ class SteamID_initialization(unittest.TestCase):
steam3_to_tuple.assert_called_once_with('[g:1:4]') steam3_to_tuple.assert_called_once_with('[g:1:4]')
self.compare(test_instance, self.compare(test_instance,
[5, 6, 7, 8]) [4, 3, 2, 1])
def test_arg_text_invalid(self): def test_arg_text_invalid(self):
self.compare(SteamID("invalid_format"), self.compare(SteamID("invalid_format"),
@ -183,6 +180,18 @@ class SteamID_properties(unittest.TestCase):
# just to cover in coverage # just to cover in coverage
repr(SteamID()) repr(SteamID())
def test_rich_comperison(self):
for test_value in [SteamID(5), 5]:
self.assertFalse(SteamID(10) == test_value)
self.assertTrue(SteamID(10) != test_value)
self.assertTrue(SteamID(10) > test_value)
self.assertTrue(SteamID(10) >= test_value)
self.assertFalse(SteamID(10) < test_value)
self.assertFalse(SteamID(10) <= test_value)
def test_is_instance_of_int(self):
self.assertIsInstance(SteamID(5), int)
def test_str(self): def test_str(self):
self.assertEqual(str(SteamID(76580280500085312)), '76580280500085312') self.assertEqual(str(SteamID(76580280500085312)), '76580280500085312')

Loading…
Cancel
Save