Best Python code snippet using avocado_python
mpd2widget.py
Source:mpd2widget.py  
1"""2A widget for Music Player Daemon (MPD) based on python-mpd2.3This widget exists since python-mpd library is no longer supported.4"""5from collections import defaultdict6from html import escape7from socket import error as socket_error8from mpd import CommandError, ConnectionError, MPDClient9from libqtile import utils10from libqtile.log_utils import logger11from libqtile.widget import base12# Mouse Interaction13# TODO: Volume inc/dec support14keys = {15    # Left mouse button16    "toggle": 1,17    # Right mouse button18    "stop": 3,19    # Scroll up20    "previous": 4,21    # Scroll down22    "next": 5,23    # User defined command24    "command": None25}26# To display mpd state27play_states = {28    'play': '\u25b6',29    'pause': '\u23F8',30    'stop': '\u25a0'31}32def option(char):33    """34    old status mapping method.35    Deprecated.36    """37    def _convert(elements, key, space):38        if key in elements and elements[key] != '0':39            elements[key] = char40        else:41            elements[key] = space42    return _convert43# Changes to formatter will still use this dicitionary as a fallback44prepare_status = {45    'repeat': option('r'),46    'random': option('z'),47    'single': option('1'),48    'consume': option('c'),49    'updating_db': option('U')50}51# dictionary for new formatting method.  This is now default.52status_dict = {53    'repeat': 'r',54    'random': 'z',55    'single': '1',56    'consume': 'c',57    'updating_db': 'U'58}59default_idle_message = "MPD IDLE"60default_idle_format = '{play_status} {idle_message}' +\61                 '[{repeat}{random}{single}{consume}{updating_db}]'62default_format = '{play_status} {artist}/{title} ' +\63                 '[{repeat}{random}{single}{consume}{updating_db}]'64def default_cmd(): return None65format_fns = {66    'all': escape,67}68class Mpd2(base.ThreadPoolText):69    r"""Mpd2 Object.70    Parameters71    ==========72    status_format :73        format string to display status74        For a full list of values, see:75            MPDClient.status() and MPDClient.currentsong()76        https://musicpd.org/doc/protocol/command_reference.html#command_status77        https://musicpd.org/doc/protocol/tags.html78        Default::79            '{play_status} {artist}/{title} \80                [{repeat}{random}{single}{consume}{updating_db}]'81            ``play_status`` is a string from ``play_states`` dict82            Note that the ``time`` property of the song renamed to ``fulltime``83            to prevent conflicts with status information during formating.84    idle_format :85        format string to display status when no song is in queue.86        Default::87            '{play_status} {idle_message} \88                [{repeat}{random}{single}{consume}{updating_db}]'89    idle_message :90        text to display instead of song information when MPD is idle.91        (i.e. no song in queue)92        Default:: "MPD IDLE"93    prepare_status :94        dict of functions to replace values in status with custom characters.95        ``f(status, key, space_element) => str``96        New functionality allows use of a dictionary of plain strings.97        Default::98            status_dict = {99                'repeat': 'r',100                'random': 'z',101                'single': '1',102                'consume': 'c',103                'updating_db': 'U'104            }105    format_fns :106        A dict of functions to format the various elements.107        'Tag' : f(str) => str108        Default:: { 'all': lambda s: cgi.escape(s) }109        N.B. if 'all' is present, it is processed on every element of song_info110            before any other formatting is done.111    mouse_buttons :112        A dict of mouse button numbers to actions113    Widget requirements: python-mpd2_.114    .. _python-mpd2: https://pypi.org/project/python-mpd2/115    """116    orientations = base.ORIENTATION_HORIZONTAL117    defaults = [118        ('update_interval', 1, 'Interval of update widget'),119        ('host', 'localhost', 'Host of mpd server'),120        ('port', 6600, 'Port of mpd server'),121        ('password', None, 'Password for auth on mpd server'),122        ('keys', keys, 'mouse button mapping. action -> b_num. deprecated.'),123        ('mouse_buttons', {}, 'b_num -> action. replaces keys.'),124        ('play_states', play_states, 'Play state mapping'),125        ('format_fns', format_fns, 'Dictionary of format methods'),126        ('command', default_cmd,127            'command to be executed by mapped mouse button.'),128        ('prepare_status', status_dict,129            'characters to show the status of MPD'),130        ('status_format', default_format, 'format for displayed song info.'),131        ('idle_format', default_idle_format,132            'format for status when mpd has no playlist.'),133        ('idle_message', default_idle_message,134            'text to display when mpd is idle.'),135        ('timeout', 30, 'MPDClient timeout'),136        ('idletimeout', 5, 'MPDClient idle command timeout'),137        ('no_connection', 'No connection', 'Text when mpd is disconnected'),138        ('color_progress', None, 'Text color to indicate track progress.'),139        ('space', '-', 'Space keeper')140    ]141    def __init__(self, **config):142        """Constructor."""143        super().__init__(None, **config)144        self.add_defaults(Mpd2.defaults)145        self.client = MPDClient()146        self.client.timeout = self.timeout147        self.client.idletimeout = self.idletimeout148        if self.color_progress:149            self.color_progress = utils.hex(self.color_progress)150        # remap self.keys as mouse_buttons for new button_press functionality.151        # so we don't break existing configurations.152        # TODO: phase out use of self.keys in favor of self.mouse_buttons153        if self.mouse_buttons == {}:154            for k in self.keys:155                if self.keys[k] is not None:156                    self.mouse_buttons[self.keys[k]] = k157    @property158    def connected(self):159        """Attempt connection to mpd server."""160        try:161            self.client.ping()  # pylint: disable=E1101162        except(socket_error, ConnectionError):163            try:164                self.client.connect(self.host, self.port)165                if self.password:166                    self.client.password(self.password)  # pylint: disable=E1101167            except(socket_error, ConnectionError, CommandError):168                return False169        return True170    def poll(self):171        """172        Called by qtile manager.173        poll the mpd server and update widget.174        """175        if self.connected:176            return self.update_status()177        else:178            return self.no_connection179    def update_status(self):180        """get updated info from mpd server and call format."""181        self.client.command_list_ok_begin()182        self.client.status()  # pylint: disable=E1101183        self.client.currentsong()  # pylint: disable=E1101184        status, current_song = self.client.command_list_end()185        return self.formatter(status, current_song)186    def button_press(self, x, y, button):187        """handle click event on widget."""188        base.ThreadPoolText.button_press(self, x, y, button)189        m_name = self.mouse_buttons[button]190        if self.connected:191            if hasattr(self, m_name):192                self.__try_call(m_name)193            elif hasattr(self.client, m_name):194                self.__try_call(m_name, self.client)195    def __try_call(self, attr_name, obj=None):196        err1 = 'Class {Class} has no attribute {attr}.'197        err2 = 'attribute "{Class}.{attr}" is not callable.'198        context = obj or self199        try:200            getattr(context, attr_name)()201        except (AttributeError, TypeError) as e:202            if isinstance(e, AttributeError):203                err = err1.format(Class=type(context).__name__, attr=attr_name)204            else:205                err = err2.format(Class=type(context).__name__, attr=attr_name)206            logger.exception(err + " {}".format(e.args[0]))207    def toggle(self):208        """toggle play/pause."""209        status = self.client.status()  # pylint: disable=E1101210        play_status = status['state']211        if play_status == 'play':212            self.client.pause()  # pylint: disable=E1101213        else:214            self.client.play()  # pylint: disable=E1101215    def formatter(self, status, current_song):216        """format song info."""217        default = 'Undefined'218        song_info = defaultdict(lambda: default)219        song_info['play_status'] = self.play_states[status['state']]220        if status['state'] == 'stop' and current_song == {}:221            song_info['idle_message'] = self.idle_message222            fmt = self.idle_format223        else:224            fmt = self.status_format225        for k in current_song:226            song_info[k] = current_song[k]227        song_info['fulltime'] = song_info['time']228        del song_info['time']229        song_info.update(status)230        if song_info['updating_db'] == default:231            song_info['updating_db'] = '0'232        if not callable(self.prepare_status['repeat']):233            for k in self.prepare_status:234                if k in status and status[k] != '0':235                    # Much more direct.236                    song_info[k] = self.prepare_status[k]237                else:238                    song_info[k] = self.space239        else:240            self.prepare_formatting(song_info)241        # 'remaining' isn't actually in the information provided by mpd242        # so we construct it from 'fulltime' and 'elapsed'.243        # 'elapsed' is always less than or equal to 'fulltime', if it exists.244        # Remaining should default to '00:00' if either or both are missing.245        # These values are also used for coloring text by progress, if wanted.246        if 'remaining' in self.status_format or self.color_progress:247            total = float(song_info['fulltime'])\248                if song_info['fulltime'] != default else 0.0249            elapsed = float(song_info['elapsed'])\250                if song_info['elapsed'] != default else 0.0251            song_info['remaining'] = "{:.2f}".format(float(total - elapsed))252        # mpd serializes tags containing commas as lists.253        for key in song_info:254            if isinstance(song_info[key], list):255                song_info[key] = ', '.join(song_info[key])256        # Now we apply the user formatting to selected elements in song_info.257        # if 'all' is defined, it is applied first.258        # the reason for this is that, if the format functions do pango markup.259        # we don't want to do anything that would mess it up, e.g. `escape`ing.260        if 'all' in self.format_fns:261            for key in song_info:262                song_info[key] = self.format_fns['all'](song_info[key])263        for fmt_fn in self.format_fns:264            if fmt_fn in song_info and fmt_fn != 'all':265                song_info[fmt_fn] = self.format_fns[fmt_fn](song_info[fmt_fn])266        # fmt = self.status_format267        if not isinstance(fmt, str):268            fmt = str(fmt)269        formatted = fmt.format_map(song_info)270        if self.color_progress and status['state'] != 'stop':271            try:272                progress = int(len(formatted) * elapsed / total)273                formatted = '<span color="{0}">{1}</span>{2}'.format(274                    self.color_progress, formatted[:progress], formatted[progress:],275                )276            except (ZeroDivisionError, ValueError):277                pass278        return formatted279    def prepare_formatting(self, status):280        """old way of preparing status formatting."""281        for key in self.prepare_status:282            self.prepare_status[key](status, key, self.space)283    def finalize(self):284        """finalize."""285        super().finalize()286        try:287            self.client.close()  # pylint: disable=E1101288            self.client.disconnect()289        except ConnectionError:...Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!
