Example #1
0
class AppLibrary:
    def __init__(self):
        self._io = StubIO()
        self._user_repository = UserRepository()
        self._user_service = UserService(self._user_repository)
        self._app = App(self._user_service, self._io)

    def input(self, value):
        self._io.add_input(value)

    def output_should_contain(self, value):
        outputs = self._io.outputs

        if value not in outputs:
            raise AssertionError(f'Output "{value}" is not in {str(outputs)}')

    def run_application(self):
        self._app.run()

    def create_user(self, username, password):
        try:
            self._user_service.create_user(username, password)
        except Exception as err:
            self._io.outputs.append(str(err))

    def user_should_exist(self, username):
        if not self._user_repository.find_by_username(username):
            raise AssertionError(f"User {username} does not exist")
Example #2
0
class AppTestCase(unittest.TestCase):
    def setUp(self):
        self.input = MagicMock()
        # swipe somewhere (south):
        self.input.getline = MagicMock(return_value='s')

        self.output = MagicMock()
        self.output.write = MagicMock()

        self.app = App(self.input, self.output)
        self.app.renderer = create_autospec(Renderer)  # type: MagicMock

    def test_runZeroTimesRendersOnce(self):
        self.app.run(max_prompts=0)
        count = len(self.app.renderer.mock_calls)
        self.assertEqual(
            1,
            count,
            "Method render on Renderer should be called one time."
        )

    def test_runOneTimeSwipingSouthRendersTwice(self):
        self.app.run(max_prompts=1)
        count = len(self.app.renderer.mock_calls)
        self.assertEqual(
            2,
            count,
            "Method render on Renderer should be called two times."
        )
Example #3
0
def sharedapp():
    print("\n@fixture: shared session Application load...")
    app = App()
    app.run(pytest=True)
    yield app
    app.quit()
    print("\n@fixture: shared session Application quited.")
def main():
    args = docopt(__doc__)

    schema = Schema({
        '--help': bool,
        '--headless': bool,
        '--width': Use(int),
        '--height': Use(int),
        '<save_path>': str,
    })

    try:
        args = schema.validate(args)
    except SchemaError as e:
        exit(e)

    model_path = 'models'

    loadPrcFileData('', 'window-title Babble')
    loadPrcFileData('', 'win-size %d %d' % (args['--width'], args['--height']))
    loadPrcFileData('', 'audio-library-name null') # suppress warning
    loadPrcFileData('', 'model-path %s' % model_path)
    loadPrcFileData('', 'bullet-filter-algorithm groups-mask')

    if args['--headless']:
        loadPrcFileData('', 'window-type none')

    app = App(args)
    app.run()
Example #5
0
def run() -> None:
    """Update path and run the App."""

    # update the path to ensure the App has access to required modules
    app_lib = AppLib()
    app_lib.update_path()

    # import modules after path has been updated
    # third-party
    from app import App  # pylint: disable=import-outside-toplevel
    from tcex import TcEx  # pylint: disable=import-outside-toplevel

    tcex = TcEx()

    try:
        # load App class
        app = App(tcex)

        # perform prep/setup operations
        app.setup(**{})

        # run the App logic
        app.run(**{})

        # perform cleanup/teardown operations
        app.teardown(**{})

        # explicitly call the exit method
        tcex.exit(msg=app.exit_message)

    except Exception as e:
        main_err = f'Generic Error.  See logs for more details ({e}).'
        tcex.log.error(traceback.format_exc())
        tcex.playbook.exit(1, main_err)
Example #6
0
def main():
    try:
        from app import App, pygame_quit

        settings_file = path.join(
            path.join(filepath, '..', 'data', 'settings.json'))
        if path.isfile(settings_file):
            with open(settings_file, 'r') as f:
                app_settings = json.load(f)
        else:
            logging.info(
                f'Warning: {settings_file} not found. Using default settings.')
            app_settings = DEFAULT_SETTINGS

        app = App(**app_settings)
        app.run()
    except Exception:
        logging.error(traceback.format_exc())
        # stop all parallel threads
        try:
            # stop all parallel threads
            app.should_stop.set()
        except UnboundLocalError:
            # if thread Event wasn't initialized yet
            pass
        # de-initialise pygame on error
        pygame_quit()
