Fix type specifiers in models

This commit is contained in:
Mads Marquart
2020-03-11 14:43:28 +01:00
parent ef95aed208
commit c27f599e37
15 changed files with 126 additions and 107 deletions

View File

@@ -1,7 +1,7 @@
import attr
import requests
from typing import Any
from typing import Any, Optional
# Not frozen, since that doesn't work in PyPy
@attr.s(slots=True, auto_exc=True)
@@ -20,7 +20,7 @@ class HTTPError(FacebookError):
"""Base class for errors with the HTTP(s) connection to Facebook."""
#: The returned HTTP status code, if relevant
status_code = attr.ib(None, type=int)
status_code = attr.ib(None, type=Optional[int])
def __str__(self):
if not self.status_code:
@@ -58,7 +58,7 @@ class ExternalError(FacebookError):
#: The error message that Facebook returned (Possibly in the user's own language)
description = attr.ib(type=str)
#: The error code that Facebook returned
code = attr.ib(None, type=int)
code = attr.ib(None, type=Optional[int])
def __str__(self):
if self.code:
@@ -73,7 +73,7 @@ class GraphQLError(ExternalError):
# TODO: Handle multiple errors
#: Query debug information
debug_info = attr.ib(None, type=str)
debug_info = attr.ib(None, type=Optional[str])
def __str__(self):
if self.debug_info:

View File

@@ -5,7 +5,7 @@ import requests
from ._common import log, kw_only
from . import _util, _exception, _session, _graphql, _events
from typing import Iterable, Optional, Mapping
from typing import Iterable, Optional, Mapping, List
HOST = "edge-chat.facebook.com"
@@ -118,7 +118,7 @@ class Listener:
_mqtt = attr.ib(factory=mqtt_factory, type=paho.mqtt.client.Client)
_sync_token = attr.ib(None, type=Optional[str])
_sequence_id = attr.ib(None, type=Optional[int])
_tmp_events = attr.ib(factory=list, type=Iterable[_events.Event])
_tmp_events = attr.ib(factory=list, type=List[_events.Event])
def __attrs_post_init__(self):
# Configure callbacks

View File

@@ -3,7 +3,7 @@ from . import Image
from .._common import attrs_default
from .. import _util
from typing import Sequence
from typing import Optional, Sequence
@attrs_default
@@ -11,7 +11,7 @@ class Attachment:
"""Represents a Facebook attachment."""
#: The attachment ID
id = attr.ib(None, type=str)
id = attr.ib(None, type=Optional[str])
@attrs_default
@@ -24,21 +24,21 @@ class ShareAttachment(Attachment):
"""Represents a shared item (e.g. URL) attachment."""
#: ID of the author of the shared post
author = attr.ib(None, type=str)
author = attr.ib(None, type=Optional[str])
#: Target URL
url = attr.ib(None, type=str)
url = attr.ib(None, type=Optional[str])
#: Original URL if Facebook redirects the URL
original_url = attr.ib(None, type=str)
original_url = attr.ib(None, type=Optional[str])
#: Title of the attachment
title = attr.ib(None, type=str)
title = attr.ib(None, type=Optional[str])
#: Description of the attachment
description = attr.ib(None, type=str)
description = attr.ib(None, type=Optional[str])
#: Name of the source
source = attr.ib(None, type=str)
source = attr.ib(None, type=Optional[str])
#: The attached image
image = attr.ib(None, type=Image)
image = attr.ib(None, type=Optional[Image])
#: URL of the original image if Facebook uses ``safe_image``
original_image_url = attr.ib(None, type=str)
original_image_url = attr.ib(None, type=Optional[str])
#: List of additional attachments
attachments = attr.ib(factory=list, type=Sequence[Attachment])

View File

@@ -4,6 +4,8 @@ import enum
from .._common import attrs_default
from .. import _util
from typing import Optional
class ThreadLocation(enum.Enum):
"""Used to specify where a thread is located (inbox, pending, archived, other)."""
@@ -21,11 +23,11 @@ class ThreadLocation(enum.Enum):
@attrs_default
class ActiveStatus:
#: Whether the user is active now
active = attr.ib(None, type=bool)
active = attr.ib(type=bool)
#: Datetime when the user was last active
last_active = attr.ib(None, type=datetime.datetime)
last_active = attr.ib(None, type=Optional[datetime.datetime])
#: Whether the user is playing Messenger game now
in_game = attr.ib(None, type=bool)
in_game = attr.ib(None, type=Optional[bool])
@classmethod
def _from_orca_presence(cls, data):
@@ -42,9 +44,9 @@ class Image:
#: URL to the image
url = attr.ib(type=str)
#: Width of the image
width = attr.ib(None, type=int)
width = attr.ib(None, type=Optional[int])
#: Height of the image
height = attr.ib(None, type=int)
height = attr.ib(None, type=Optional[int])
@classmethod
def _from_uri(cls, data):

