Skip to content

Commit

Permalink
address comments from @sherifnada
Browse files Browse the repository at this point in the history
  • Loading branch information
eugene-kulak committed Feb 3, 2022
1 parent 2a4bf87 commit 58b21c2
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 59 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,19 @@
import logging
from abc import ABC, abstractmethod
from enum import Enum
from typing import Any, List, Mapping, Optional
from typing import Any, List, Mapping, Optional, Sequence, Iterator, Union

import pendulum
from facebook_business.adobjects.adaccount import AdAccount
from facebook_business.adobjects.adreportrun import AdReportRun
from facebook_business.adobjects.campaign import Campaign
from facebook_business.adobjects.objectparser import ObjectParser
from facebook_business.api import FacebookAdsApiBatch, FacebookResponse
from facebook_business.api import FacebookAdsApiBatch, FacebookResponse, FacebookAdsApi

logger = logging.getLogger("airbyte")


def chunks(data, n):
def chunks(data: Sequence[Any], n: int) -> Iterator[Any]:
"""Yield successive n-sized chunks from lst."""
for i in range(0, len(data), n):
yield data[i : i + n]
Expand All @@ -37,6 +38,21 @@ class Status(str, Enum):
class AsyncJob(ABC):
"""Abstract AsyncJob base class"""

def __init__(self, api: FacebookAdsApi, interval: pendulum.Period):
""" Init generic async job
:param api: FB API instance (to create batch, etc)
:param interval: interval for which the job will fetch data
"""
self._api = api
self._interval = interval
self._attempt_number = 1

@property
def key(self) -> str:
"""Job identifier, in most cases start of the interval"""
return str(self._interval.start.date())

@abstractmethod
def start(self):
"""Start remote job"""
Expand All @@ -46,9 +62,9 @@ def restart(self):
"""Restart failed job"""

@property
@abstractmethod
def restart_number(self):
"""Number of restarts"""
def attempt_number(self):
"""Number of attempts"""
return self._attempt_number

@property
@abstractmethod
Expand All @@ -61,21 +77,28 @@ def failed(self) -> bool:
"""Tell if the job previously failed"""

@abstractmethod
def update_job(self, batch=None):
"""Method to retrieve job's status, separated because of retry handler"""
def update_job(self, batch: Optional[FacebookAdsApiBatch] = None):
""" Method to retrieve job's status, separated because of retry handler
:param batch: FB batch executor
"""

@abstractmethod
def get_result(self) -> Any:
def get_result(self) -> Iterator[Any]:
"""Retrieve result of the finished job."""

@abstractmethod
def split_job(self) -> "AsyncJob":
"""Split existing job in few smaller ones grouped by ParentAsyncJob"""


class ParentAsyncJob(AsyncJob):
"""Group of async jobs"""

def __init__(self, api, jobs: List[AsyncJob]):
self._api = api
def __init__(self, jobs: List[AsyncJob], **kwargs):
"""Initialize jobs"""
super().__init__(**kwargs)
self._jobs = jobs
self._restart_number = 0

def start(self):
"""Start each job in the group."""
Expand All @@ -87,12 +110,7 @@ def restart(self):
for job in self._jobs:
if job.failed:
job.restart()
self._restart_number = max(self._restart_number, job.restart_number)

@property
def restart_number(self):
"""Number of restarts"""
return self._restart_number
self._attempt_number = max(self._attempt_number, job.attempt_number)

@property
def completed(self) -> bool:
Expand All @@ -104,7 +122,7 @@ def failed(self) -> bool:
"""Tell if any job previously failed"""
return any(job.failed for job in self._jobs)

def update_job(self, batch=None):
def update_job(self, batch: Optional[FacebookAdsApiBatch] = None):
"""Checks jobs status in advance and restart if some failed."""
batch = self._api.new_batch()
unfinished_jobs = [job for job in self._jobs if not job.completed]
Expand All @@ -117,51 +135,62 @@ def update_job(self, batch=None):
# FacebookAdsApiBatch object with those calls
batch = batch.execute()

def get_result(self) -> Any:
def get_result(self) -> Iterator[Any]:
"""Retrieve result of the finished job."""
for job in self._jobs:
yield from job.get_result()

def split_job(self) -> "AsyncJob":
"""Split existing job in few smaller ones grouped by ParentAsyncJob class"""
"""Split existing job in few smaller ones grouped by ParentAsyncJob class. Will be implemented in future versions."""
raise RuntimeError("Splitting of ParentAsyncJob is not allowed.")


class InsightAsyncJob(AsyncJob):
"""AsyncJob wraps FB AdReport class and provides interface to restart/retry the async job"""

def __init__(self, api, edge_object: Any, params: Mapping[str, Any], key: Optional[Any] = None):
page_size = 100

def __init__(self, edge_object: Union[AdAccount, Campaign], params: Mapping[str, Any], **kwargs):
"""Initialize
:param api: FB API
:param edge_object: Account, Campaign, (AdSet or Ad in future)
:param params: job params, required to start/restart job
"""
self._api = api
self._params = params
super().__init__(**kwargs)
self._params = dict(params)
self._params["time_range"] = {
"since": self._interval.start.to_date_string(),
"until": self._interval.end.to_date_string(),
}

self._edge_object = edge_object
self._job: Optional[AdReportRun] = None
self._start_time = None
self._finish_time = None
self._failed = False
self._restart_number = 0
self.key = key

def split_job(self) -> ParentAsyncJob:
def split_job(self) -> "AsyncJob":
"""Split existing job in few smaller ones grouped by ParentAsyncJob class.
TODO: use some cache to avoid expensive queries across different streams.
"""
campaign_params = dict(copy.deepcopy(self._params))
# get campaigns from attribution window as well (28 day + 1 current day)
new_start = pendulum.parse(self._params["time_range"]["since"]) - pendulum.duration(days=28 + 1)
new_start = self._interval.start.date() - pendulum.duration(days=28 + 1)
campaign_params.update(fields=["campaign_id"], level="campaign")
campaign_params["time_range"].update(since=new_start.to_date_string())
campaign_params.pop("time_increment") # query all days
result = self._edge_object.get_insights(params=campaign_params)
campaign_ids = set(row["campaign_id"] for row in result)
logger.info(f"Got {len(campaign_ids)} campaigns for period {self._params['time_range']}: {campaign_ids}")
logger.info(
"Got %(num)s campaigns for period %(period)s: %(campaign_ids)s",
num=len(campaign_ids),
period=self._params['time_range'],
campaign_ids=campaign_ids
)

return ParentAsyncJob(self._api, jobs=[InsightAsyncJob(self._api, Campaign(pk), self._params) for pk in campaign_ids])
jobs = [InsightAsyncJob(api=self._api, edge_object=Campaign(pk), params=self._params) for pk in campaign_ids]
return ParentAsyncJob(api=self._api, interval=self._interval, jobs=jobs)