def run() -> None:
    """Update path and run the App."""

    # update the path to ensure the App has access to required modules
    app_lib = AppLib()
    app_lib.update_path()

    # import modules after path has been updated

    # third-party
    from tcex import TcEx  # pylint: disable=import-outside-toplevel

    # first-party
    from app import App  # pylint: disable=import-outside-toplevel

    tcex = TcEx()

    try:
        # load App class
        app = App(tcex)

        # perform prep/setup operations
        app.setup(**{})

        # run the App logic
        if hasattr(app.args, 'tc_action') and app.args.tc_action is not None:
            # if the args NameSpace has the reserved arg of "tc_action", this arg value is used to
            # triggers a call to the app.<tc_action>() method.  an exact match to the method is
            # tried first, followed by a normalization of the tc_action value, and finally an
            # attempt is made to find the reserved "tc_action_map" property to map value to method.
            tc_action: str = app.args.tc_action
            tc_action_formatted: str = tc_action.lower().replace(' ', '_')
            tc_action_map = 'tc_action_map'  # reserved property name for action to method map
            # run action method
            if hasattr(app, tc_action):
                getattr(app, tc_action)()
            elif hasattr(app, tc_action_formatted):
                getattr(app, tc_action_formatted)()
            elif hasattr(app, tc_action_map):
                app.tc_action_map.get(app.args.tc_action)()  # pylint: disable=no-member
            else:
                tcex.exit(
                    1, f'Action method ({app.args.tc_action}) was not found.')
        else:
            # default to run method
            app.run(**{})

        # write requested value for downstream Apps
        app.write_output()  # pylint: disable=no-member

        # perform cleanup/teardown operations
        app.teardown(**{})

        # explicitly call the exit method
        tcex.playbook.exit(msg=app.exit_message)

    except Exception as e:
        main_err = f'Generic Error.  See logs for more details ({e}).'
        tcex.log.error(traceback.format_exc())
        tcex.playbook.exit(1, main_err)
Example #8
0
class AppLibrary:
    def __init__(self):
        self._io = StubIO()
        self._user_repository = UserRepository()
        self._user_service = UserService(self._user_repository)

        self._app = App(self._user_service, self._io)

    def input(self, value):
        self._io.add_input(value)

    def output_should_contain(self, value):
        outputs = self._io.outputs

        if not value in outputs:
            raise AssertionError(
                f"Output \"{value}\" is not in {str(outputs)}")

    def run_application(self):
        self._app.run()

    def create_user(self, username, password):
        try:
            self._user_service.create_user(username, password)
            self._io.write("New user registered")
        except Exception as e:
            self._io.write(str(e))
Example #9
0
File: rta.py Project: ahua/python
class RtAgent(object):
    def __init__(self, conf):
        self.gb = GeneralBase(conf)
        self.app = App(conf)
        self.vod = Vod(conf)
        self.guide = Guide(conf)
        self.cat = Cat(conf)

    def interrupt(self):
        pass

    def accept(self, p, isnew = True):
        if p.get('_type') != 'normal':
            return

        if p.get('_device', '').lower() not in ['a11', 'a21', 'k72', 'k82', 'ud10a', 'ds70a', 'lx750a', 'lx755a', 'lx850a', 'lx960a', 'k91', 's31', 's51', 's61', 'e31', 'e62']:
            return

        if p.get('event') not in ['video_exit', 'app_start', 'launcher_vod_click', 'video_category_out']:
            return

        if isnew:
            if self.gb.run(p):
                self.app.run(p)
                self.vod.run(p)
                self.guide.run(p)
                self.cat.run(p)
        else:
            self.vod.run(p)