View File

@@ -4,7 +4,7 @@ from . import Image, Attachment
from .._common import attrs_default
from .. import _util
from typing import Set
from typing import Set, Optional
@attrs_default
@@ -12,13 +12,13 @@ class FileAttachment(Attachment):
"""Represents a file that has been sent as a Facebook attachment."""
#: URL where you can download the file
url = attr.ib(None, type=str)
url = attr.ib(None, type=Optional[str])
#: Size of the file in bytes
size = attr.ib(None, type=int)
size = attr.ib(None, type=Optional[int])
#: Name of the file
name = attr.ib(None, type=str)
name = attr.ib(None, type=Optional[str])
#: Whether Facebook determines that this file may be harmful
is_malicious = attr.ib(None, type=bool)
is_malicious = attr.ib(None, type=Optional[bool])
@classmethod
def _from_graphql(cls, data, size=None):
@@ -36,13 +36,13 @@ class AudioAttachment(Attachment):
"""Represents an audio file that has been sent as a Facebook attachment."""
#: Name of the file
filename = attr.ib(None, type=str)
filename = attr.ib(None, type=Optional[str])
#: URL of the audio file
url = attr.ib(None, type=str)
url = attr.ib(None, type=Optional[str])
#: Duration of the audio clip as a timedelta
duration = attr.ib(None, type=datetime.timedelta)
duration = attr.ib(None, type=Optional[datetime.timedelta])
#: Audio type
audio_type = attr.ib(None, type=str)
audio_type = attr.ib(None, type=Optional[str])
@classmethod
def _from_graphql(cls, data):
@@ -63,13 +63,13 @@ class ImageAttachment(Attachment):
"""
#: The extension of the original image (e.g. ``png``)
original_extension = attr.ib(None, type=str)
original_extension = attr.ib(None, type=Optional[str])
#: Width of original image
width = attr.ib(None, converter=lambda x: None if x is None else int(x), type=int)
width = attr.ib(None, converter=_util.int_or_none, type=Optional[int])
#: Height of original image
height = attr.ib(None, converter=lambda x: None if x is None else int(x), type=int)
height = attr.ib(None, converter=_util.int_or_none, type=Optional[int])
#: Whether the image is animated
is_animated = attr.ib(None, type=bool)
is_animated = attr.ib(None, type=Optional[bool])
#: A set, containing variously sized / various types of previews of the image
previews = attr.ib(factory=set, type=Set[Image])
@@ -113,15 +113,15 @@ class VideoAttachment(Attachment):
"""Represents a video that has been sent as a Facebook attachment."""
#: Size of the original video in bytes
size = attr.ib(None, type=int)
size = attr.ib(None, type=Optional[int])
#: Width of original video
width = attr.ib(None, type=int)
width = attr.ib(None, type=Optional[int])
#: Height of original video
height = attr.ib(None, type=int)
height = attr.ib(None, type=Optional[int])
#: Length of video as a timedelta
duration = attr.ib(None, type=datetime.timedelta)
duration = attr.ib(None, type=Optional[datetime.timedelta])
#: URL to very compressed preview video
preview_url = attr.ib(None, type=str)
preview_url = attr.ib(None, type=Optional[str])
#: A set, containing variously sized previews of the video
previews = attr.ib(factory=set, type=Set[Image])

View File

@@ -1,8 +1,11 @@
import attr
import datetime
from . import Image, Attachment
from .._common import attrs_default
from .. import _util, _exception
from typing import Optional
@attrs_default
class LocationAttachment(Attachment):
@@ -12,15 +15,15 @@ class LocationAttachment(Attachment):
"""
#: Latitude of the location
latitude = attr.ib(None, type=float)
latitude = attr.ib(None, type=Optional[float])
#: Longitude of the location
longitude = attr.ib(None, type=float)
longitude = attr.ib(None, type=Optional[float])
#: Image showing the map of the location
image = attr.ib(None, type=Image)
image = attr.ib(None, type=Optional[Image])
#: URL to Bing maps with the location
url = attr.ib(None, type=str)
url = attr.ib(None, type=Optional[str])
# Address of the location
address = attr.ib(None, type=str)
address = attr.ib(None, type=Optional[str])
@classmethod
def _from_graphql(cls, data):
@@ -51,11 +54,11 @@ class LiveLocationAttachment(LocationAttachment):
"""Represents a live user location."""
#: Name of the location
name = attr.ib(None)
name = attr.ib(None, type=Optional[str])
#: Datetime when live location expires
expires_at = attr.ib(None)
expires_at = attr.ib(None, type=Optional[datetime.datetime])
#: True if live location is expired
is_expired = attr.ib(None)
is_expired = attr.ib(None, type=Optional[bool])
@classmethod
def _from_pull(cls, data):