def start(self):
"""Start remote job"""
Expand All @@ -170,10 +199,13 @@ def start(self):

self._job = self._edge_object.get_insights(params=self._params, is_async=True)
self._start_time = pendulum.now()
job_id = self._job["report_run_id"]
time_range = self._params["time_range"]
breakdowns = self._params["breakdowns"]
logger.info(f"Created AdReportRun: {job_id} to sync insights {time_range} with breakdown {breakdowns} for {self._edge_object}")
logger.info(
"Created AdReportRun: %(job_id)s to sync insights %(time_range)s with breakdown %(breakdowns)s for %(obj)s",
job_id = self._job["report_run_id"],
time_range=self._params["time_range"],
breakdowns=self._params["breakdowns"],
obj=self._edge_object,
)

def restart(self):
"""Restart failed job"""
Expand All @@ -184,14 +216,9 @@ def restart(self):
self._failed = False
self._start_time = None
self._finish_time = None
self._restart_number += 1
self._attempt_number += 1
self.start()
logger.info(f"{self}: restarted")

@property
def restart_number(self):
"""Number of restarts"""
return self._restart_number
logger.info("%s: restarted", self)

@property
def elapsed_time(self) -> Optional[pendulum.duration]:
Expand All @@ -218,22 +245,23 @@ def failed(self) -> bool:

def _batch_success_handler(self, response: FacebookResponse):
"""Update job status from response"""
print("GOT", response.json())
self._job = ObjectParser(reuse_object=self._job).parse_single(response.json())
self._check_status()

def _batch_failure_handler(self, response: FacebookResponse):
"""Update job status from response"""
logger.info(f"Request failed with response: {response.body()}")
logger.info("Request failed with response: %s", response.body())

def update_job(self, batch: Optional[FacebookAdsApiBatch] = None):
"""Method to retrieve job's status, separated because of retry handler"""
if not self._job:
raise RuntimeError(f"{self}: Incorrect usage of the method - the job is not started")

if self.completed:
job_progress_pct = self._job["async_percent_completion"]
logger.info(f"{self} is {job_progress_pct}% complete ({self._job['async_status']})")
logger.info(
"%(job)s is %(percent)s complete (%(status)s)",
job=self, percent=self._job["async_percent_completion"], status=self._job['async_status']
)
# No need to update job status if its already completed
return

Expand All @@ -248,30 +276,35 @@ def _check_status(self) -> bool:
:return: True if the job is completed, False - if the job is still running
"""
job_progress_pct = self._job["async_percent_completion"]
job_status = self._job["async_status"]
logger.info(f"{self} is {job_progress_pct}% complete ({job_status})")
logger.info(
"%(job)s is %(percent)s complete (%(status)s)",
job=self, percent=self._job["async_percent_completion"], status=job_status,
)

if job_status == Status.COMPLETED:
self._finish_time = pendulum.now() # TODO: is not actual running time, but interval between check_status calls
return True
elif job_status in [Status.FAILED, Status.SKIPPED]:
self._finish_time = pendulum.now()
self._failed = True
logger.info(f"{self._job} has status {job_status} after {self.elapsed_time.in_seconds()} seconds.")
logger.info(
"%(job)s has status %(status)s after %(elapsed)s seconds.",
job=self, status=job_status, elapsed=self.elapsed_time.in_seconds(),
)
return True

return False

def get_result(self) -> Any:
"""Retrieve result of the finished job."""
if not self._job or self.failed:
raise RuntimeError(f"{self}: Incorrect usage of get_result - the job is not started of failed")
return self._job.get_result(params={"limit": 100})
raise RuntimeError(f"{self}: Incorrect usage of get_result - the job is not started or failed")
return self._job.get_result(params={"limit": self.page_size})

def __str__(self) -> str:
"""String representation of the job wrapper."""
job_id = self._job["report_run_id"] if self._job else "<None>"
time_range = self._params["time_range"]
breakdowns = self._params["breakdowns"]
return f"AdReportRun(id={job_id}, {self._edge_object}, time_range={time_range}, breakdowns={breakdowns}"
return f"InsightAsyncJob(id={job_id}, {self._edge_object}, time_range={time_range}, breakdowns={breakdowns}"
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class InsightAsyncJobManager:

# When current insights throttle hit this value no new jobs added.
THROTTLE_LIMIT = 70
FAILED_JOBS_RESTART_COUNT = 5
MAX_NUMBER_OF_ATTEMPTS = 5
# Time to wait before checking job status update again.
JOB_STATUS_UPDATE_SLEEP_SECONDS = 30
# Maximum of concurrent jobs that could be scheduled. Since throttling
Expand Down Expand Up @@ -113,15 +113,15 @@ def _check_jobs_status_and_restart(self) -> List[AsyncJob]:
self._wait_throttle_limit_down()
for job in self._running_jobs:
if job.failed:
if job.restart_number >= self.FAILED_JOBS_RESTART_COUNT:
raise JobException(f"Job {job} failed more than {self.FAILED_JOBS_RESTART_COUNT} times. Terminating...")
elif job.restart_number:
logger.info(f"Job {job} failed, trying to split job into smaller chunks (campaigns).")
if job.attempt_number >= self.MAX_NUMBER_OF_ATTEMPTS:
raise JobException("%s: failed more than {self.MAX_NUMBER_OF_ATTEMPTS} times. Terminating...", job)
elif job.attempt_number == 2:
logger.info("%s: failed second time, trying to split job into smaller chunks (campaigns).", job)
group_job = job.split_job()
running_jobs.append(group_job)
group_job.start()
else:
logger.info(f"Job {job} failed, restarting")
logger.info("%s: failed, restarting", job)
job.restart()
running_jobs.append(job)
failed_num += 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ def state(self) -> MutableMapping[str, Any]:
return {}

@state.setter
def state(self, value: MutableMapping[str, Any]):
def state(self, value: Mapping[str, Any]):
"""State setter"""
self._cursor_value = pendulum.parse(value[self.cursor_field]).date() if value.get(self.cursor_field) else None
self._completed_slices = set(pendulum.parse(v).date() for v in value.get("slices", []))
Expand Down Expand Up @@ -177,7 +177,7 @@ def _generate_async_jobs(self, params: Mapping) -> Iterator[AsyncJob]:
yield InsightAsyncJob(self._api.api, edge_object=self._api.account, params=total_params, key=ts_start)

def stream_slices(
self, sync_mode, cursor_field: List[str] = None, stream_state: Mapping[str, Any] = None
self, sync_mode: SyncMode, cursor_field: List[str] = None, stream_state: Mapping[str, Any] = None
) -> Iterable[Optional[Mapping[str, Any]]]:

"""Slice by date periods and schedule async job for each period, run at most MAX_ASYNC_JOBS jobs at the same time.
Expand Down

1 comment on commit 58b21c2