Example #10
0
def main():
    user_repository = UserRepository()
    user_service = UserService(user_repository)
    console_io = ConsoleIO()
    app = App(user_service, console_io)

    app.run()
Example #11
0
def main():
    app = App(
        cities_num=10,  # this >= 2
        population_size=30,  # this > 2
        next_round_passers_num=10,  # 2 <= this < population_size
        mutation_chance=1  # 0 <= this <= 1
    )
    app.run()
Example #12
0
def main():
    sensor_state_to_video_name = generate_sensor_state_to_video_name()
    app = App(rfid_uids,
              sensor_state_to_video_name,
              'window',
              transmission_rate,
              fps,
              fullscreen=False)
    app.run()
Example #13
0
    def test_it_gets_rsvps_for_upcoming_event(self):
        sesClient = getSesClientMock()
        meetupClient = getMeetupClientMock(
            rsvps=getFixtureRsvps(),
            event=getFixtureEvent(),
        )

        app = App(sesClient, meetupClient)
        app.run(GROUP_URLNAME, DEFAULT_NOTIFY_PERIOD_HOURS)
        meetupClient.getRsvpsForMeetup.assert_called_with(getFixtureEvent()['id'])
Example #14
0
def start_server(tmp_dir, port_file, icon_file, python_path, data_dir):
    app = App(tmp_dir, port_file, icon_file, python_path, data_dir)
    app.build_ui()
    open_files_from_tmp(app)
    app.run_command('show_home')
    open_files_from_arg(sys.argv, app)
    port = find_free_port()
    with open(app.port_file, 'w') as file:
        file.write(str(port))
    app.run(port)
Example #15
0
    def test_it_does_not_send_email_when_there_are_no_rsvp_answers(self):
        sesClient = getSesClientMock()
        meetupClient = getMeetupClientMock(
            rsvps=getFixtureRsvps(False),
            event=getFixtureEvent(),
        )

        app = App(sesClient, meetupClient)
        app.run(GROUP_URLNAME, DEFAULT_NOTIFY_PERIOD_HOURS)
        sesClient.send.assert_not_called()
def run(**kwargs) -> None:
    """Update path and run the App."""

    # update the path to ensure the App has access to required modules
    app_lib = AppLib()
    app_lib.update_path()

    # import modules after path has been updated

    # third-party
    from tcex import TcEx  # pylint: disable=import-outside-toplevel

    # first-party
    from app import App  # pylint: disable=import-outside-toplevel

    tcex = TcEx()

    try:
        # load App class
        app = App(tcex)

        # set app property in testing framework
        if callable(kwargs.get('set_app')):
            kwargs.get('set_app')(app)

        # configure custom trigger message handler
        tcex.service.create_config_callback = app.create_config_callback
        tcex.service.delete_config_callback = app.delete_config_callback
        tcex.service.shutdown_callback = app.shutdown_callback

        # perform prep/setup operations
        app.setup(**{})

        # listen on channel/topic
        tcex.service.listen()

        # start heartbeat threads
        tcex.service.heartbeat()

        # inform TC that micro-service is Ready
        tcex.service.ready = True

        # run app logic
        app.run(**{})

        # perform cleanup/teardown operations
        app.teardown(**{})

        # explicitly call the exit method
        tcex.playbook.exit(msg=app.exit_message)

    except Exception as e:
        main_err = f'Generic Error.  See logs for more details ({e}).'
        tcex.log.error(traceback.format_exc())
        tcex.playbook.exit(1, main_err)
Example #17
0
    def test_it_sends_email_when_there_are_rsvps_answers(self):
        sesClient = getSesClientMock()
        meetupClient = getMeetupClientMock(
            rsvps=getFixtureRsvps(),
            event=getFixtureEvent(),
        )

        app = App(sesClient, meetupClient)
        app.run(GROUP_URLNAME, DEFAULT_NOTIFY_PERIOD_HOURS)
        expectedEventStartTime = datetime.datetime.fromtimestamp(getFixtureEvent()['time'] / 1000)
        sesClient.send.assert_called_with(f'RSVPs for Group-Urlname event on {str(expectedEventStartTime)} (UTC)', f'RSVP answers for the Group-Urlname MeetUp event scheduled on {str(expectedEventStartTime)} (UTC)\n\nI have a disability\nI have another disability')