View File

@@ -255,31 +255,31 @@ class MessageData(Message):
#: Datetime of when the message was sent
created_at = attr.ib(type=datetime.datetime)
#: The actual message
text = attr.ib(None, type=str)
text = attr.ib(None, type=Optional[str])
#: A list of `Mention` objects
mentions = attr.ib(factory=list, type=Sequence[Mention])
#: Size of a sent emoji
emoji_size = attr.ib(None, type=EmojiSize)
emoji_size = attr.ib(None, type=Optional[EmojiSize])
#: Whether the message is read
is_read = attr.ib(None, type=bool)
is_read = attr.ib(None, type=Optional[bool])
#: A list of people IDs who read the message, works only with `Client.fetch_thread_messages`
read_by = attr.ib(factory=list, type=bool)
#: A dictionary with user's IDs as keys, and their reaction as values
reactions = attr.ib(factory=dict, type=Mapping[str, str])
#: A `Sticker`
sticker = attr.ib(None, type=_sticker.Sticker)
sticker = attr.ib(None, type=Optional[_sticker.Sticker])
#: A list of attachments
attachments = attr.ib(factory=list, type=Sequence[_attachment.Attachment])
#: A list of `QuickReply`
quick_replies = attr.ib(factory=list, type=Sequence[_quick_reply.QuickReply])
#: Whether the message is unsent (deleted for everyone)
unsent = attr.ib(False, type=bool)
unsent = attr.ib(False, type=Optional[bool])
#: Message ID you want to reply to
reply_to_id = attr.ib(None, type=str)
reply_to_id = attr.ib(None, type=Optional[str])
#: Replied message
replied_to = attr.ib(None, type="MessageData")
replied_to = attr.ib(None, type=Optional["MessageData"])
#: Whether the message was forwarded
forwarded = attr.ib(False, type=bool)
forwarded = attr.ib(False, type=Optional[bool])
@staticmethod
def _get_forwarded_from_tags(tags):

View File

@@ -4,7 +4,7 @@ import enum
from .._common import attrs_default
from .. import _exception, _util, _session
from typing import Mapping, Sequence
from typing import Mapping, Sequence, Optional
class GuestStatus(enum.Enum):
@@ -132,13 +132,13 @@ class PlanData(Plan):
#: Plan title
title = attr.ib(type=str)
#: Plan location name
location = attr.ib(None, converter=lambda x: x or "", type=str)
location = attr.ib(None, converter=lambda x: x or "", type=Optional[str])
#: Plan location ID
location_id = attr.ib(None, converter=lambda x: x or "", type=str)
location_id = attr.ib(None, converter=lambda x: x or "", type=Optional[str])
#: ID of the plan creator
author_id = attr.ib(None, type=str)
author_id = attr.ib(None, type=Optional[str])
#: `User` ids mapped to their `GuestStatus`
guests = attr.ib(None, type=Mapping[str, GuestStatus])
guests = attr.ib(None, type=Optional[Mapping[str, GuestStatus]])
@property
def going(self) -> Sequence[str]:

View File

