def test_exceptions_str(self): incorrect = [ {'remote': {'networking': {'nic': "eth0", "firewall": {"open_ports":[1,2,3, 'a']}}}}, {'remote': {'networking': {'nic': "eth0", "firewall": {"closed_ports": [], "open_ports":[1,2,3]}}}}, {'remote': {'networking': {'noc': "eth0", "firewall": {"open_ports":[2,3]}}}}, {'romote': {'networking': {'nic': "eth0", "firewall": {"open_ports":[2,3]}}}}, {'remote': {'nitworking': {'nic': "eth0", "firewall": {"open_ports":[2,3]}}}}, ] paths = [] for i in incorrect: try: load(i, Config, basiccast=False, failonextra=True) assert False except exceptions.TypedloadException as e: for i in e.exceptions: paths.append(e._path(e.trace) + '.' + i._path(i.trace[1:])) #1st object assert paths[0] == '.remote.networking.firewall.open_ports.[3]' assert paths[1] == '.remote.' #2nd object assert paths[2] == '.remote.networking.firewall' assert paths[3] == '.remote.' #3rd object assert paths[4] == '.remote.networking' assert paths[5] == '.remote.' #4th object # Nothing because of no sub-exceptions, fails before the union #5th object assert paths[6] == '.remote.' assert paths[7] == '.remote.' assert len(paths) == 8
def test_nestedload(self): @dataclass class A: a: int b: str @dataclass class B: a: A b: List[A] assert load({ 'a': { 'a': 101, 'b': 'ciao' }, 'b': [] }, B) == B(A(101, 'ciao'), []) assert load( { 'a': { 'a': 101, 'b': 'ciao' }, 'b': [{ 'a': 1, 'b': 'a' }, { 'a': 0, 'b': 'b' }] }, B) == B(A(101, 'ciao'), [A(1, 'a'), A(0, 'b')])
def test_correct_exception_when_mangling(self): @dataclass class A: a: str = field(metadata={'name': 'q'}) with self.assertRaises(exceptions.TypedloadAttributeError): load(1, A)
def test_correct_exception_when_mangling(self): @attrs class A: a = attrib(type=str, metadata={'name': 'q'}) with self.assertRaises(exceptions.TypedloadAttributeError): load(1, A)
async def channels(self, refresh: bool = False) -> List[Channel]: """ Returns the list of slack channels if refresh is set, the local cache is cleared """ if refresh: self._channelscache.clear() if self._channelscache: return self._channelscache cursor = None while True: r = await self.client.api_call( 'conversations.list', cursor=cursor, exclude_archived=True, types='public_channel,private_channel,mpim', limit=1000, # In vain hope that slack would not ignore this ) response = load(r, Response) if response.ok: conv = load(r, Conversations) self._channelscache += conv.channels # For this API, slack sends an empty string as next cursor, just to show off their programming "skillz" if not conv.response_metadata or not conv.response_metadata.next_cursor: break cursor = conv.response_metadata.next_cursor else: raise ResponseException(response.error) return self._channelscache
def test_factory_load(self): @dataclass class A: a: List[int] = field(default_factory=list) assert load({'a': [1, 2, 3]}, A) == A([1, 2, 3]) assert load({'a': []}, A) == A() assert load({}, A) == A()
def test_mangle_extra(self): @dataclass class Mangle: value: int = field(metadata={'name': 'Value'}) assert load({'value': 12, 'Value': 12}, Mangle) == Mangle(12) with self.assertRaises(exceptions.TypedloadValueError): load({'value': 12, 'Value': 12}, Mangle, failonextra=True)
def test_defaultvalue(self): @dataclass class A: a: int b: Optional[str] = None assert load({'a': 1}, A) == A(1) assert load({'a': 1, 'b': 'io'}, A) == A(1, 'io')
def get_members(self, id_: str) -> List[str]: r = self.client.api_call('conversations.members', channel=id_, limit=5000) response = load(r, Response) if response.ok: return load(r['members'], List[str]) raise ResponseException(response)
def test_loadperson(self): o = {'name': 'pino', 'age': 1.1} assert load(o, Person) == o assert load({'val': 3}, A) == {'val': '3'} with self.assertRaises(ValueError): o.pop('age') load(o, Person)
def test_mangle_extra(self): @attrs class Mangle: value = attrib(metadata={'name': 'Value'}, type=int) assert load({'value': 12, 'Value': 12}, Mangle) == Mangle(12) with self.assertRaises(exceptions.TypedloadValueError): load({'value': 12, 'Value': 12}, Mangle, failonextra=True)
def test_case(self): @dataclass class Mangle: value: int = field(metadata={'name': 'Value'}) assert load({'Value': 1}, Mangle) == Mangle(1) assert 'Value' in dump(Mangle(1)) with self.assertRaises(ValueError): load({'value': 1}, Mangle)
def test_nestedlegacyload(self): A = NamedTuple('A', [('a', int), ('b', str)]) B = NamedTuple('B', [('a', A), ('b', List[A])]) assert load({'a': {'a': 101, 'b': 'ciao'}, 'b': []}, B) == B(A(101, 'ciao'), []) assert load( {'a': {'a': 101, 'b': 'ciao'}, 'b': [{'a': 1, 'b': 'a'},{'a': 0, 'b': 'b'}]}, B ) == B(A(101, 'ciao'), [A(1, 'a'),A(0, 'b')])
def test_case(self): @attrs class Mangle: value = attrib(type=int, metadata={'name': 'Value'}) assert load({'Value': 1}, Mangle) == Mangle(1) assert 'Value' in dump(Mangle(1)) with self.assertRaises(TypeError): load({'value': 1}, Mangle)
def prefetch_users(self) -> None: """ Prefetch all team members for the slack team. """ r = self.client.api_call("users.list") response = load(r, Response) if response.ok: for user in load(r['members'], List[User]): self._usercache[user.id] = user self._usermapcache[user.name] = user
def events_iter(self) -> Iterator[Optional[SlackEvent]]: """ This yields an event or None. Don't call it without sleeps """ if self.client.rtm_connect(): while True: try: events = self.client.rtm_read() except: if not self.client.rtm_connect(): sleep(10) events = [] for event in events: t = event.get('type') subt = event.get('subtype') try: if t == 'message' and not subt: yield _loadwrapper(event, Message) elif t == 'message' and subt == 'slackbot_response': yield _loadwrapper(event, Message) elif t == 'message' and subt == 'file_share': yield _loadwrapper(event, MessageFileShare) elif t == 'message' and subt == 'message_changed': event['message']['channel'] = event['channel'] event['previous_message']['channel'] = event[ 'channel'] yield MessageEdit( previous=load(event['previous_message'], Message), current=load(event['message'], Message)) elif t == 'message' and subt == 'message_deleted': event['previous_message']['channel'] = event[ 'channel'] yield _loadwrapper(event['previous_message'], MessageDelete) elif t == 'message' and subt == 'bot_message': yield _loadwrapper(event, MessageBot) elif t == 'user_change': # Changes in the user, drop it from cache u = load(event['user'], User) if u.id in self._usercache: del self._usercache[u.id] #FIXME don't know if it is wise, maybe it gets lost forever del self._usermapcache[u.name] #TODO make an event for this elif t == 'file_deleted': yield _loadwrapper(event, FileDeleted) elif t in USELESS_EVENTS: continue else: print(event) except Exception as e: print('Exception: %s' % e) yield None
def test_wrongtype_nested(self): data = { 'course': 'how to be a corsair', 'students': [{ 'name': 'Alfio' }, 3] } try: load(data, Students) except exceptions.TypedloadAttributeError as e: assert e.trace[-1].annotation[1] == 1
def test_enum_exceptions_str(self): incorrect = [ [1, 1], '3', 12, ] for i in incorrect: try: load(i, Enumeration, basiccast=False, failonextra=True) except Exception as e: str(e)
def get_file(self, f: Union[FileShared, str]) -> File: """ Returns a file object """ fileid = f if isinstance(f, str) else f.file_id r = self.client.api_call("files.info", file=fileid) response = load(r, Response) if response.ok: return load(r['file'], File) else: raise KeyError(response)
def test_uuid(self): import uuid @attrs class A: a = attrib(type=int) uuid_value = attrib(type=str, init=False) def __attrs_post_init__(self): self.uuid_value = str(uuid.uuid4()) assert type(load({'a': 1}, A).uuid_value) == str assert load({'a': 1}, A) != load({'a': 1}, A)
def get_ims(self) -> List[IM]: """ Returns a list of the IMs Some bullshit slack invented because 1 to 1 conversations need to have an ID to send to, you can't send directly to a user. """ r = self.client.api_call("im.list", ) response = load(r, Response) if response.ok: return load(r['ims'], List[IM]) raise ResponseException(response)
def test_tuple_exceptions_str(self): incorrect = [ [1, 1], [1, 1, 1], [1], [1, 1.2], [1, None], [1, None, 1], ] for i in incorrect: try: load(i, Tuple[int, int], basiccast=False, failonextra=True) except Exception as e: str(e)
def channels(self) -> List[Channel]: """ Returns the list of slack channels """ result = [] # type: List[Channel] r = self.client.api_call("conversations.list", exclude_archived=True, types='public_channel,private_channel,mpim', limit=1000) response = load(r, Response) if response.ok: return load(r['channels'], List[Channel]) else: raise ResponseException(response)
def get_ims(self) -> List[IM]: """ Returns a list of the IMs Some bullshit slack invented because 1 to 1 conversations need to have an ID to send to, you can't send directly to a user. """ r = self.client.api_call("conversations.list", exclude_archived=True, types='im', limit=1000) response = load(r, Response) if response.ok: return load(r['channels'], List[IM]) raise ResponseException(response)
def kick(self, channel: Channel, user: User) -> None: r = self.client.api_call('conversations.kick', channel=channel.id, user=user.id) response = load(r, Response) if not response.ok: raise ResponseException(response)
def topic(self, channel: Channel, topic: str) -> None: r = self.client.api_call('conversations.setTopic', channel=channel.id, topic=topic) response = load(r, Response) if not response.ok: raise ResponseException(response)
def test_weird_mangle(self): @attrs class Mangle: a = attrib(type=int, metadata={'name': 'b', 'alt': 'q'}) b = attrib(type=str, metadata={'name': 'a'}) assert load({'b': 1, 'a': 'ciao'}, Mangle) == Mangle(1, 'ciao') assert load({ 'q': 1, 'b': 'ciao' }, Mangle, mangle_key='alt') == Mangle(1, 'ciao') assert dump(Mangle(1, 'ciao')) == {'b': 1, 'a': 'ciao'} assert dump(Mangle(1, 'ciao'), mangle_key='alt') == { 'q': 1, 'b': 'ciao' }
def __init__(self, token: str, cookie: Optional[str], previous_status: Optional[bytes]) -> None: """ A slack client object. token: The slack token cookie: If the slack instance also uses a cookie, it must be passed here previous_status: Opaque bytestring to restore internal status from a different object. Obtained from get_status() """ self.client = SlackClient(token, cookie) self._usercache: Dict[str, User] = {} self._usermapcache: Dict[str, User] = {} self._usermapcache_keys: List[str] self._imcache: Dict[str, str] = {} self._channelscache: List[Channel] = [] self._get_members_cache: Dict[str, Set[str]] = {} self._get_members_cache_cursor: Dict[str, Optional[str]] = {} self._internalevents: List[SlackEvent] = [] self._sent_by_self: Set[float] = set() self._wsblock: int = 0 # Semaphore to block the socket and avoid events being received before their API call ended. self.login_info: Optional[LoginInfo] = None if previous_status is None: self._status = SlackStatus() else: self._status = load(json.loads(previous_status), SlackStatus)
async def _thread_history(self, channel: str, thread_id: str) -> List[Union[HistoryMessage, HistoryBotMessage]]: r: List[Union[HistoryMessage, HistoryBotMessage]] = [] cursor = None log('Thread history', channel, thread_id) while True: log('Cursor') p = await self.client.api_call( 'conversations.replies', channel=channel, ts=thread_id, limit=1000, cursor=cursor, ) try: response = load(p, History) except Exception as e: log('Failed to parse', e) log(p) break r += [i for i in response.messages if i.ts != i.thread_ts] if response.has_more and response.response_metadata: cursor = response.response_metadata.next_cursor else: break log('Thread fetched') r[0].thread_ts = None return r
def test_home_config_convert(): config = typedload.load( { 'accounts': [{ 'id': '1', 'name': 'peter', 'port': 8080, 'ssl': False, 'username': '', 'host': 'deepkit.ai', 'token': 'abc' }, { 'id': '2', 'name': 'localhost', 'port': 8080, 'ssl': False, 'username': '', 'host': 'deepkit.ai', 'token': 'abc' }], 'folderLinks': [] }, HomeConfig) assert config.get_account_for_id('1').name == 'peter' assert config.get_account_for_id('2').name == 'localhost'
def location(self, user_input) -> Stops: '''Returns a list of Stop objects, completing from the user input''' a = self._request( "location.name", urllib.parse.urlencode({'input': user_input})) c = a["LocationList"]['StopLocation'] if isinstance(c, dict): c = [c] return load(c, Stops)
def _get_token(key: str) -> str: if hasattr(_get_token, 'token') and monotonic() < _get_token.obtained + _get_token.token.expires_in: return _get_token.token.access_token url = "https://api.vasttrafik.se:443/token" req = urllib.request.Request(url) req.data = b'grant_type=client_credentials' req.headers['Authorization'] = 'Basic ' + key with urllib.request.urlopen(req) as f: token = load(json.load(f), Token) _get_token.token = token _get_token.obtained = monotonic() return token.access_token
def trip(self, originCoord=None, originId=None, originCoordName=None, destCoord=None, destId=None, destCoordName=None, viaId=None, datetime_obj=None) -> 'Trips': ''' originCoord = a tuple with origin coordinates (lat,lon) originId = stop id originCoordName = address destCoord destId destCoordName viaId = pass by a certain stop datetime_obj = search from this moment ''' service = 'trip' params = {} if originCoord != None: params['originCoordLat'] = originCoord[0] params['originCoordLong'] = originCoord[1] elif originId != None: params['originId'] = originId elif originCoordName != None: params['originCoordName'] = originCoordName if destCoord != None: params['destCoordLat'] = destCoord[0] params['destCoordLong'] = destCoord[1] elif destId != None: params['destId'] = destId elif destCoordName != None: params['destCoordName'] = destCoordName if viaId != None: params['viaId'] = viaId if datetime_obj != None: params['date'] = '%04d-%02d-%02d' % ( datetime_obj.year, datetime_obj.month, datetime_obj.day) params['time'] = '%02d:%02d' % ( datetime_obj.hour, datetime_obj.minute) # Request b = self._request(service, urllib.parse.urlencode(params)) c = b['TripList'] self.datetime_obj = to_datetime(c['serverdate'], c['servertime']) return load(c['Trip'], Trips)
def nearby(self, lat: float, lon: float, stops: int = 10, dist: Optional[int] = None) -> Stops: ''' Returns the list of stops close to a certain location lat = latitude lon = longitude stops = maximum number of stops to return dist = maximum distance in meters ''' params = 'originCoordLat=%s&originCoordLong=%s&maxNo=%d' % ( str(lat), str(lon), stops) if dist != None: params += '&maxDist=%d' % dist b = self._request('location.nearbystops', params) c = b["LocationList"]['StopLocation'] return load(c, Stops)