def main(source, skip_frames, tolerance, upsample_times, report, video):
    app = App(source)
    if skip_frames:
        app.face_finder.skip_frames_num = skip_frames
    if tolerance:
        app.face_comparer.tolerance = tolerance
    if upsample_times:
        app.face_finder.upsample_num = upsample_times
    app.face_finder.generate_report = report
    app.face_finder.generate_video = video
    app.run()
Example #19
0
def main():
    logging.basicConfig(level=logging.DEBUG, format='%(asctime)s: %(message)s')
    logging.getLogger('digit_analysis.node.traversal').setLevel(logging.INFO)
    logging.getLogger('user_dependency_graph').setLevel(logging.INFO)
    logging.getLogger('ucmexport').setLevel(logging.INFO)
    cur_dir = os.path.dirname(os.path.abspath(__file__))
    tar_files = glob.glob(os.path.join(cur_dir, '*.tar'))
    tar_files = sorted(map(os.path.basename, tar_files))
    assert tar_files, f'No TAR files found in {cur_dir}'
    app = App(tar_files=tar_files)
    app.run()
    return
Example #20
0
def handle_command():
    try:
        command = sys.argv[1]
        if command in ['runserver', 'initdb']:
            if command == 'runserver':
                App.run()
            elif command == 'initdb':
                db_utils.init_db('./schema.sql')
        else:
            print("Command Not Found, Avaliable commands [runserver, initdb]")
    except IndexError:
        print("Available commands are:\n\trunserver\n\tinitdb")
Example #21
0
def main(debug=False):
    '''init logging'''
    kw = {
        'format': '[%(asctime)s] %(message)s',
        'datefmt': '%d/%m/%Y %H:%M:%S',
        'level': logging.INFO if debug else logging.DEBUG,
        'stream': sys.stdout,
    }
    logging.basicConfig(**kw)
    '''init app'''
    app = App()
    app.run(debug)
Example #22
0
def test_run(file, expected_output):
    stdout = sys.stdout
    sys.stdout = io.StringIO()

    app = App()
    app.run(file)

    output = sys.stdout.getvalue()
    sys.stdout = stdout

    assert output == expected_output
    return "success"
def main():
    model_path = 'models'
    width = 640
    height = 640

    loadPrcFileData('', 'window-title Babble')
    loadPrcFileData('', 'win-size %d %d' % (width, height))
    loadPrcFileData('', 'audio-library-name null') # suppress warning
    loadPrcFileData('', 'model-path %s' % model_path)
    loadPrcFileData('', 'bullet-filter-algorithm groups-mask')

    app = App(width, height)
    app.run()
Example #24
0
def start_app():

    parser = argparse.ArgumentParser(description='Starts the App')
    parser.add_argument("--debug",
                        default=False,
                        action='store_true',
                        help="define the section to be updated",
                        required=False)
    args = parser.parse_args()
    debug = args.debug

    App.wsgi_app = ProxyFix(App.wsgi_app)
    App.run(debug=debug)
def main():
    width = 640
    height = 640

    loadPrcFileData('', 'window-title Animate')
    loadPrcFileData('', 'win-size %d %d' % (width, height))
    loadPrcFileData('', 'audio-library-name null') # suppress warning
    loadPrcFileData('', 'model-path %s' % '.')

    model_path = sys.argv[1]
    animation_path = sys.argv[2] if len(sys.argv) > 2 else None

    app = App(width, height, model_path, animation_path)
    app.run()
Example #26
0
def run(reindex, interval_hours):
    interval = timedelta(hours=interval_hours) if interval_hours else None

    app = App(reindex)
    app.run()

    if interval is None:
        logging.info('completed')
        return

    while True:
        logging.info(f'app will be rescheduled in {interval}')
        sleep(interval.seconds)
        app.run()
Example #27
0
def main():

    from homescreen import HomeScreen
    from app import App

    graphics.setup()
    graphics.initFonts()

    homescreen = HomeScreen()
    homescreen.play(graphics.screen)

    app = App(graphics.screen)
    while True:
        app.run(graphics.screen)
        pygame.display.flip()  # call it once to avoid flickering
Example #28
0
def main():
    N, k, min, max, d, launch_test, bot_wait_time = get_params()
    app = App(N, k, min, max, d, bot_wait_time)
    if launch_test:
        draws = 0
        p1_wins = 0
        p2_wins = 0
        no_moves = []
        no_games = 100
        for i in range(no_games):
            print(f'\rLaunch game {i+1}/{no_games}', end='')
            winner, no_m = app.run(0)
            if winner == 1:
                p1_wins += 1
            elif winner == 2:
                p2_wins += 1
            else:
                draws += 1
            no_moves.append(no_m)
        print(f'\nPlayer 1 win rate: {100*p1_wins/no_games}%')
        print(f'Player 2 win rate: {100*p2_wins/no_games}%')
        print(f'Draws: {100*draws/no_games}%')
        av = sum(no_moves) / len(no_moves)
        print(f'Average number of moves per game: {av}')
    else:
        winner, no_moves = app.run(10_000)
        if winner == 0:
            print("Draw!")
        elif winner == 1:
            print("Player 1 won!")
        else:
            print("Player 2 won!")
Example #29
0
 def test_it_exits_with_fail_return_code_if_no_notifications_sent(self):
     sesClient = getSesClientMock()
     meetupClient = getMeetupClientMock(
         rsvps=MagicMock(results=[]),
         event=getFixtureEvent(),
     )
     app = App(sesClient, meetupClient)
     self.assertFalse(app.run(GROUP_URLNAME, DEFAULT_NOTIFY_PERIOD_HOURS))
Example #30
0
    def run():
        print("Welcome to Mardown - README - generator.\nWhat do you want?\nA) Help\tB) Create")
        input1 = input(">> ")

        # Help system
        if input1 == "a" or input1 == "A":
            Help.run()
        # Application system
        elif input1 == "b" or input1 == "B":
            path = Path.run()
            Save.run(path)
            App.init(path)
            App.run()
        # Error
        else:
            print("[!] Choose the options beetwen A or B!")
            Terminal.run()
Example #31
0
def main():
    try:
        from app import App, pygame_quit

        app_settings = {'window_width': 800, 'window_height': 480, 'FPS': 30}
        app = App(**app_settings)
        app.run()
    except Exception:
        logging.error(traceback.format_exc())
        # stop all parallel threads
        if hasattr(app, 'should_stop'):
            try:
                app.should_stop.set()
            except UnboundLocalError:
                pass
        # de-initialise pygame
        pygame_quit()
Example #32
0
    def test_it_exits_if_it_cannot_find_upcoming_event(self):
        sesClient = getSesClientMock()
        meetupClient = getMeetupClientMock(
            rsvps=getFixtureRsvps(),
            event=None,
        )

        app = App(sesClient, meetupClient)
        self.assertFalse(app.run(GROUP_URLNAME, DEFAULT_NOTIFY_PERIOD_HOURS))
Example #33
0
def start():

    parser = argparse.ArgumentParser()
    parser.add_argument('-n', type=int, help="本次需要获取漏洞总数")
    parser.add_argument('--proxy', help="代理ip,支持http代理,如http://127.0.0.1:8087")
    parser.add_argument('--date', help="设置获取漏洞的开始日期,格式为YYYY-MM-DD")
    args = parser.parse_args()
    res = {'proxy': False, 'num': '', 'date': ''}
    if args.proxy:
        HTTP_PROXY = args.proxy
        res['proxy'] = True
    if args.n:
        res['num'] = args.n
    if args.date:
        res['date'] = args.date
    if args.n or args.date:
        app = App(res)
        app.run()