@@ -2,7 +2,7 @@ import attr
from . import Attachment
from .._common import attrs_default
from typing import Any
from typing import Any, Optional
@attrs_default
@@ -24,9 +24,9 @@ class QuickReplyText(QuickReply):
"""Represents a text quick reply."""
#: Title of the quick reply
title = attr.ib(None, type=str)
#: URL of the quick reply image (optional)
image_url = attr.ib(None, type=str)
title = attr.ib(None, type=Optional[str])
#: URL of the quick reply image
image_url = attr.ib(None, type=Optional[str])
#: Type of the quick reply
_type = "text"
@@ -43,8 +43,8 @@ class QuickReplyLocation(QuickReply):
class QuickReplyPhoneNumber(QuickReply):
"""Represents a phone number quick reply (Doesn't work on mobile)."""
#: URL of the quick reply image (optional)
image_url = attr.ib(None, type=str)
#: URL of the quick reply image
image_url = attr.ib(None, type=Optional[str])
#: Type of the quick reply
_type = "user_phone_number"
@@ -53,8 +53,8 @@ class QuickReplyPhoneNumber(QuickReply):
class QuickReplyEmail(QuickReply):
"""Represents an email quick reply (Doesn't work on mobile)."""
#: URL of the quick reply image (optional)
image_url = attr.ib(None, type=str)
#: URL of the quick reply image
image_url = attr.ib(None, type=Optional[str])
#: Type of the quick reply
_type = "user_email"

View File

@@ -2,34 +2,36 @@ import attr
from . import Image, Attachment
from .._common import attrs_default
from typing import Optional
@attrs_default
class Sticker(Attachment):
"""Represents a Facebook sticker that has been sent to a thread as an attachment."""
#: The sticker-pack's ID
pack = attr.ib(None, type=str)
pack = attr.ib(None, type=Optional[str])
#: Whether the sticker is animated
is_animated = attr.ib(False, type=bool)
# If the sticker is animated, the following should be present
#: URL to a medium spritemap
medium_sprite_image = attr.ib(None, type=str)
medium_sprite_image = attr.ib(None, type=Optional[str])
#: URL to a large spritemap
large_sprite_image = attr.ib(None, type=str)
large_sprite_image = attr.ib(None, type=Optional[str])
#: The amount of frames present in the spritemap pr. row
frames_per_row = attr.ib(None, type=int)
frames_per_row = attr.ib(None, type=Optional[int])
#: The amount of frames present in the spritemap pr. column
frames_per_col = attr.ib(None, type=int)
frames_per_col = attr.ib(None, type=Optional[int])
#: The total amount of frames in the spritemap
frame_count = attr.ib(None, type=int)
frame_count = attr.ib(None, type=Optional[int])
#: The frame rate the spritemap is intended to be played in
frame_rate = attr.ib(None, type=int)
frame_rate = attr.ib(None, type=Optional[int])
#: The sticker's image
image = attr.ib(None, type=Image)
image = attr.ib(None, type=Optional[Image])
#: The sticker's label/name
label = attr.ib(None, type=str)
label = attr.ib(None, type=Optional[str])
@classmethod
def _from_graphql(cls, data):

View File

@@ -157,7 +157,7 @@ class Session:
_session = attr.ib(factory=session_factory, type=requests.Session)
_counter = attr.ib(0, type=int)
_client_id = attr.ib(factory=client_id_factory, type=str)
_logout_h = attr.ib(None, type=str)
_logout_h = attr.ib(None, type=Optional[str])
@property
def user(self):

View File