@github-actions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SonarQube Report

SonarQube report for Airbyte Connectors Source Facebook Marketing(#9805)

Measures

Name Value Name Value Name Value
Lines of Code 1761 Code Smells 34 Bugs 1
Reliability Rating B Duplicated Blocks 2 Coverage 14.6
Vulnerabilities 0 Duplicated Lines (%) 6.0 Quality Gate Status ERROR
Security Rating A Lines to Cover 205 Blocker Issues 0
Critical Issues 2 Major Issues 22 Minor Issues 176

Detected Issues

Rule File Description Message
python:S1226 (MINOR) streams/async_job.py:125 Function parameters initial values should not be ignored Introduce a new variable or use its initial value before reassigning 'batch'.
python:S3457 (MAJOR) streams/async_job.py:186 String formatting should be used correctly Add argument(s) corresponding to the message's replacement field(s).
python:S3457 (MAJOR) streams/async_job.py:203 String formatting should be used correctly Add argument(s) corresponding to the message's replacement field(s).
python:S3457 (MAJOR) streams/async_job.py:262 String formatting should be used correctly Add argument(s) corresponding to the message's replacement field(s).
python:S3457 (MAJOR) streams/async_job.py:281 String formatting should be used correctly Add argument(s) corresponding to the message's replacement field(s).
python:S3457 (MAJOR) streams/async_job.py:292 String formatting should be used correctly Add argument(s) corresponding to the message's replacement field(s).
python:black_need_format (MINOR) streams/async_job.py Please run one of the commands: "black --config ./pyproject.toml <path_to_updated_folder>" or "./gradlew format" 6 code part(s) should be updated.
python:isort_need_format (MINOR) streams/async_job.py Please run one of the commands: "isort <path_to_updated_folder>" or "./gradlew format" 1 code part(s) should be updated.
python:mypy_no_untyped_def (MINOR) streams/async_job.py:65 Check that every function has an annotation Function is missing a return type annotation . Code line: def attempt_number(self):
python:mypy_no_untyped_def (MINOR) streams/async_job.py:80 Check that every function has an annotation Function is missing a return type annotation . Code line: def update_job(self, batch: Optional[FacebookAdsApiBatch] = None):
python:mypy_no_untyped_def (MINOR) streams/async_job.py:98 Check that every function has an annotation Function is missing a type annotation for one or more arguments . Code line: def init(self, jobs: List[AsyncJob], **kwargs):
python:mypy_no_untyped_def (MINOR) streams/async_job.py:125 Check that every function has an annotation Function is missing a return type annotation . Code line: def update_job(self, batch: Optional[FacebookAdsApiBatch] = None):
python:mypy_no_untyped_def (MINOR) streams/async_job.py:153 Check that every function has an annotation Function is missing a type annotation for one or more arguments . Code line: def init(self, edge_object: Union[AdAccount, Campaign], params...
python:mypy_call_arg (MINOR) streams/async_job.py:185 Check number, names and kinds of arguments in calls Unexpected keyword argument "num" for "info" of "Logger" . Code line: logger.info(
python:mypy_call_arg (MINOR) streams/async_job.py:185 Check number, names and kinds of arguments in calls Unexpected keyword argument "period" for "info" of "Logger" . Code line: logger.info(
python:mypy_call_arg (MINOR) streams/async_job.py:185 Check number, names and kinds of arguments in calls Unexpected keyword argument "campaign_ids" for "info" of "Logger" . Code line: logger.info(
python:mypy_arg_type (MINOR) streams/async_job.py:193 Check argument types in calls Argument "jobs" to "ParentAsyncJob" has incompatible type "List[InsightAsyncJob]"; expected "List[AsyncJob]" . Code line: ...turn ParentAsyncJob(api=self._api, interval=self._interval, jobs=jobs)
flake8:E251 (MAJOR) streams/async_job.py:204 unexpected spaces around keyword / parameter equals unexpected spaces around keyword / parameter equals
flake8:E251 (MAJOR) streams/async_job.py:204 unexpected spaces around keyword / parameter equals unexpected spaces around keyword / parameter equals
python:mypy_call_arg (MINOR) streams/async_job.py:261 Check number, names and kinds of arguments in calls Unexpected keyword argument "job" for "info" of "Logger" . Code line: logger.info(
python:mypy_call_arg (MINOR) streams/async_job.py:261 Check number, names and kinds of arguments in calls Unexpected keyword argument "percent" for "info" of "Logger" . Code line: logger.info(
python:mypy_call_arg (MINOR) streams/async_job.py:261 Check number, names and kinds of arguments in calls Unexpected keyword argument "status" for "info" of "Logger" . Code line: logger.info(
python:mypy_call_arg (MINOR) streams/async_job.py:280 Check number, names and kinds of arguments in calls Unexpected keyword argument "job" for "info" of "Logger" . Code line: logger.info(
python:mypy_call_arg (MINOR) streams/async_job.py:280 Check number, names and kinds of arguments in calls Unexpected keyword argument "percent" for "info" of "Logger" . Code line: logger.info(
python:mypy_call_arg (MINOR) streams/async_job.py:280 Check number, names and kinds of arguments in calls Unexpected keyword argument "status" for "info" of "Logger" . Code line: logger.info(
python:mypy_call_arg (MINOR) streams/async_job.py:291 Check number, names and kinds of arguments in calls Unexpected keyword argument "job" for "info" of "Logger" . Code line: logger.info(
python:mypy_call_arg (MINOR) streams/async_job.py:291 Check number, names and kinds of arguments in calls Unexpected keyword argument "status" for "info" of "Logger" . Code line: logger.info(
python:mypy_call_arg (MINOR) streams/async_job.py:291 Check number, names and kinds of arguments in calls Unexpected keyword argument "elapsed" for "info" of "Logger" . Code line: logger.info(
python:mypy_attr_defined (MINOR) streams/async_job.py:293 Check that attribute exists pendulum.duration? has no attribute "in_seconds" . Code line: ... job=self, status=job_status, elapsed=self.elapsed_time.in_...
python:isort_need_format (MINOR) streams/async_job_manager.py Please run one of the commands: "isort <path_to_updated_folder>" or "./gradlew format" 1 code part(s) should be updated.
python:mypy_no_untyped_def (MINOR) streams/base_insight_streams.py:132 Check that every function has an annotation Function is missing a return type annotation . Code line: def state(self, value: Mapping[str, Any]):
python:mypy_no_untyped_def (MINOR) streams/async_job_manager.py:88 Check that every function has an annotation Function is missing a return type annotation . Code line: def _check_jobs_status(self):
python:isort_need_format (MINOR) unit_tests/test_base_insight_streams.py Please run one of the commands: "isort <path_to_updated_folder>" or "./gradlew format" 1 code part(s) should be updated.
python:mypy_no_untyped_def (MINOR) streams/base_insight_streams.py Check that every function has an annotation Function is missing a type annotation for one or more arguments . Code line: def stream_slices(
python:mypy_name_defined (MINOR) streams/base_insight_streams.py:138 Check that name is defined Function is missing a return type annotation [no-untyped-def] def get_updated_state(self, current_stream_state: MutableMapping[s...
python:mypy_name_defined (MINOR) streams/base_insight_streams.py:196 Check that name is defined Incompatible types in assignment (expression has type "Mapping[str, Any]", variable has type "MutableMapping[str, Any]") [assignment] self.state = stream_state
python:mypy_misc (MINOR) source_facebook_marketing/spec.py:18 Miscellaneous other checks Enum() expects a string, tuple, list or dict literal as the second argument . Code line: ValidFields = Enum("ValidEnums", AdsInsights.Field.dict)
python:mypy_misc (MINOR) source_facebook_marketing/spec.py:19 Miscellaneous other checks Enum() expects a string, tuple, list or dict literal as the second argument . Code line: ValidBreakdowns = Enum("ValidBreakdowns", AdsInsights.Breakdowns.__dic...
python:mypy_misc (MINOR) source_facebook_marketing/spec.py:20 Miscellaneous other checks Enum() expects a string, tuple, list or dict literal as the second argument . Code line: ValidActionBreakdowns = Enum("ValidActionBreakdowns", AdsInsights.Acti...
python:isort_need_format (MINOR) streams/base_insight_streams.py Please run one of the commands: "isort <path_to_updated_folder>" or "./gradlew format" 1 code part(s) should be updated.
python:isort_need_format (MINOR) unit_tests/test_async_job.py Please run one of the commands: "isort <path_to_updated_folder>" or "./gradlew format" 1 code part(s) should be updated.
python:isort_need_format (MINOR) unit_tests/test_source.py Please run one of the commands: "isort <path_to_updated_folder>" or "./gradlew format" 1 code part(s) should be updated.
python:isort_need_format (MINOR) integration_tests/test_streams.py Please run one of the commands: "isort <path_to_updated_folder>" or "./gradlew format" 1 code part(s) should be updated.
python:isort_need_format (MINOR) source_facebook_marketing/source.py Please run one of the commands: "isort <path_to_updated_folder>" or "./gradlew format" 1 code part(s) should be updated.
python:mypy_no_untyped_def (MINOR) streams/async_job.py:57 Check that every function has an annotation Function is missing a return type annotation . Code line: def start(self):
python:mypy_no_untyped_def (MINOR) streams/async_job.py:103 Check that every function has an annotation Function is missing a return type annotation . Code line: def start(self):
python:mypy_no_untyped_def (MINOR) streams/async_job.py:195 Check that every function has an annotation Function is missing a return type annotation . Code line: def start(self):
python:isort_need_format (MINOR) unit_tests/test_client.py Please run one of the commands: "isort <path_to_updated_folder>" or "./gradlew format" 1 code part(s) should be updated.
python:mypy_attr_defined (MINOR) streams/init.py Check that attribute exists Module "source_facebook_marketing.streams.streams" does not explicitly export attribute "AdsInsights"; implicit reexport disabled . Code line: from .streams import (
python:mypy_no_untyped_def (MINOR) streams/base_streams.py:40 Check that every function has an annotation Function is missing a type annotation for one or more arguments . Code line: def init(self, api: "API", include_deleted: bool = False, **kw...
python:mypy_no_untyped_def (MINOR) streams/base_streams.py:50 Check that every function has an annotation Function is missing a type annotation . Code line: def _execute_batch(self, batch):
python:mypy_no_untyped_def (MINOR) streams/base_streams.py:61 Check that every function has an annotation Function is missing a return type annotation . Code line: def success(response: FacebookResponse):
python:mypy_no_untyped_def (MINOR) streams/base_streams.py:64 Check that every function has an annotation Function is missing a return type annotation . Code line: def failure(response: FacebookResponse):
python:S1134 (MAJOR) streams/base_streams.py:65 Track uses of "FIXME" tags Take the required action to fix the issue indicated by this "FIXME" comment.
python:mypy_no_redef (MINOR) streams/base_streams.py:74 Check that each name is defined once Name "api_batch" already defined on line 68 . Code line: api_batch: FacebookAdsApiBatch = self._api.api.new_bat...
python:mypy_assignment (MINOR) streams/base_streams.py:91 Check that assigned value is compatible with target Incompatible types in assignment (expression has type "Iterable[MutableMapping[str, Any]]", variable has type "Generator[Any, None, None]") . Code line: loaded_records_iter = self.execute_in_batch(loaded_records...
python:mypy_no_untyped_def (MINOR) streams/base_streams.py:105 Check that every function has an annotation Function is missing a type annotation for one or more arguments . Code line: def request_params(self, **kwargs) -> MutableMapping[str, Any]:
python:mypy_no_untyped_def (MINOR) streams/base_streams.py:143 Check that every function has an annotation Function is missing a type annotation for one or more arguments . Code line: def init(self, start_date: datetime, end_date: datetime, **kwa...
python:mypy_no_untyped_def (MINOR) streams/base_streams.py:148 Check that every function has an annotation Function is missing a return type annotation . Code line: def get_updated_state(self, current_stream_state: MutableMapping[s...
python:mypy_attr_defined (MINOR) streams/base_streams.py:153 Check that attribute exists Module has no attribute "parse" . Code line: max_cursor = max(pendulum.parse(state_value), pendulum.parse(r...
python:mypy_no_untyped_def (MINOR) streams/base_streams.py:162 Check that every function has an annotation Function is missing a type annotation for one or more arguments . Code line: def request_params(self, stream_state: Mapping[str, Any], **kwargs...
python:mypy_override (MINOR) streams/base_streams.py:162 Check that method override is compatible with base class Signature of "request_params" incompatible with supertype "FBMarketingStream" . Code line: def request_params(self, stream_state: Mapping[str, Any], **kwargs...
python:mypy_no_any_return (MINOR) streams/base_streams.py:166 Reject returning value with "Any" type if return type is not "Any" Returning Any from function declared to return "MutableMapping[str, Any]" . Code line: return params
python:mypy_attr_defined (MINOR) streams/base_streams.py:171 Check that attribute exists Module has no attribute "parse" . Code line: ...value = self.start_date if not state_value else pendulum.parse(state...
python:mypy_var_annotated (MINOR) streams/streams.py Require variable annotation if type can't be inferred Need type annotation for "breakdowns" (hint: "breakdowns: List[] = ...") . Code line: breakdowns = []
python:mypy_override (MINOR) streams/base_insight_streams.py:95 Check that method override is compatible with base class Signature of "primary_key" incompatible with supertype "FBMarketingStream" . Code line: def primary_key(self) -> Optional[Union[str, List[str], List[List[...
python:mypy_no_untyped_def (MINOR) streams/async_job_manager.py:49 Check that every function has an annotation Function is missing a return type annotation . Code line: def _start_jobs(self):
python:isort_need_format (MINOR) unit_tests/test_async_job_manager.py Please run one of the commands: "isort <path_to_updated_folder>" or "./gradlew format" 1 code part(s) should be updated.
python:mypy_index (MINOR) streams/streams.py Check indexing operations Unsupported target for indexed assignment ("Mapping[str, Any]") . Code line: record["thumbnail_data_url"] = fetch_thumbnail_data_ur...
python:isort_need_format (MINOR) source_facebook_marketing/api.py Please run one of the commands: "isort <path_to_updated_folder>" or "./gradlew format" 1 code part(s) should be updated.
python:mypy_attr_defined (MINOR) streams/base_insight_streams.py:134 Check that attribute exists Module has no attribute "parse" . Code line: self._cursor_value = pendulum.parse(value[self.cursor_field])....
python:mypy_attr_defined (MINOR) streams/base_insight_streams.py:135 Check that attribute exists Module has no attribute "parse" . Code line: self._completed_slices = set(pendulum.parse(v).date() for v in...
python:mypy_no_any_return (MINOR) streams/base_insight_streams.py:148 Reject returning value with "Any" type if return type is not "Any" Returning Any from function declared to return "Iterator[Any]" . Code line: return date_range.range("days", self.time_increment)
python:mypy_no_untyped_def (MINOR) source_facebook_marketing/api.py:106 Check that every function has an annotation Function is missing a return type annotation . Code line: def _update_insights_throttle_limit(self, response: FacebookRespon...
python:mypy_no_untyped_def (MINOR) streams/async_job.py Check that every function has an annotation Function is missing a type annotation . Code line: def chunks(data, n):
python:mypy_no_untyped_def (MINOR) streams/async_job.py Check that every function has an annotation Function is missing a return type annotation . Code line: def restart_number(self):
python:mypy_no_untyped_def (MINOR) streams/async_job.py Check that every function has an annotation Function is missing a type annotation for one or more arguments . Code line: def init(self, api, jobs: List[AsyncJob]):
python:mypy_no_untyped_def (MINOR) streams/async_job.py Check that every function has an annotation Function is missing a return type annotation . Code line: def restart_number(self):
python:mypy_no_untyped_def (MINOR) streams/async_job.py Check that every function has an annotation Function is missing a type annotation for one or more arguments . Code line: def init(self, api, edge_object: Any, params: Mapping[str, Any...
python:mypy_attr_defined (MINOR) streams/async_job.py Check that attribute exists Module has no attribute "parse" . Code line: new_start = pendulum.parse(self._params["time_range"]["since"]...
python:mypy_valid_type (MINOR) streams/async_job.py Check that type (annotation) is valid Function is missing a return type annotation [no-untyped-def] def restart_number(self):
flake8:E203 (MAJOR) streams/async_job.py:24 whitespace before ‘:’ whitespace before ':'
python:mypy_no_untyped_def (MINOR) streams/async_job.py:255 Check that every function has an annotation Function is missing a return type annotation . Code line: def update_job(self, batch: Optional[FacebookAdsApiBatch] = None):
python:S3776 (CRITICAL) streams/async_job_manager.py Cognitive Complexity of functions should not be too high Refactor this function to reduce its Cognitive Complexity from 18 to the 15 allowed.
python:S112 (MAJOR) streams/async_job_manager.py "Exception" and "BaseException" should not be raised Replace this generic exception class with a more specific one.
python:mypy_var_annotated (MINOR) streams/async_job_manager.py:47 Require variable annotation if type can't be inferred Need type annotation for "_running_jobs" (hint: "_running_jobs: List[] = ...") . Code line: self._running_jobs = []
python:mypy_name_defined (MINOR) streams/base_insight_streams.py Check that name is defined Function is missing a type annotation for one or more arguments [no-untyped-def] def stream_slices(self, **kwargs) -> Iterable[Optional[Mapping[str...
python:mypy_no_untyped_def (MINOR) streams/base_insight_streams.py Check that every function has an annotation Function is missing a return type annotation . Code line: def state(self, value: MutableMapping[str, Any]):
python:mypy_var_annotated (MINOR) streams/base_insight_streams.py:86 Require variable annotation if type can't be inferred Need type annotation for "_completed_slices" (hint: "_completed_slices: Set[] = ...") . Code line: self._completed_slices = set()
python:mypy_no_untyped_def (MINOR) streams/base_insight_streams.py:150 Check that every function has an annotation Function is missing a return type annotation . Code line: def _advance_cursor(self):
python:mypy_misc (MINOR) streams/base_insight_streams.py:177 Miscellaneous other checks "InsightAsyncJob" gets multiple values for keyword argument "edge_object" . Code line: yield InsightAsyncJob(self._api.api, edge_object=self._api...
python:S1134 (MAJOR) streams/base_insight_streams.py:221 Track uses of "FIXME" tags Take the required action to fix the issue indicated by this "FIXME" comment.
python:mypy_no_untyped_def (MINOR) streams/base_insight_streams.py:234 Check that every function has an annotation Function is missing a type annotation for one or more arguments . Code line: def request_params(self, **kwargs) -> MutableMapping[str, Any]:
python:mypy_override (MINOR) streams/base_insight_streams.py:234 Check that method override is compatible with base class Signature of "request_params" incompatible with supertype "FBMarketingIncrementalStream" . Code line: def request_params(self, **kwargs) -> MutableMapping[str, Any]:
python:S1226 (MINOR) streams/async_job.py Function parameters initial values should not be ignored Introduce a new variable or use its initial value before reassigning 'batch'.
python:mypy_no_untyped_def (MINOR) streams/async_job.py Check that every function has an annotation Function is missing a type annotation . Code line: def update_job(self, batch=None):
python:mypy_no_untyped_def (MINOR) streams/async_job.py Check that every function has an annotation Function is missing a type annotation . Code line: def update_job(self, batch=None):
python:mypy_no_untyped_def (MINOR) streams/async_job.py:61 Check that every function has an annotation Function is missing a return type annotation . Code line: def restart(self):
python:mypy_no_untyped_def (MINOR) streams/async_job.py:108 Check that every function has an annotation Function is missing a return type annotation . Code line: def restart(self):
python:mypy_no_any_return (MINOR) streams/async_job_manager.py:148 Reject returning value with "Any" type if return type is not "Any" Returning Any from function declared to return "Tuple[float, float]" . Code line: return self._api.api.ads_insights_throttle
python:S1226 (MINOR) streams/async_job.py:125 Function parameters initial values should not be ignored Introduce a new variable or use its initial value before reassigning 'batch'.
python:S3457 (MAJOR) streams/async_job.py:186 String formatting should be used correctly Add argument(s) corresponding to the message's replacement field(s).
python:S3457 (MAJOR) streams/async_job.py:203 String formatting should be used correctly Add argument(s) corresponding to the message's replacement field(s).
python:S3457 (MAJOR) streams/async_job.py:262 String formatting should be used correctly Add argument(s) corresponding to the message's replacement field(s).
python:S3457 (MAJOR) streams/async_job.py:281 String formatting should be used correctly Add argument(s) corresponding to the message's replacement field(s).
python:S3457 (MAJOR) streams/async_job.py:292 String formatting should be used correctly Add argument(s) corresponding to the message's replacement field(s).
python:black_need_format (MINOR) streams/async_job.py Please run one of the commands: "black --config ./pyproject.toml <path_to_updated_folder>" or "./gradlew format" 6 code part(s) should be updated.
python:isort_need_format (MINOR) streams/async_job.py Please run one of the commands: "isort <path_to_updated_folder>" or "./gradlew format" 1 code part(s) should be updated.
python:mypy_no_untyped_def (MINOR) streams/async_job.py:65 Check that every function has an annotation Function is missing a return type annotation . Code line: def attempt_number(self):
python:mypy_no_untyped_def (MINOR) streams/async_job.py:80 Check that every function has an annotation Function is missing a return type annotation . Code line: def update_job(self, batch: Optional[FacebookAdsApiBatch] = None):
python:mypy_no_untyped_def (MINOR) streams/async_job.py:98 Check that every function has an annotation Function is missing a type annotation for one or more arguments . Code line: def init(self, jobs: List[AsyncJob], **kwargs):
python:mypy_no_untyped_def (MINOR) streams/async_job.py:125 Check that every function has an annotation Function is missing a return type annotation . Code line: def update_job(self, batch: Optional[FacebookAdsApiBatch] = None):
python:mypy_no_untyped_def (MINOR) streams/async_job.py:153 Check that every function has an annotation Function is missing a type annotation for one or more arguments . Code line: def init(self, edge_object: Union[AdAccount, Campaign], params...
python:mypy_call_arg (MINOR) streams/async_job.py:185 Check number, names and kinds of arguments in calls Unexpected keyword argument "num" for "info" of "Logger" . Code line: logger.info(
python:mypy_call_arg (MINOR) streams/async_job.py:185 Check number, names and kinds of arguments in calls Unexpected keyword argument "period" for "info" of "Logger" . Code line: logger.info(
python:mypy_call_arg (MINOR) streams/async_job.py:185 Check number, names and kinds of arguments in calls Unexpected keyword argument "campaign_ids" for "info" of "Logger" . Code line: logger.info(
python:mypy_arg_type (MINOR) streams/async_job.py:193 Check argument types in calls Argument "jobs" to "ParentAsyncJob" has incompatible type "List[InsightAsyncJob]"; expected "List[AsyncJob]" . Code line: ...turn ParentAsyncJob(api=self._api, interval=self._interval, jobs=jobs)
flake8:E251 (MAJOR) streams/async_job.py:204 unexpected spaces around keyword / parameter equals unexpected spaces around keyword / parameter equals
flake8:E251 (MAJOR) streams/async_job.py:204 unexpected spaces around keyword / parameter equals unexpected spaces around keyword / parameter equals
python:mypy_call_arg (MINOR) streams/async_job.py:261 Check number, names and kinds of arguments in calls Unexpected keyword argument "job" for "info" of "Logger" . Code line: logger.info(
python:mypy_call_arg (MINOR) streams/async_job.py:261 Check number, names and kinds of arguments in calls Unexpected keyword argument "percent" for "info" of "Logger" . Code line: logger.info(
python:mypy_call_arg (MINOR) streams/async_job.py:261 Check number, names and kinds of arguments in calls Unexpected keyword argument "status" for "info" of "Logger" . Code line: logger.info(
python:mypy_call_arg (MINOR) streams/async_job.py:280 Check number, names and kinds of arguments in calls Unexpected keyword argument "job" for "info" of "Logger" . Code line: logger.info(
python:mypy_call_arg (MINOR) streams/async_job.py:280 Check number, names and kinds of arguments in calls Unexpected keyword argument "percent" for "info" of "Logger" . Code line: logger.info(
python:mypy_call_arg (MINOR) streams/async_job.py:280 Check number, names and kinds of arguments in calls Unexpected keyword argument "status" for "info" of "Logger" . Code line: logger.info(
python:mypy_call_arg (MINOR) streams/async_job.py:291 Check number, names and kinds of arguments in calls Unexpected keyword argument "job" for "info" of "Logger" . Code line: logger.info(
python:mypy_call_arg (MINOR) streams/async_job.py:291 Check number, names and kinds of arguments in calls Unexpected keyword argument "status" for "info" of "Logger" . Code line: logger.info(
python:mypy_call_arg (MINOR) streams/async_job.py:291 Check number, names and kinds of arguments in calls Unexpected keyword argument "elapsed" for "info" of "Logger" . Code line: logger.info(
python:mypy_attr_defined (MINOR) streams/async_job.py:293 Check that attribute exists pendulum.duration? has no attribute "in_seconds" . Code line: ... job=self, status=job_status, elapsed=self.elapsed_time.in_...
python:isort_need_format (MINOR) streams/async_job_manager.py Please run one of the commands: "isort <path_to_updated_folder>" or "./gradlew format" 1 code part(s) should be updated.
python:mypy_no_untyped_def (MINOR) streams/base_insight_streams.py:132 Check that every function has an annotation Function is missing a return type annotation . Code line: def state(self, value: Mapping[str, Any]):
python:mypy_no_untyped_def (MINOR) streams/async_job_manager.py:88 Check that every function has an annotation Function is missing a return type annotation . Code line: def _check_jobs_status(self):
python:isort_need_format (MINOR) unit_tests/test_base_insight_streams.py Please run one of the commands: "isort <path_to_updated_folder>" or "./gradlew format" 1 code part(s) should be updated.
python:mypy_no_untyped_def (MINOR) streams/base_insight_streams.py Check that every function has an annotation Function is missing a type annotation for one or more arguments . Code line: def stream_slices(
python:mypy_name_defined (MINOR) streams/base_insight_streams.py:138 Check that name is defined Function is missing a return type annotation [no-untyped-def] def get_updated_state(self, current_stream_state: MutableMapping[s...
python:mypy_name_defined (MINOR) streams/base_insight_streams.py:196 Check that name is defined Incompatible types in assignment (expression has type "Mapping[str, Any]", variable has type "MutableMapping[str, Any]") [assignment] self.state = stream_state
python:mypy_misc (MINOR) source_facebook_marketing/spec.py:18 Miscellaneous other checks Enum() expects a string, tuple, list or dict literal as the second argument . Code line: ValidFields = Enum("ValidEnums", AdsInsights.Field.dict)
python:mypy_misc (MINOR) source_facebook_marketing/spec.py:19 Miscellaneous other checks Enum() expects a string, tuple, list or dict literal as the second argument . Code line: ValidBreakdowns = Enum("ValidBreakdowns", AdsInsights.Breakdowns.__dic...
python:mypy_misc (MINOR) source_facebook_marketing/spec.py:20 Miscellaneous other checks Enum() expects a string, tuple, list or dict literal as the second argument . Code line: ValidActionBreakdowns = Enum("ValidActionBreakdowns", AdsInsights.Acti...
python:isort_need_format (MINOR) streams/base_insight_streams.py Please run one of the commands: "isort <path_to_updated_folder>" or "./gradlew format" 1 code part(s) should be updated.
python:isort_need_format (MINOR) unit_tests/test_async_job.py Please run one of the commands: "isort <path_to_updated_folder>" or "./gradlew format" 1 code part(s) should be updated.
python:isort_need_format (MINOR) unit_tests/test_source.py Please run one of the commands: "isort <path_to_updated_folder>" or "./gradlew format" 1 code part(s) should be updated.
python:isort_need_format (MINOR) integration_tests/test_streams.py Please run one of the commands: "isort <path_to_updated_folder>" or "./gradlew format" 1 code part(s) should be updated.
python:isort_need_format (MINOR) source_facebook_marketing/source.py Please run one of the commands: "isort <path_to_updated_folder>" or "./gradlew format" 1 code part(s) should be updated.
python:mypy_no_untyped_def (MINOR) streams/async_job.py:57 Check that every function has an annotation Function is missing a return type annotation . Code line: def start(self):
python:mypy_no_untyped_def (MINOR) streams/async_job.py:103 Check that every function has an annotation Function is missing a return type annotation . Code line: def start(self):
python:mypy_no_untyped_def (MINOR) streams/async_job.py:195 Check that every function has an annotation Function is missing a return type annotation . Code line: def start(self):
python:isort_need_format (MINOR) unit_tests/test_client.py Please run one of the commands: "isort <path_to_updated_folder>" or "./gradlew format" 1 code part(s) should be updated.
python:mypy_attr_defined (MINOR) streams/init.py Check that attribute exists Module "source_facebook_marketing.streams.streams" does not explicitly export attribute "AdsInsights"; implicit reexport disabled . Code line: from .streams import (
python:mypy_no_untyped_def (MINOR) streams/base_streams.py:40 Check that every function has an annotation Function is missing a type annotation for one or more arguments . Code line: def init(self, api: "API", include_deleted: bool = False, **kw...
python:mypy_no_untyped_def (MINOR) streams/base_streams.py:50 Check that every function has an annotation Function is missing a type annotation . Code line: def _execute_batch(self, batch):
python:mypy_no_untyped_def (MINOR) streams/base_streams.py:61 Check that every function has an annotation Function is missing a return type annotation . Code line: def success(response: FacebookResponse):
python:mypy_no_untyped_def (MINOR) streams/base_streams.py:64 Check that every function has an annotation Function is missing a return type annotation . Code line: def failure(response: FacebookResponse):
python:S1134 (MAJOR) streams/base_streams.py:65 Track uses of "FIXME" tags Take the required action to fix the issue indicated by this "FIXME" comment.
python:mypy_no_redef (MINOR) streams/base_streams.py:74 Check that each name is defined once Name "api_batch" already defined on line 68 . Code line: api_batch: FacebookAdsApiBatch = self._api.api.new_bat...
python:mypy_assignment (MINOR) streams/base_streams.py:91 Check that assigned value is compatible with target Incompatible types in assignment (expression has type "Iterable[MutableMapping[str, Any]]", variable has type "Generator[Any, None, None]") . Code line: loaded_records_iter = self.execute_in_batch(loaded_records...
python:mypy_no_untyped_def (MINOR) streams/base_streams.py:105 Check that every function has an annotation Function is missing a type annotation for one or more arguments . Code line: def request_params(self, **kwargs) -> MutableMapping[str, Any]:
python:mypy_no_untyped_def (MINOR) streams/base_streams.py:143 Check that every function has an annotation Function is missing a type annotation for one or more arguments . Code line: def init(self, start_date: datetime, end_date: datetime, **kwa...
python:mypy_no_untyped_def (MINOR) streams/base_streams.py:148 Check that every function has an annotation Function is missing a return type annotation . Code line: def get_updated_state(self, current_stream_state: MutableMapping[s...
python:mypy_attr_defined (MINOR) streams/base_streams.py:153 Check that attribute exists Module has no attribute "parse" . Code line: max_cursor = max(pendulum.parse(state_value), pendulum.parse(r...
python:mypy_no_untyped_def (MINOR) streams/base_streams.py:162 Check that every function has an annotation Function is missing a type annotation for one or more arguments . Code line: def request_params(self, stream_state: Mapping[str, Any], **kwargs...
python:mypy_override (MINOR) streams/base_streams.py:162 Check that method override is compatible with base class Signature of "request_params" incompatible with supertype "FBMarketingStream" . Code line: def request_params(self, stream_state: Mapping[str, Any], **kwargs...
python:mypy_no_any_return (MINOR) streams/base_streams.py:166 Reject returning value with "Any" type if return type is not "Any" Returning Any from function declared to return "MutableMapping[str, Any]" . Code line: return params
python:mypy_attr_defined (MINOR) streams/base_streams.py:171 Check that attribute exists Module has no attribute "parse" . Code line: ...value = self.start_date if not state_value else pendulum.parse(state...
python:mypy_var_annotated (MINOR) streams/streams.py Require variable annotation if type can't be inferred Need type annotation for "breakdowns" (hint: "breakdowns: List[] = ...") . Code line: breakdowns = []
python:mypy_override (MINOR) streams/base_insight_streams.py:95 Check that method override is compatible with base class Signature of "primary_key" incompatible with supertype "FBMarketingStream" . Code line: def primary_key(self) -> Optional[Union[str, List[str], List[List[...
python:mypy_no_untyped_def (MINOR) streams/async_job_manager.py:49 Check that every function has an annotation Function is missing a return type annotation . Code line: def _start_jobs(self):
python:isort_need_format (MINOR) unit_tests/test_async_job_manager.py Please run one of the commands: "isort <path_to_updated_folder>" or "./gradlew format" 1 code part(s) should be updated.
python:mypy_index (MINOR) streams/streams.py Check indexing operations Unsupported target for indexed assignment ("Mapping[str, Any]") . Code line: record["thumbnail_data_url"] = fetch_thumbnail_data_ur...
python:isort_need_format (MINOR) source_facebook_marketing/api.py Please run one of the commands: "isort <path_to_updated_folder>" or "./gradlew format" 1 code part(s) should be updated.
python:mypy_attr_defined (MINOR) streams/base_insight_streams.py:134 Check that attribute exists Module has no attribute "parse" . Code line: self._cursor_value = pendulum.parse(value[self.cursor_field])....
python:mypy_attr_defined (MINOR) streams/base_insight_streams.py:135 Check that attribute exists Module has no attribute "parse" . Code line: self._completed_slices = set(pendulum.parse(v).date() for v in...
python:mypy_no_any_return (MINOR) streams/base_insight_streams.py:148 Reject returning value with "Any" type if return type is not "Any" Returning Any from function declared to return "Iterator[Any]" . Code line: return date_range.range("days", self.time_increment)
python:mypy_no_untyped_def (MINOR) source_facebook_marketing/api.py:106 Check that every function has an annotation Function is missing a return type annotation . Code line: def _update_insights_throttle_limit(self, response: FacebookRespon...
python:mypy_no_untyped_def (MINOR) streams/async_job.py Check that every function has an annotation Function is missing a type annotation . Code line: def chunks(data, n):
python:mypy_no_untyped_def (MINOR) streams/async_job.py Check that every function has an annotation Function is missing a return type annotation . Code line: def restart_number(self):
python:mypy_no_untyped_def (MINOR) streams/async_job.py Check that every function has an annotation Function is missing a type annotation for one or more arguments . Code line: def init(self, api, jobs: List[AsyncJob]):
python:mypy_no_untyped_def (MINOR) streams/async_job.py Check that every function has an annotation Function is missing a return type annotation . Code line: def restart_number(self):
python:mypy_no_untyped_def (MINOR) streams/async_job.py Check that every function has an annotation Function is missing a type annotation for one or more arguments . Code line: def init(self, api, edge_object: Any, params: Mapping[str, Any...
python:mypy_attr_defined (MINOR) streams/async_job.py Check that attribute exists Module has no attribute "parse" . Code line: new_start = pendulum.parse(self._params["time_range"]["since"]...
python:mypy_valid_type (MINOR) streams/async_job.py Check that type (annotation) is valid Function is missing a return type annotation [no-untyped-def] def restart_number(self):
flake8:E203 (MAJOR) streams/async_job.py:24 whitespace before ‘:’ whitespace before ':'
python:mypy_no_untyped_def (MINOR) streams/async_job.py:255 Check that every function has an annotation Function is missing a return type annotation . Code line: def update_job(self, batch: Optional[FacebookAdsApiBatch] = None):
python:S3776 (CRITICAL) streams/async_job_manager.py Cognitive Complexity of functions should not be too high Refactor this function to reduce its Cognitive Complexity from 18 to the 15 allowed.
python:S112 (MAJOR) streams/async_job_manager.py "Exception" and "BaseException" should not be raised Replace this generic exception class with a more specific one.
python:mypy_var_annotated (MINOR) streams/async_job_manager.py:47 Require variable annotation if type can't be inferred Need type annotation for "_running_jobs" (hint: "_running_jobs: List[] = ...") . Code line: self._running_jobs = []
python:mypy_name_defined (MINOR) streams/base_insight_streams.py Check that name is defined Function is missing a type annotation for one or more arguments [no-untyped-def] def stream_slices(self, **kwargs) -> Iterable[Optional[Mapping[str...
python:mypy_no_untyped_def (MINOR) streams/base_insight_streams.py Check that every function has an annotation Function is missing a return type annotation . Code line: def state(self, value: MutableMapping[str, Any]):
python:mypy_var_annotated (MINOR) streams/base_insight_streams.py:86 Require variable annotation if type can't be inferred Need type annotation for "_completed_slices" (hint: "_completed_slices: Set[] = ...") . Code line: self._completed_slices = set()
python:mypy_no_untyped_def (MINOR) streams/base_insight_streams.py:150 Check that every function has an annotation Function is missing a return type annotation . Code line: def _advance_cursor(self):
python:mypy_misc (MINOR) streams/base_insight_streams.py:177 Miscellaneous other checks "InsightAsyncJob" gets multiple values for keyword argument "edge_object" . Code line: yield InsightAsyncJob(self._api.api, edge_object=self._api...
python:S1134 (MAJOR) streams/base_insight_streams.py:221 Track uses of "FIXME" tags Take the required action to fix the issue indicated by this "FIXME" comment.
python:mypy_no_untyped_def (MINOR) streams/base_insight_streams.py:234 Check that every function has an annotation Function is missing a type annotation for one or more arguments . Code line: def request_params(self, **kwargs) -> MutableMapping[str, Any]:
python:mypy_override (MINOR) streams/base_insight_streams.py:234 Check that method override is compatible with base class Signature of "request_params" incompatible with supertype "FBMarketingIncrementalStream" . Code line: def request_params(self, **kwargs) -> MutableMapping[str, Any]:
python:S1226 (MINOR) streams/async_job.py Function parameters initial values should not be ignored Introduce a new variable or use its initial value before reassigning 'batch'.
python:mypy_no_untyped_def (MINOR) streams/async_job.py Check that every function has an annotation Function is missing a type annotation . Code line: def update_job(self, batch=None):
python:mypy_no_untyped_def (MINOR) streams/async_job.py Check that every function has an annotation Function is missing a type annotation . Code line: def update_job(self, batch=None):
python:mypy_no_untyped_def (MINOR) streams/async_job.py:61 Check that every function has an annotation Function is missing a return type annotation . Code line: def restart(self):
python:mypy_no_untyped_def (MINOR) streams/async_job.py:108 Check that every function has an annotation Function is missing a return type annotation . Code line: def restart(self):
python:mypy_no_any_return (MINOR) streams/async_job_manager.py:148 Reject returning value with "Any" type if return type is not "Any" Returning Any from function declared to return "Tuple[float, float]" . Code line: return self._api.api.ads_insights_throttle

Coverage (14.6%)

File Coverage File Coverage
integration_tests/acceptance.py 0.0 integration_tests/conftest.py 0.0
integration_tests/test_streams.py 0.0 main.py 0.0
setup.py 0.0 source_facebook_marketing/init.py 100.0
source_facebook_marketing/api.py 76.7 source_facebook_marketing/source.py 74.3
source_facebook_marketing/spec.py 100.0 source_facebook_marketing/streams/init.py 100.0
source_facebook_marketing/streams/async_job.py 44.2 source_facebook_marketing/streams/async_job_manager.py 48.3
source_facebook_marketing/streams/base_insight_streams.py 87.5 source_facebook_marketing/streams/base_streams.py 79.2
source_facebook_marketing/streams/common.py 97.1 source_facebook_marketing/streams/streams.py 80.0
unit_tests/conftest.py 0.0 unit_tests/test_async_job.py 0.0
unit_tests/test_async_job_manager.py 0.0 unit_tests/test_base_insight_streams.py 0.0
unit_tests/test_client.py 0.0 unit_tests/test_deep_merge.py 0.0
unit_tests/test_source.py 0.0

Please sign in to comment.