Example #34
0
    def test_it_exits_if_upcoming_event_is_far_into_the_future(self):
        sesClient = getSesClientMock()
        eventFixture = getFixtureEvent()
        meetupClient = getMeetupClientMock(
            rsvps=getFixtureRsvps(),
            event=eventFixture,
        )

        app = App(sesClient, meetupClient)
        sesClient.send.assert_not_called()
        self.assertFalse(app.run(GROUP_URLNAME, 1))
Example #35
0
def main():
    # Some ports requires to allocate extra mem for exceptions
    if hasattr(micropython, 'alloc_emergency_exception_buf'):
        micropython.alloc_emergency_exception_buf(100)

    # Main loop
    try:
        loop = asyncio.get_event_loop()
        app = App(loop)
        app.run()
        loop.run_forever()
    except KeyboardInterrupt as e:
        pass
    except Exception as e:
        log = logging.Logger('main')
        log.exc(e, "Unhandled exception in main loop")

    # Gracefully stop app
    app.stop()
    loop.run_until_complete(shutdown_wait())
Example #36
0
def main():
    args = get_cmd_arguments()
    app = App(os.path.abspath(args.config))
    app.run()
Example #37
0
from app import App
App.run(host='0.0.0.0')
Example #38
0
File: wsgi.py Project: martyni/app
from app import App as application

if __name__ == "__main__":
   application.run()
Example #39
0
def main():
  app = App(GameFactory())
  app.run()
Example #40
0
#!flask/bin/python

from app import App
App.run(debug = True)
Example #41
0
import logging
logging.basicConfig(level=logging.DEBUG)

from widget import Widget
from app import App

import hooks

app = App("console.json")

app.run()
Example #42
0
class BaseLyricSourcePlugin(DBusObject):
    """ Base class for implementing a lyric source plugin
    """

    def __init__(self, id, name=None, watch_daemon=True):
        """
        Create a new lyric source instance. 

        Arguments:

        - `id`: The unique ID of the lyric source plugin. The full bus
          name of the plugin will be `org.osdlyrics.LyricSourcePlugin.id`
        - `name`: (optional) The name of the plugin, which should be properly
          localized. If `name` is missing, the plugin will take `id` as its
          name.
        - `watch_daemon`: Whether to watch daemon bus.
        """
        self._id = id
        self._app = App('LyricSourcePlugin.' + id,
                        watch_daemon=watch_daemon)
        DBusObject.__init__(self,
                            conn=self._app.connection,
                            object_path=LYRIC_SOURCE_PLUGIN_OBJECT_PATH_PREFIX + self._id)
        self._search_count = 0
        self._download_count = 0
        self._search_tasks = {}
        self._download_tasks = {}
        self._name = name if name is not None else id
        self._config = None

    def do_search(self, metadata):
        """
        Do the real search work by plugins. All plugins MUST implement this method.

        This method runs in a seperate thread, so don't worry about block IO.

        Parameters:

        - `metadata`: The metadata of the track to search. The type of `metadata`
          is osdlyrics.metadata.Metadata

        Returns: A list of SearchResult objects
        """
        raise NotImplementedError()

    @onmainthread
    def do_searchsuccess(self, ticket, results):
        if ticket in self._search_tasks:
            del self._search_tasks[ticket]
            dbusresults = [result.to_dict() for result in results]
            self.SearchComplete(ticket, SEARCH_SUCCEED, dbusresults)

    @onmainthread
    def do_searchfailure(self, ticket, e):
        if ticket in self._search_tasks:
            del self._search_tasks[ticket]
            logging.info('Search fail, %s' % e)
            self.SearchComplete(ticket, SEARCH_FAILED, [])

    @dbus.service.method(dbus_interface=LYRIC_SOURCE_PLUGIN_INTERFACE,
                         in_signature='a{sv}',
                         out_signature='i')
    def Search(self, metadata):
        ticket = self._search_count
        self._search_count = self._search_count + 1
        thread = BaseTaskThread(onfinish=lambda result: self.do_searchsuccess(self._app, ticket, result),
                                onerror=lambda e: self.do_searchfailure(self._app, ticket, e),
                                target=self.do_search,
                                kwargs={'metadata': Metadata.from_dict(metadata)})
        self._search_tasks[ticket] = thread
        thread.start()
        return ticket

    @dbus.service.method(dbus_interface=LYRIC_SOURCE_PLUGIN_INTERFACE,
                         in_signature='i',
                         out_signature='')
    def CancelSearch(self, ticket):
        if ticket in self._search_tasks:
            del self._search_tasks[ticket]
            self.SearchComplete(ticket, SEARCH_CANCELLED, [])


    def do_download(self, downloadinfo):
        """
        Do the real download work by plugins. All plugins MUST implement this
        method.

        This method runs in a seperate thread, so don't worry about block IO.

        Parameters:

        - `downloadinfo`: The additional info taken from `downloadinfo` field in
          SearchResult objects. 

        Returns: A string of the lyric content
        """
        raise NotImplementedError()

    @onmainthread
    def do_downloadsuccess(self, ticket, content):
        if ticket in self._download_tasks:
            del self._download_tasks[ticket]
            self.DownloadComplete(ticket, DOWNLOAD_SUCCEED, str(content))

    @onmainthread
    def do_downloadfailure(self, ticket, e):
        if ticket in self._download_tasks:
            del self._download_tasks[ticket]
            self.DownloadComplete(ticket, DOWNLOAD_FAILED, str(e))

    @dbus.service.method(dbus_interface=LYRIC_SOURCE_PLUGIN_INTERFACE,
                         in_signature='v',
                         out_signature='i')
    def Download(self, downloadinfo):
        ticket = self._download_count
        self._download_count = self._download_count + 1
        thread = BaseTaskThread(onfinish=lambda content: self.do_downloadsuccess(self._app, ticket, content),
                                onerror=lambda e: self.do_downloadfailure(self._app, ticket, e),
                                target=self.do_download,
                                kwargs={'downloadinfo': downloadinfo})
        self._download_tasks[ticket] = thread
        thread.start()
        return ticket

    @dbus.service.method(dbus_interface=LYRIC_SOURCE_PLUGIN_INTERFACE,
                         in_signature='i',
                         out_signature='')
    def CancelDownload(self, ticket):
        if ticket in self._download_tasks:
            del self._download_tasks[ticket]
            self.DownloadComplete(ticket, DOWNLOAD_CANCELLED, '')

    @dbusproperty(dbus_interface=LYRIC_SOURCE_PLUGIN_INTERFACE,
                  type_signature='s')
    def Name(self):
        return self._name

    @dbus.service.signal(dbus_interface=LYRIC_SOURCE_PLUGIN_INTERFACE,
                         signature='iiaa{sv}')
    def SearchComplete(self, ticket, status, results):
        logging.debug('search complete: ticket: %d, status: %d' % (ticket, status))
        pass
        
    
    @dbus.service.signal(dbus_interface=LYRIC_SOURCE_PLUGIN_INTERFACE,
                         signature='iiay')
    def DownloadComplete(self, ticket, status, result):
        logging.debug('download complete: ticket: %d, status: %d' % (ticket, status), '' if status == DOWNLOAD_SUCCEED else ', result: %s' % result)
        pass

    def run(self):
        """
        Run the plugin as a standalone application
        """
        self._app.run()

    @property
    def app(self):
        """
        Return the App object that the plugin uses.
        """
        return self._app

    @property
    def id(self):
        """
        Return the ID of the lyric source
        """
        return self._id

    @property
    def config_proxy(self):
        if self._config is None:
            self._config = Config(self._app.connection)
        return self._config