@@ -4,7 +4,8 @@ from ._abc import ThreadABC
from . import _user
from .._common import attrs_default
from .. import _util, _session, _graphql, _models
from typing import Sequence, Iterable, Set, Mapping
from typing import Sequence, Iterable, Set, Mapping, Optional
@attrs_default
@@ -179,31 +180,31 @@ class GroupData(Group):
"""
#: The group's picture
photo = attr.ib(None, type="_models.Image")
photo = attr.ib(None, type=Optional["_models.Image"])
#: The name of the group
name = attr.ib(None, type=str)
name = attr.ib(None, type=Optional[str])
#: When the group was last active / when the last message was sent
last_active = attr.ib(None, type=datetime.datetime)
last_active = attr.ib(None, type=Optional[datetime.datetime])
#: Number of messages in the group
message_count = attr.ib(None, type=int)
message_count = attr.ib(None, type=Optional[int])
#: Set `Plan`
plan = attr.ib(None, type="_models.PlanData")
plan = attr.ib(None, type=Optional["_models.PlanData"])
#: The group thread's participant user ids
participants = attr.ib(factory=set, type=Set[str])
#: A dictionary, containing user nicknames mapped to their IDs
nicknames = attr.ib(factory=dict, type=Mapping[str, str])
#: The groups's message color
color = attr.ib(None, type=str)
color = attr.ib(None, type=Optional[str])
#: The groups's default emoji
emoji = attr.ib(None, type=str)
emoji = attr.ib(None, type=Optional[str])
# User ids of thread admins
admins = attr.ib(factory=set, type=Set[str])
# True if users need approval to join
approval_mode = attr.ib(None, type=bool)
approval_mode = attr.ib(None, type=Optional[bool])
# Set containing user IDs requesting to join
approval_requests = attr.ib(factory=set, type=Set[str])
# Link for joining group
join_link = attr.ib(None, type=str)
join_link = attr.ib(None, type=Optional[str])
@classmethod
def _from_graphql(cls, session, data):

View File

@@ -4,6 +4,8 @@ from ._abc import ThreadABC
from .._common import attrs_default
from .. import _session, _models
from typing import Optional
@attrs_default
class Page(ThreadABC):
@@ -39,21 +41,21 @@ class PageData(Page):
#: The name of the page
name = attr.ib(type=str)
#: When the thread was last active / when the last message was sent
last_active = attr.ib(None, type=datetime.datetime)
last_active = attr.ib(None, type=Optional[datetime.datetime])
#: Number of messages in the thread
message_count = attr.ib(None, type=int)
message_count = attr.ib(None, type=Optional[int])
#: Set `Plan`
plan = attr.ib(None, type="_models.PlanData")
plan = attr.ib(None, type=Optional["_models.PlanData"])
#: The page's custom URL
url = attr.ib(None, type=str)
url = attr.ib(None, type=Optional[str])
#: The name of the page's location city
city = attr.ib(None, type=str)
city = attr.ib(None, type=Optional[str])
#: Amount of likes the page has
likes = attr.ib(None, type=int)
likes = attr.ib(None, type=Optional[int])
#: Some extra information about the page
sub_title = attr.ib(None, type=str)
sub_title = attr.ib(None, type=Optional[str])
#: The page's category
category = attr.ib(None, type=str)
category = attr.ib(None, type=Optional[str])
@classmethod
def _from_graphql(cls, session, data):

View File

@@ -4,6 +4,8 @@ from ._abc import ThreadABC
from .._common import log, attrs_default
from .. import _util, _session, _models
from typing import Optional
GENDERS = {
# For standard requests
@@ -111,27 +113,27 @@ class UserData(User):
#: The users first name
first_name = attr.ib(type=str)
#: The users last name
last_name = attr.ib(None, type=str)
last_name = attr.ib(None, type=Optional[str])
#: Datetime when the thread was last active / when the last message was sent
last_active = attr.ib(None, type=datetime.datetime)
last_active = attr.ib(None, type=Optional[datetime.datetime])
#: Number of messages in the thread
message_count = attr.ib(None, type=int)
message_count = attr.ib(None, type=Optional[int])
#: Set `Plan`
plan = attr.ib(None, type="_models.PlanData")
plan = attr.ib(None, type=Optional["_models.PlanData"])
#: The profile URL. ``None`` for Messenger-only users
url = attr.ib(None, type=str)
url = attr.ib(None, type=Optional[str])
#: The user's gender
gender = attr.ib(None, type=str)
gender = attr.ib(None, type=Optional[str])
#: From 0 to 1. How close the client is to the user
affinity = attr.ib(None, type=float)
affinity = attr.ib(None, type=Optional[float])
#: The user's nickname
nickname = attr.ib(None, type=str)
nickname = attr.ib(None, type=Optional[str])
#: The clients nickname, as seen by the user
own_nickname = attr.ib(None, type=str)
own_nickname = attr.ib(None, type=Optional[str])
#: The message color
color = attr.ib(None, type=str)
color = attr.ib(None, type=Optional[str])
#: The default emoji
emoji = attr.ib(None, type=str)
emoji = attr.ib(None, type=Optional[str])
@staticmethod
def _get_other_user(data):

View File

@@ -20,6 +20,13 @@ USER_AGENTS = [
]
def int_or_none(inp: Any) -> Optional[int]:
try:
return int(inp)
except Exception:
return None
def get_limits(limit: Optional[int], max_limit: int) -> Iterable[int]:
"""Helper that generates limits based on a max limit."""
if limit is None: