This file is indexed.

/usr/share/doc/mopidy-doc/html/extensiondev.html is in mopidy-doc 2.1.0-1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">


<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    
    <title>Extension development &#8212; Mopidy 2.1.0 documentation</title>
    
    <link rel="stylesheet" href="_static/alabaster.css" type="text/css" />
    <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
    
    <script type="text/javascript">
      var DOCUMENTATION_OPTIONS = {
        URL_ROOT:    './',
        VERSION:     '2.1.0',
        COLLAPSE_INDEX: false,
        FILE_SUFFIX: '.html',
        HAS_SOURCE:  true
      };
    </script>
    <script type="text/javascript" src="_static/jquery.js"></script>
    <script type="text/javascript" src="_static/underscore.js"></script>
    <script type="text/javascript" src="_static/doctools.js"></script>
    <link rel="index" title="Index" href="genindex.html" />
    <link rel="search" title="Search" href="search.html" />
    <link rel="top" title="Mopidy 2.1.0 documentation" href="index.html" />
    <link rel="next" title="Glossary" href="glossary.html" />
    <link rel="prev" title="Code style" href="codestyle.html" />
   
  <link rel="stylesheet" href="_static/custom.css" type="text/css" />
  
  <meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9" />

  </head>
  <body role="document">
  

    <div class="document">
      <div class="documentwrapper">
        <div class="bodywrapper">
          <div class="body" role="main">
            
  <div class="section" id="extension-development">
<span id="extensiondev"></span><h1>Extension development<a class="headerlink" href="#extension-development" title="Permalink to this headline"></a></h1>
<p>Mopidy started as simply an MPD server that could play music from Spotify.
Early on, Mopidy got multiple &#8220;frontends&#8221; to expose Mopidy to more than just MPD
clients: for example the scrobbler frontend that scrobbles your listening
history to your Last.fm account, the MPRIS frontend that integrates Mopidy into the
Ubuntu Sound Menu, and the HTTP server and JavaScript player API making web
based Mopidy clients possible. In Mopidy 0.9 we added support for multiple
music sources without stopping and reconfiguring Mopidy: for example the local
backend for playing music from your disk, the stream backend for playing
Internet radio streams, and the Spotify and SoundCloud backends, for playing
music directly from those services.</p>
<p>All of these are examples of what you can accomplish by creating a Mopidy
extension. If you want to create your own Mopidy extension for something that
does not exist yet, this guide to extension development will help you get your
extension running in no time, and make it feel the way users would expect your
extension to behave.</p>
<div class="section" id="anatomy-of-an-extension">
<h2>Anatomy of an extension<a class="headerlink" href="#anatomy-of-an-extension" title="Permalink to this headline"></a></h2>
<p>Extensions are located in a Python package called <code class="docutils literal"><span class="pre">mopidy_something</span></code> where
&#8220;something&#8221; is the name of the application, library or web service you want to
integrate with Mopidy. So, for example, if you plan to add support for a service
named Soundspot to Mopidy, you would name your extension&#8217;s Python package
<code class="docutils literal"><span class="pre">mopidy_soundspot</span></code>.</p>
<p>The extension must be shipped with a <code class="docutils literal"><span class="pre">setup.py</span></code> file and be registered on
<a class="reference external" href="https://pypi.python.org/">PyPI</a>.  The name of the distribution on PyPI would
be something like &#8220;Mopidy-Soundspot&#8221;. Make sure to include the name &#8220;Mopidy&#8221;
somewhere in that name and that you check the capitalization. This is the name
users will use when they install your extension from PyPI.</p>
<p>Mopidy extensions must be licensed under an Apache 2.0 (like Mopidy itself),
BSD, MIT or more liberal license to be able to be enlisted in the Mopidy
documentation. The license text should be included in the <code class="docutils literal"><span class="pre">LICENSE</span></code> file in
the root of the extension&#8217;s Git repo.</p>
<p>Combining this together, we get the following folder structure for our
extension, Mopidy-Soundspot:</p>
<div class="highlight-default"><div class="highlight"><pre><span></span><span class="n">mopidy</span><span class="o">-</span><span class="n">soundspot</span><span class="o">/</span>           <span class="c1"># The Git repo root</span>
    <span class="n">LICENSE</span>                 <span class="c1"># The license text</span>
    <span class="n">MANIFEST</span><span class="o">.</span><span class="ow">in</span>             <span class="c1"># List of data files to include in PyPI package</span>
    <span class="n">README</span><span class="o">.</span><span class="n">rst</span>              <span class="c1"># Document what it is and how to use it</span>
    <span class="n">mopidy_soundspot</span><span class="o">/</span>       <span class="c1"># Your code</span>
        <span class="n">__init__</span><span class="o">.</span><span class="n">py</span>
        <span class="n">ext</span><span class="o">.</span><span class="n">conf</span>            <span class="c1"># Default config for the extension</span>
        <span class="o">...</span>
    <span class="n">setup</span><span class="o">.</span><span class="n">py</span>                <span class="c1"># Installation script</span>
</pre></div>
</div>
<p>Example content for the most important files follows below.</p>
</div>
<div class="section" id="cookiecutter-project-template">
<h2>cookiecutter project template<a class="headerlink" href="#cookiecutter-project-template" title="Permalink to this headline"></a></h2>
<p>We&#8217;ve also made a <a class="reference external" href="http://cookiecutter.readthedocs.org/">cookiecutter</a>
project template for <a class="reference external" href="https://github.com/mopidy/cookiecutter-mopidy-ext">creating new Mopidy extensions</a>. If you install
cookiecutter and run a single command, you&#8217;re asked a few questions about the
name of your extension, etc. This is used to create a folder structure similar
to the above, with all the needed files and most of the details filled in for
you. This saves you a lot of tedious work and copy-pasting from this howto. See
the readme of <a class="reference external" href="https://github.com/mopidy/cookiecutter-mopidy-ext">cookiecutter-mopidy-ext</a> for further details.</p>
</div>
<div class="section" id="example-readme-rst">
<h2>Example README.rst<a class="headerlink" href="#example-readme-rst" title="Permalink to this headline"></a></h2>
<p>The README file should quickly explain what the extension does, how to install
it, and how to configure it. It should also contain a link to a tarball of the
latest development version of the extension. It&#8217;s important that this link ends
with <code class="docutils literal"><span class="pre">#egg=Mopidy-Something-dev</span></code> for installation using
<code class="docutils literal"><span class="pre">pip</span> <span class="pre">install</span> <span class="pre">Mopidy-Something==dev</span></code> to work.</p>
<div class="highlight-rst"><div class="highlight"><pre><span></span><span class="gh">****************</span>
<span class="gh">Mopidy-Soundspot</span>
<span class="gh">****************</span>

<span class="s">`Mopidy </span><span class="si">&lt;http://www.mopidy.com/&gt;</span><span class="s">`_</span> extension for playing music from
<span class="s">`Soundspot </span><span class="si">&lt;http://soundspot.example.com/&gt;</span><span class="s">`_</span>.

Requires a Soundspot Platina subscription and the pysoundspot library.


<span class="gh">Installation</span>
<span class="gh">============</span>

Install by running<span class="se">::</span>

<span class="s">    sudo pip install Mopidy-Soundspot</span>

Or, if available, install the Debian/Ubuntu package from `apt.mopidy.com
<span class="nt">&lt;http://apt.mopidy.com/&gt;</span>`_.


<span class="gh">Configuration</span>
<span class="gh">=============</span>

Before starting Mopidy, you must add your Soundspot username and password
to the Mopidy configuration file<span class="se">::</span>

<span class="s">    [soundspot]</span>
<span class="s">    username = alice</span>
<span class="s">    password = secret</span>


<span class="gh">Project resources</span>
<span class="gh">=================</span>

<span class="m">-</span> <span class="s">`Source code </span><span class="si">&lt;https://github.com/mopidy/mopidy-soundspot&gt;</span><span class="s">`_</span>
<span class="m">-</span> <span class="s">`Issue tracker </span><span class="si">&lt;https://github.com/mopidy/mopidy-soundspot/issues&gt;</span><span class="s">`_</span>
<span class="m">-</span> <span class="s">`Development branch tarball </span><span class="si">&lt;https://github.com/mopidy/mopidy-soundspot/tarball/master#egg=Mopidy-Soundspot-dev&gt;</span><span class="s">`_</span>


<span class="gh">Changelog</span>
<span class="gh">=========</span>

<span class="gh">v0.1.0 (2013-09-17)</span>
<span class="gh">-------------------</span>

<span class="m">-</span> Initial release.
</pre></div>
</div>
</div>
<div class="section" id="example-setup-py">
<h2>Example setup.py<a class="headerlink" href="#example-setup-py" title="Permalink to this headline"></a></h2>
<p>The <code class="docutils literal"><span class="pre">setup.py</span></code> file must use setuptools, and not distutils. This is because
Mopidy extensions use setuptools&#8217; entry point functionality to register
themselves as available Mopidy extensions when they are installed on your
system.</p>
<p>The example below also includes a couple of convenient tricks for reading the
package version from the source code so that it is defined in a single place,
and to reuse the README file as the long description of the package for the
PyPI registration.</p>
<p>The package must have <code class="docutils literal"><span class="pre">install_requires</span></code> on <code class="docutils literal"><span class="pre">setuptools</span></code> and <code class="docutils literal"><span class="pre">Mopidy</span> <span class="pre">&gt;=</span>
<span class="pre">0.14</span></code> (or a newer version, if your extension requires it), in addition to any
other dependencies required by your extension. If you implement a Mopidy
frontend or backend, you&#8217;ll need to include <code class="docutils literal"><span class="pre">Pykka</span> <span class="pre">&gt;=</span> <span class="pre">1.1</span></code> in the
requirements. The <code class="docutils literal"><span class="pre">entry_points</span></code> part must be included. The <code class="docutils literal"><span class="pre">mopidy.ext</span></code>
part cannot be changed, but the innermost string should be changed. It&#8217;s format
is <code class="docutils literal"><span class="pre">ext_name</span> <span class="pre">=</span> <span class="pre">package_name:Extension</span></code>.  <code class="docutils literal"><span class="pre">ext_name</span></code> should be a short name
for your extension, typically the part after &#8220;Mopidy-&#8221; in lowercase. This name
is used e.g. to name the config section for your extension. The
<code class="docutils literal"><span class="pre">package_name:Extension</span></code> part is simply the Python path to the extension
class that will connect the rest of the dots.</p>
<div class="highlight-default"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">__future__</span> <span class="k">import</span> <span class="n">absolute_import</span><span class="p">,</span> <span class="n">unicode_literals</span>

<span class="kn">import</span> <span class="nn">re</span>
<span class="kn">from</span> <span class="nn">setuptools</span> <span class="k">import</span> <span class="n">setup</span><span class="p">,</span> <span class="n">find_packages</span>


<span class="k">def</span> <span class="nf">get_version</span><span class="p">(</span><span class="n">filename</span><span class="p">):</span>
    <span class="n">content</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="n">filename</span><span class="p">)</span><span class="o">.</span><span class="n">read</span><span class="p">()</span>
    <span class="n">metadata</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">(</span><span class="n">re</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="s2">&quot;__([a-z]+)__ = &#39;([^&#39;]+)&#39;&quot;</span><span class="p">,</span> <span class="n">content</span><span class="p">))</span>
    <span class="k">return</span> <span class="n">metadata</span><span class="p">[</span><span class="s1">&#39;version&#39;</span><span class="p">]</span>


<span class="n">setup</span><span class="p">(</span>
    <span class="n">name</span><span class="o">=</span><span class="s1">&#39;Mopidy-Soundspot&#39;</span><span class="p">,</span>
    <span class="n">version</span><span class="o">=</span><span class="n">get_version</span><span class="p">(</span><span class="s1">&#39;mopidy_soundspot/__init__.py&#39;</span><span class="p">),</span>
    <span class="n">url</span><span class="o">=</span><span class="s1">&#39;https://github.com/your-account/mopidy-soundspot&#39;</span><span class="p">,</span>
    <span class="n">license</span><span class="o">=</span><span class="s1">&#39;Apache License, Version 2.0&#39;</span><span class="p">,</span>
    <span class="n">author</span><span class="o">=</span><span class="s1">&#39;Your Name&#39;</span><span class="p">,</span>
    <span class="n">author_email</span><span class="o">=</span><span class="s1">&#39;your-email@example.com&#39;</span><span class="p">,</span>
    <span class="n">description</span><span class="o">=</span><span class="s1">&#39;Very short description&#39;</span><span class="p">,</span>
    <span class="n">long_description</span><span class="o">=</span><span class="nb">open</span><span class="p">(</span><span class="s1">&#39;README.rst&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">read</span><span class="p">(),</span>
    <span class="n">packages</span><span class="o">=</span><span class="n">find_packages</span><span class="p">(</span><span class="n">exclude</span><span class="o">=</span><span class="p">[</span><span class="s1">&#39;tests&#39;</span><span class="p">,</span> <span class="s1">&#39;tests.*&#39;</span><span class="p">]),</span>
    <span class="n">zip_safe</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span>
    <span class="n">include_package_data</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
    <span class="n">install_requires</span><span class="o">=</span><span class="p">[</span>
        <span class="s1">&#39;setuptools&#39;</span><span class="p">,</span>
        <span class="s1">&#39;Mopidy &gt;= 0.14&#39;</span><span class="p">,</span>
        <span class="s1">&#39;Pykka &gt;= 1.1&#39;</span><span class="p">,</span>
        <span class="s1">&#39;pysoundspot&#39;</span><span class="p">,</span>
    <span class="p">],</span>
    <span class="n">entry_points</span><span class="o">=</span><span class="p">{</span>
        <span class="s1">&#39;mopidy.ext&#39;</span><span class="p">:</span> <span class="p">[</span>
            <span class="s1">&#39;soundspot = mopidy_soundspot:Extension&#39;</span><span class="p">,</span>
        <span class="p">],</span>
    <span class="p">},</span>
    <span class="n">classifiers</span><span class="o">=</span><span class="p">[</span>
        <span class="s1">&#39;Environment :: No Input/Output (Daemon)&#39;</span><span class="p">,</span>
        <span class="s1">&#39;Intended Audience :: End Users/Desktop&#39;</span><span class="p">,</span>
        <span class="s1">&#39;License :: OSI Approved :: Apache Software License&#39;</span><span class="p">,</span>
        <span class="s1">&#39;Operating System :: OS Independent&#39;</span><span class="p">,</span>
        <span class="s1">&#39;Programming Language :: Python :: 2&#39;</span><span class="p">,</span>
        <span class="s1">&#39;Topic :: Multimedia :: Sound/Audio :: Players&#39;</span><span class="p">,</span>
    <span class="p">],</span>
<span class="p">)</span>
</pre></div>
</div>
<p>To make sure your README, license file and default config file is included in
the package that is uploaded to PyPI, we&#8217;ll also need to add a <code class="docutils literal"><span class="pre">MANIFEST.in</span></code>
file:</p>
<div class="highlight-default"><div class="highlight"><pre><span></span><span class="n">include</span> <span class="n">LICENSE</span>
<span class="n">include</span> <span class="n">MANIFEST</span><span class="o">.</span><span class="ow">in</span>
<span class="n">include</span> <span class="n">README</span><span class="o">.</span><span class="n">rst</span>
<span class="n">include</span> <span class="n">mopidy_soundspot</span><span class="o">/</span><span class="n">ext</span><span class="o">.</span><span class="n">conf</span>
</pre></div>
</div>
<p>For details on the <code class="docutils literal"><span class="pre">MANIFEST.in</span></code> file format, check out the <a class="reference external" href="https://docs.python.org/2/distutils/sourcedist.html#manifest-template">distutils docs</a>.
<a class="reference external" href="https://pypi.python.org/pypi/check-manifest">check-manifest</a> is a very
useful tool to check your <code class="docutils literal"><span class="pre">MANIFEST.in</span></code> file for completeness.</p>
</div>
<div class="section" id="example-init-py">
<h2>Example __init__.py<a class="headerlink" href="#example-init-py" title="Permalink to this headline"></a></h2>
<p>The <code class="docutils literal"><span class="pre">__init__.py</span></code> file should be placed inside the <code class="docutils literal"><span class="pre">mopidy_soundspot</span></code>
Python package.</p>
<p>The root of your Python package should have an <code class="docutils literal"><span class="pre">__version__</span></code> attribute with a
<span class="target" id="index-0"></span><a class="pep reference external" href="https://www.python.org/dev/peps/pep-0386"><strong>PEP 386</strong></a> compliant version number, for example &#8220;0.1&#8221;. Next, it should have a
class named <code class="docutils literal"><span class="pre">Extension</span></code> which inherits from Mopidy&#8217;s extension base class,
<a class="reference internal" href="api/ext.html#mopidy.ext.Extension" title="mopidy.ext.Extension"><code class="xref py py-class docutils literal"><span class="pre">mopidy.ext.Extension</span></code></a>. This is the class referred to in the
<code class="docutils literal"><span class="pre">entry_points</span></code> part of <code class="docutils literal"><span class="pre">setup.py</span></code>. Any imports of other files in your
extension, outside of Mopidy and it&#8217;s core requirements, should be kept inside
methods. This ensures that this file can be imported without raising
<code class="xref py py-exc docutils literal"><span class="pre">ImportError</span></code> exceptions for missing dependencies, etc.</p>
<p>The default configuration for the extension is defined by the
<code class="docutils literal"><span class="pre">get_default_config()</span></code> method in the <code class="docutils literal"><span class="pre">Extension</span></code> class which returns a
<code class="xref py py-mod docutils literal"><span class="pre">ConfigParser</span></code> compatible config section. The config section&#8217;s name must
be the same as the extension&#8217;s short name, as defined in the <code class="docutils literal"><span class="pre">entry_points</span></code>
part of <code class="docutils literal"><span class="pre">setup.py</span></code>, for example <code class="docutils literal"><span class="pre">soundspot</span></code>. All extensions must include
an <code class="docutils literal"><span class="pre">enabled</span></code> config which normally should default to <code class="docutils literal"><span class="pre">true</span></code>. Provide good
defaults for all config values so that as few users as possible will need to
change them. The exception is if the config value has security implications; in
that case you should default to the most secure configuration. Leave any
configurations that don&#8217;t have meaningful defaults blank, like <code class="docutils literal"><span class="pre">username</span></code>
and <code class="docutils literal"><span class="pre">password</span></code>. In the example below, we&#8217;ve chosen to maintain the default
config as a separate file named <code class="docutils literal"><span class="pre">ext.conf</span></code>. This makes it easy to include the
default config in documentation without duplicating it.</p>
<p>This is <code class="docutils literal"><span class="pre">mopidy_soundspot/__init__.py</span></code>:</p>
<div class="highlight-default"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">__future__</span> <span class="k">import</span> <span class="n">absolute_import</span><span class="p">,</span> <span class="n">unicode_literals</span>

<span class="kn">import</span> <span class="nn">logging</span>
<span class="kn">import</span> <span class="nn">os</span>

<span class="kn">from</span> <span class="nn">mopidy</span> <span class="k">import</span> <span class="n">config</span><span class="p">,</span> <span class="n">exceptions</span><span class="p">,</span> <span class="n">ext</span>


<span class="n">__version__</span> <span class="o">=</span> <span class="s1">&#39;0.1&#39;</span>

<span class="c1"># If you need to log, use loggers named after the current Python module</span>
<span class="n">logger</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">getLogger</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span>


<span class="k">class</span> <span class="nc">Extension</span><span class="p">(</span><span class="n">ext</span><span class="o">.</span><span class="n">Extension</span><span class="p">):</span>

    <span class="n">dist_name</span> <span class="o">=</span> <span class="s1">&#39;Mopidy-Soundspot&#39;</span>
    <span class="n">ext_name</span> <span class="o">=</span> <span class="s1">&#39;soundspot&#39;</span>
    <span class="n">version</span> <span class="o">=</span> <span class="n">__version__</span>

    <span class="k">def</span> <span class="nf">get_default_config</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="n">conf_file</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">dirname</span><span class="p">(</span><span class="n">__file__</span><span class="p">),</span> <span class="s1">&#39;ext.conf&#39;</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">config</span><span class="o">.</span><span class="n">read</span><span class="p">(</span><span class="n">conf_file</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">get_config_schema</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="n">schema</span> <span class="o">=</span> <span class="nb">super</span><span class="p">(</span><span class="n">Extension</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="n">get_config_schema</span><span class="p">()</span>
        <span class="n">schema</span><span class="p">[</span><span class="s1">&#39;username&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">config</span><span class="o">.</span><span class="n">String</span><span class="p">()</span>
        <span class="n">schema</span><span class="p">[</span><span class="s1">&#39;password&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">config</span><span class="o">.</span><span class="n">Secret</span><span class="p">()</span>
        <span class="k">return</span> <span class="n">schema</span>

    <span class="k">def</span> <span class="nf">get_command</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="kn">from</span> <span class="nn">.commands</span> <span class="k">import</span> <span class="n">SoundspotCommand</span>
        <span class="k">return</span> <span class="n">SoundspotCommand</span><span class="p">()</span>

    <span class="k">def</span> <span class="nf">validate_environment</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="c1"># Any manual checks of the environment to fail early.</span>
        <span class="c1"># Dependencies described by setup.py are checked by Mopidy, so you</span>
        <span class="c1"># should not check their presence here.</span>
        <span class="k">pass</span>

    <span class="k">def</span> <span class="nf">setup</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">registry</span><span class="p">):</span>
        <span class="c1"># You will typically only do one of the following things in a</span>
        <span class="c1"># single extension.</span>

        <span class="c1"># Register a frontend</span>
        <span class="kn">from</span> <span class="nn">.frontend</span> <span class="k">import</span> <span class="n">SoundspotFrontend</span>
        <span class="n">registry</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="s1">&#39;frontend&#39;</span><span class="p">,</span> <span class="n">SoundspotFrontend</span><span class="p">)</span>

        <span class="c1"># Register a backend</span>
        <span class="kn">from</span> <span class="nn">.backend</span> <span class="k">import</span> <span class="n">SoundspotBackend</span>
        <span class="n">registry</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="s1">&#39;backend&#39;</span><span class="p">,</span> <span class="n">SoundspotBackend</span><span class="p">)</span>

        <span class="c1"># Or nothing to register e.g. command extension</span>
        <span class="k">pass</span>
</pre></div>
</div>
<p>And this is <code class="docutils literal"><span class="pre">mopidy_soundspot/ext.conf</span></code>:</p>
<div class="highlight-ini"><div class="highlight"><pre><span></span><span class="k">[soundspot]</span>
<span class="na">enabled</span> <span class="o">=</span> <span class="s">true</span>
<span class="na">username</span> <span class="o">=</span>
<span class="na">password</span> <span class="o">=</span>
</pre></div>
</div>
<p>For more detailed documentation on the extension class, see the <a class="reference internal" href="api/ext.html#ext-api"><span class="std std-ref">mopidy.ext &#8211; Extension API</span></a>.</p>
</div>
<div class="section" id="example-frontend">
<h2>Example frontend<a class="headerlink" href="#example-frontend" title="Permalink to this headline"></a></h2>
<p>If you want to <em>use</em> Mopidy&#8217;s core API from your extension, then you want to
implement a frontend.</p>
<p>The skeleton of a frontend would look like this. Notice that the frontend gets
passed a reference to the core API when it&#8217;s created. See the
<a class="reference internal" href="api/frontend.html#frontend-api"><span class="std std-ref">Frontend API</span></a> for more details.</p>
<div class="highlight-default"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">pykka</span>

<span class="kn">from</span> <span class="nn">mopidy</span> <span class="k">import</span> <span class="n">core</span>


<span class="k">class</span> <span class="nc">SoundspotFrontend</span><span class="p">(</span><span class="n">pykka</span><span class="o">.</span><span class="n">ThreadingActor</span><span class="p">,</span> <span class="n">core</span><span class="o">.</span><span class="n">CoreListener</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">config</span><span class="p">,</span> <span class="n">core</span><span class="p">):</span>
        <span class="nb">super</span><span class="p">(</span><span class="n">SoundspotFrontend</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="n">__init__</span><span class="p">()</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">core</span> <span class="o">=</span> <span class="n">core</span>

    <span class="c1"># Your frontend implementation</span>
</pre></div>
</div>
</div>
<div class="section" id="example-backend">
<h2>Example backend<a class="headerlink" href="#example-backend" title="Permalink to this headline"></a></h2>
<p>If you want to extend Mopidy to support new music and playlist sources, you
want to implement a backend. A backend does not have access to Mopidy&#8217;s core
API at all, but it does have a bunch of interfaces it can implement to extend
Mopidy.</p>
<p>The skeleton of a backend would look like this. See <a class="reference internal" href="api/backend.html#backend-api"><span class="std std-ref">mopidy.backend &#8212; Backend API</span></a> for more
details.</p>
<div class="highlight-default"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">pykka</span>

<span class="kn">from</span> <span class="nn">mopidy</span> <span class="k">import</span> <span class="n">backend</span>


<span class="k">class</span> <span class="nc">SoundspotBackend</span><span class="p">(</span><span class="n">pykka</span><span class="o">.</span><span class="n">ThreadingActor</span><span class="p">,</span> <span class="n">backend</span><span class="o">.</span><span class="n">Backend</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">config</span><span class="p">,</span> <span class="n">audio</span><span class="p">):</span>
        <span class="nb">super</span><span class="p">(</span><span class="n">SoundspotBackend</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="n">__init__</span><span class="p">()</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">audio</span> <span class="o">=</span> <span class="n">audio</span>

    <span class="c1"># Your backend implementation</span>
</pre></div>
</div>
</div>
<div class="section" id="example-command">
<h2>Example command<a class="headerlink" href="#example-command" title="Permalink to this headline"></a></h2>
<p>If you want to extend the Mopidy with a new helper not run from the server,
such as scanning for media, adding a command is the way to go. Your top level
command name will always match your extension name, but you are free to add
sub-commands with names of your choosing.</p>
<p>The skeleton of a command would look like this. See <a class="reference internal" href="api/commands.html#commands-api"><span class="std std-ref">mopidy.commands &#8212; Commands API</span></a> for
more details.</p>
<div class="highlight-default"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">mopidy</span> <span class="k">import</span> <span class="n">commands</span>


<span class="k">class</span> <span class="nc">SoundspotCommand</span><span class="p">(</span><span class="n">commands</span><span class="o">.</span><span class="n">Command</span><span class="p">):</span>
    <span class="n">help</span> <span class="o">=</span> <span class="s1">&#39;Some text that will show up in --help&#39;</span>

    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="nb">super</span><span class="p">(</span><span class="n">SoundspotCommand</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="n">__init__</span><span class="p">()</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s1">&#39;--foo&#39;</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">run</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">args</span><span class="p">,</span> <span class="n">config</span><span class="p">,</span> <span class="n">extensions</span><span class="p">):</span>
       <span class="c1"># Your command implementation</span>
       <span class="k">return</span> <span class="mi">0</span>
</pre></div>
</div>
</div>
<div class="section" id="example-web-application">
<h2>Example web application<a class="headerlink" href="#example-web-application" title="Permalink to this headline"></a></h2>
<p>As of Mopidy 0.19, extensions can use Mopidy&#8217;s built-in web server to host
static web clients as well as Tornado and WSGI web applications. For several
examples, see the <a class="reference internal" href="api/http-server.html#http-server-api"><span class="std std-ref">HTTP server side API</span></a> docs or explore with
<a class="reference internal" href="ext/web.html#http-explore-extension"><span class="std std-ref">Mopidy-API-Explorer</span></a> extension.</p>
</div>
<div class="section" id="running-an-extension">
<h2>Running an extension<a class="headerlink" href="#running-an-extension" title="Permalink to this headline"></a></h2>
<p>Once your extension is ready to go, to see it in action you&#8217;ll need to register
it with Mopidy. Typically this is done by running <code class="docutils literal"><span class="pre">python</span> <span class="pre">setup.py</span> <span class="pre">install</span></code>
from your extension&#8217;s Git repo root directory. While developing your extension
and to avoid doing this every time you make a change, you can instead run
<code class="docutils literal"><span class="pre">python</span> <span class="pre">setup.py</span> <span class="pre">develop</span></code> to effectively link Mopidy directly with your
development files.</p>
</div>
<div class="section" id="python-conventions">
<h2>Python conventions<a class="headerlink" href="#python-conventions" title="Permalink to this headline"></a></h2>
<p>In general, it would be nice if Mopidy extensions followed the same
<a class="reference internal" href="codestyle.html#codestyle"><span class="std std-ref">Code style</span></a> as Mopidy itself, as they&#8217;re part of the same ecosystem. Among
other things, the code style guide explains why all the above examples start
with <code class="docutils literal"><span class="pre">from</span> <span class="pre">__future__</span> <span class="pre">import</span> <span class="pre">absolute_import,</span> <span class="pre">unicode_literals</span></code>.</p>
</div>
<div class="section" id="use-of-mopidy-apis">
<h2>Use of Mopidy APIs<a class="headerlink" href="#use-of-mopidy-apis" title="Permalink to this headline"></a></h2>
<p>When writing an extension, you should only use APIs documented at
<a class="reference internal" href="api/index.html#api-ref"><span class="std std-ref">API reference</span></a>. Other parts of Mopidy, like <code class="xref py py-mod docutils literal"><span class="pre">mopidy.internal</span></code>, may change
at any time and are not something extensions should use.</p>
<p>Mopidy performs type checking to help catch extension bugs. This applies to
both frontend calls into core and return values from backends. Additionally
model fields always get validated to further guard against bad data.</p>
</div>
<div class="section" id="logging-in-extensions">
<h2>Logging in extensions<a class="headerlink" href="#logging-in-extensions" title="Permalink to this headline"></a></h2>
<p>For servers like Mopidy, logging is essential for understanding what&#8217;s
going on. We use the <code class="xref py py-mod docutils literal"><span class="pre">logging</span></code> module from Python&#8217;s standard library. When
creating a logger, always namespace the logger using your Python package name
as this will be visible in Mopidy&#8217;s debug log:</p>
<div class="highlight-default"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">logging</span>

<span class="n">logger</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">getLogger</span><span class="p">(</span><span class="s1">&#39;mopidy_soundspot&#39;</span><span class="p">)</span>

<span class="c1"># Or even better, use the Python module name as the logger name:</span>
<span class="n">logger</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">getLogger</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span>
</pre></div>
</div>
<p>When logging at logging level <code class="docutils literal"><span class="pre">info</span></code> or higher (i.e. <code class="docutils literal"><span class="pre">warning</span></code>, <code class="docutils literal"><span class="pre">error</span></code>,
and <code class="docutils literal"><span class="pre">critical</span></code>, but not <code class="docutils literal"><span class="pre">debug</span></code>) the log message will be displayed to all
Mopidy users. Thus, the log messages at those levels should be well written and
easy to understand.</p>
<p>As the logger name is not included in Mopidy&#8217;s default logging format, you
should make it obvious from the log message who is the source of the log
message. For example:</p>
<div class="highlight-default"><div class="highlight"><pre><span></span><span class="n">Loaded</span> <span class="mi">17</span> <span class="n">Soundspot</span> <span class="n">playlists</span>
</pre></div>
</div>
<p>Is much better than:</p>
<div class="highlight-default"><div class="highlight"><pre><span></span><span class="n">Loaded</span> <span class="mi">17</span> <span class="n">playlists</span>
</pre></div>
</div>
<p>If you want to turn on debug logging for your own extension, but not for
everything else due to the amount of noise, see the docs for the
<a class="reference internal" href="config.html#confval-loglevels/*"><code class="xref std std-confval docutils literal"><span class="pre">loglevels/*</span></code></a> config section.</p>
</div>
<div class="section" id="making-http-requests-from-extensions">
<h2>Making HTTP requests from extensions<a class="headerlink" href="#making-http-requests-from-extensions" title="Permalink to this headline"></a></h2>
<p>Many Mopidy extensions need to make HTTP requests to use some web API. Here&#8217;s a
few recommendations to those extensions.</p>
<div class="section" id="proxies">
<h3>Proxies<a class="headerlink" href="#proxies" title="Permalink to this headline"></a></h3>
<p>If you make HTTP requests please make sure to respect the <a class="reference internal" href="config.html#proxy-config"><span class="std std-ref">proxy configs</span></a>, so that all the requests you make go through the proxy
configured by the Mopidy user. To make this easier for extension developers,
the helper function <a class="reference internal" href="api/httpclient.html#mopidy.httpclient.format_proxy" title="mopidy.httpclient.format_proxy"><code class="xref py py-func docutils literal"><span class="pre">mopidy.httpclient.format_proxy()</span></code></a> was added in Mopidy
1.1. This function returns the proxy settings <a class="reference external" href="http://www.python-requests.org/en/latest/user/advanced/#proxies">formatted the way Requests
expects</a>.</p>
</div>
<div class="section" id="user-agent-strings">
<h3>User-Agent strings<a class="headerlink" href="#user-agent-strings" title="Permalink to this headline"></a></h3>
<p>When you make HTTP requests, it&#8217;s helpful for debugging and usage analysis if
the client identifies itself with a proper User-Agent string. In Mopidy 1.1, we
added the helper function <a class="reference internal" href="api/httpclient.html#mopidy.httpclient.format_user_agent" title="mopidy.httpclient.format_user_agent"><code class="xref py py-func docutils literal"><span class="pre">mopidy.httpclient.format_user_agent()</span></code></a>.  Here&#8217;s
an example of how to use it:</p>
<div class="highlight-default"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="kn">from</span> <span class="nn">mopidy</span> <span class="k">import</span> <span class="n">httpclient</span>
<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">mopidy_soundspot</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">httpclient</span><span class="o">.</span><span class="n">format_user_agent</span><span class="p">(</span><span class="s1">&#39;</span><span class="si">%s</span><span class="s1">/</span><span class="si">%s</span><span class="s1">&#39;</span> <span class="o">%</span> <span class="p">(</span>
<span class="gp">... </span>    <span class="n">mopidy_soundspot</span><span class="o">.</span><span class="n">Extension</span><span class="o">.</span><span class="n">dist_name</span><span class="p">,</span> <span class="n">mopidy_soundspot</span><span class="o">.</span><span class="n">__version__</span><span class="p">))</span>
<span class="go">u&#39;Mopidy-SoundSpot/2.0.0 Mopidy/1.0.7 Python/2.7.10&#39;</span>
</pre></div>
</div>
</div>
<div class="section" id="example-using-requests-sessions">
<h3>Example using Requests sessions<a class="headerlink" href="#example-using-requests-sessions" title="Permalink to this headline"></a></h3>
<p>Most Mopidy extensions that make HTTP requests use the <a class="reference external" href="http://www.python-requests.org/">Requests</a> library to do so. When using Requests, the
most convenient way to make sure the proxy and User-Agent header is set
properly is to create a Requests session object and use that object to make all
your HTTP requests:</p>
<div class="highlight-default"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">mopidy</span> <span class="k">import</span> <span class="n">httpclient</span>

<span class="kn">import</span> <span class="nn">requests</span>

<span class="kn">import</span> <span class="nn">mopidy_soundspot</span>


<span class="k">def</span> <span class="nf">get_requests_session</span><span class="p">(</span><span class="n">proxy_config</span><span class="p">,</span> <span class="n">user_agent</span><span class="p">):</span>
    <span class="n">proxy</span> <span class="o">=</span> <span class="n">httpclient</span><span class="o">.</span><span class="n">format_proxy</span><span class="p">(</span><span class="n">proxy_config</span><span class="p">)</span>
    <span class="n">full_user_agent</span> <span class="o">=</span> <span class="n">httpclient</span><span class="o">.</span><span class="n">format_user_agent</span><span class="p">(</span><span class="n">user_agent</span><span class="p">)</span>

    <span class="n">session</span> <span class="o">=</span> <span class="n">requests</span><span class="o">.</span><span class="n">Session</span><span class="p">()</span>
    <span class="n">session</span><span class="o">.</span><span class="n">proxies</span><span class="o">.</span><span class="n">update</span><span class="p">({</span><span class="s1">&#39;http&#39;</span><span class="p">:</span> <span class="n">proxy</span><span class="p">,</span> <span class="s1">&#39;https&#39;</span><span class="p">:</span> <span class="n">proxy</span><span class="p">})</span>
    <span class="n">session</span><span class="o">.</span><span class="n">headers</span><span class="o">.</span><span class="n">update</span><span class="p">({</span><span class="s1">&#39;user-agent&#39;</span><span class="p">:</span> <span class="n">full_user_agent</span><span class="p">})</span>

    <span class="k">return</span> <span class="n">session</span>


<span class="c1"># ``mopidy_config`` is the config object passed to your frontend/backend</span>
<span class="c1"># constructor</span>
<span class="n">session</span> <span class="o">=</span> <span class="n">get_requests_session</span><span class="p">(</span>
    <span class="n">proxy_config</span><span class="o">=</span><span class="n">mopidy_config</span><span class="p">[</span><span class="s1">&#39;proxy&#39;</span><span class="p">],</span>
    <span class="n">user_agent</span><span class="o">=</span><span class="s1">&#39;</span><span class="si">%s</span><span class="s1">/</span><span class="si">%s</span><span class="s1">&#39;</span> <span class="o">%</span> <span class="p">(</span>
        <span class="n">mopidy_soundspot</span><span class="o">.</span><span class="n">Extension</span><span class="o">.</span><span class="n">dist_name</span><span class="p">,</span>
        <span class="n">mopidy_soundspot</span><span class="o">.</span><span class="n">__version__</span><span class="p">))</span>

<span class="n">response</span> <span class="o">=</span> <span class="n">session</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;http://example.com&#39;</span><span class="p">)</span>
<span class="c1"># Now do something with ``response`` and/or make further requests using the</span>
<span class="c1"># ``session`` object.</span>
</pre></div>
</div>
<p>For further details, see Requests&#8217; docs on <a class="reference external" href="http://www.python-requests.org/en/latest/user/advanced/#session-objects">session objects</a>.</p>
</div>
</div>
<div class="section" id="testing-extensions">
<h2>Testing extensions<a class="headerlink" href="#testing-extensions" title="Permalink to this headline"></a></h2>
<p>Creating test cases for your extensions makes them much simpler to maintain
over the long term. It can also make it easier for you to review and accept
pull requests from other contributors knowing that they will not break the
extension in some unanticipated way.</p>
<p>Before getting started, it is important to familiarize yourself with the
Python <a class="reference external" href="https://docs.python.org/dev/library/unittest.mock.html">mock library</a>.
When it comes to running tests, Mopidy typically makes use of testing tools
like <a class="reference external" href="https://tox.readthedocs.org/en/latest/">tox</a> and
<a class="reference external" href="http://pytest.org/latest/">pytest</a>.</p>
<div class="section" id="testing-approach">
<h3>Testing approach<a class="headerlink" href="#testing-approach" title="Permalink to this headline"></a></h3>
<p>To a large extent the testing approach to follow depends on how your extension
is structured, which parts of Mopidy it interacts with, and if it uses any 3rd
party APIs or makes any HTTP requests to the outside world.</p>
<p>The sections that follow contain code extracts that highlight some of the
key areas that should be tested. For more exhaustive examples, you may want to
take a look at the test cases that ship with Mopidy itself which covers
everything from instantiating various controllers, reading configuration files,
and simulating events that your extension can listen to.</p>
<p>In general your tests should cover the extension definition, the relevant
Mopidy controllers, and the Pykka backend and / or frontend actors that form
part of the extension.</p>
</div>
<div class="section" id="testing-the-extension-definition">
<h3>Testing the extension definition<a class="headerlink" href="#testing-the-extension-definition" title="Permalink to this headline"></a></h3>
<p>Test cases for checking the definition of the extension should ensure that:</p>
<ul class="simple">
<li>the extension provides a <code class="docutils literal"><span class="pre">ext.conf</span></code> configuration file containing the
relevant parameters with their default values,</li>
<li>that the config schema is fully defined, and</li>
<li>that the extension&#8217;s actor(s) are added to the Mopidy registry on setup.</li>
</ul>
<p>An example of what these tests could look like is provided below:</p>
<div class="highlight-default"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">test_get_default_config</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
    <span class="n">ext</span> <span class="o">=</span> <span class="n">Extension</span><span class="p">()</span>
    <span class="n">config</span> <span class="o">=</span> <span class="n">ext</span><span class="o">.</span><span class="n">get_default_config</span><span class="p">()</span>

    <span class="k">assert</span> <span class="s1">&#39;[my_extension]&#39;</span> <span class="ow">in</span> <span class="n">config</span>
    <span class="k">assert</span> <span class="s1">&#39;enabled = true&#39;</span> <span class="ow">in</span> <span class="n">config</span>
    <span class="k">assert</span> <span class="s1">&#39;param_1 = value_1&#39;</span> <span class="ow">in</span> <span class="n">config</span>
    <span class="k">assert</span> <span class="s1">&#39;param_2 = value_2&#39;</span> <span class="ow">in</span> <span class="n">config</span>
    <span class="k">assert</span> <span class="s1">&#39;param_n = value_n&#39;</span> <span class="ow">in</span> <span class="n">config</span>

<span class="k">def</span> <span class="nf">test_get_config_schema</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
    <span class="n">ext</span> <span class="o">=</span> <span class="n">Extension</span><span class="p">()</span>
    <span class="n">schema</span> <span class="o">=</span> <span class="n">ext</span><span class="o">.</span><span class="n">get_config_schema</span><span class="p">()</span>

    <span class="k">assert</span> <span class="s1">&#39;enabled&#39;</span> <span class="ow">in</span> <span class="n">schema</span>
    <span class="k">assert</span> <span class="s1">&#39;param_1&#39;</span> <span class="ow">in</span> <span class="n">schema</span>
    <span class="k">assert</span> <span class="s1">&#39;param_2&#39;</span> <span class="ow">in</span> <span class="n">schema</span>
    <span class="k">assert</span> <span class="s1">&#39;param_n&#39;</span> <span class="ow">in</span> <span class="n">schema</span>

<span class="k">def</span> <span class="nf">test_setup</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
    <span class="n">registry</span> <span class="o">=</span> <span class="n">mock</span><span class="o">.</span><span class="n">Mock</span><span class="p">()</span>

    <span class="n">ext</span> <span class="o">=</span> <span class="n">Extension</span><span class="p">()</span>
    <span class="n">ext</span><span class="o">.</span><span class="n">setup</span><span class="p">(</span><span class="n">registry</span><span class="p">)</span>
    <span class="n">calls</span> <span class="o">=</span> <span class="p">[</span><span class="n">mock</span><span class="o">.</span><span class="n">call</span><span class="p">(</span><span class="s1">&#39;frontend&#39;</span><span class="p">,</span> <span class="n">frontend_lib</span><span class="o">.</span><span class="n">MyFrontend</span><span class="p">),</span>
             <span class="n">mock</span><span class="o">.</span><span class="n">call</span><span class="p">(</span><span class="s1">&#39;backend&#39;</span><span class="p">,</span>  <span class="n">backend_lib</span><span class="o">.</span><span class="n">MyBackend</span><span class="p">)]</span>
    <span class="n">registry</span><span class="o">.</span><span class="n">add</span><span class="o">.</span><span class="n">assert_has_calls</span><span class="p">(</span><span class="n">calls</span><span class="p">,</span> <span class="n">any_order</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</pre></div>
</div>
</div>
<div class="section" id="testing-backend-actors">
<h3>Testing backend actors<a class="headerlink" href="#testing-backend-actors" title="Permalink to this headline"></a></h3>
<p>Backends can usually be constructed with a small mockup of the configuration
file, and mocking the audio actor:</p>
<div class="highlight-default"><div class="highlight"><pre><span></span><span class="nd">@pytest</span><span class="o">.</span><span class="n">fixture</span>
<span class="k">def</span> <span class="nf">config</span><span class="p">():</span>
    <span class="k">return</span> <span class="p">{</span>
        <span class="s1">&#39;http&#39;</span><span class="p">:</span> <span class="p">{</span>
            <span class="s1">&#39;hostname&#39;</span><span class="p">:</span> <span class="s1">&#39;127.0.0.1&#39;</span><span class="p">,</span>
            <span class="s1">&#39;port&#39;</span><span class="p">:</span> <span class="s1">&#39;6680&#39;</span>
        <span class="p">},</span>
        <span class="s1">&#39;proxy&#39;</span><span class="p">:</span> <span class="p">{</span>
            <span class="s1">&#39;hostname&#39;</span><span class="p">:</span> <span class="s1">&#39;host_mock&#39;</span><span class="p">,</span>
            <span class="s1">&#39;port&#39;</span><span class="p">:</span> <span class="s1">&#39;port_mock&#39;</span>
        <span class="p">},</span>
        <span class="s1">&#39;my_extension&#39;</span><span class="p">:</span> <span class="p">{</span>
            <span class="s1">&#39;enabled&#39;</span><span class="p">:</span> <span class="kc">True</span><span class="p">,</span>
            <span class="s1">&#39;param_1&#39;</span><span class="p">:</span> <span class="s1">&#39;value_1&#39;</span><span class="p">,</span>
            <span class="s1">&#39;param_2&#39;</span><span class="p">:</span> <span class="s1">&#39;value_2&#39;</span><span class="p">,</span>
            <span class="s1">&#39;param_n&#39;</span><span class="p">:</span> <span class="s1">&#39;value_n&#39;</span><span class="p">,</span>
        <span class="p">}</span>
    <span class="p">}</span>

<span class="k">def</span> <span class="nf">get_backend</span><span class="p">(</span><span class="n">config</span><span class="p">):</span>
    <span class="k">return</span> <span class="n">backend</span><span class="o">.</span><span class="n">MyBackend</span><span class="p">(</span><span class="n">config</span><span class="o">=</span><span class="n">config</span><span class="p">,</span> <span class="n">audio</span><span class="o">=</span><span class="n">mock</span><span class="o">.</span><span class="n">Mock</span><span class="p">())</span>
</pre></div>
</div>
<p>The following libraries might be useful for mocking any HTTP requests that
your extension makes:</p>
<ul class="simple">
<li><a class="reference external" href="https://pypi.python.org/pypi/responses">responses</a> - A utility library for
mocking out the requests Python library.</li>
<li><a class="reference external" href="https://pypi.python.org/pypi/vcrpy">vcrpy</a> - Automatically mock your HTTP
interactions to simplify and speed up testing.</li>
</ul>
<p>At the very least, you&#8217;ll probably want to patch <code class="docutils literal"><span class="pre">requests</span></code> or any other web
API&#8217;s that you use to avoid any unintended HTTP requests from being made by
your backend during testing:</p>
<div class="highlight-default"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">mock</span> <span class="k">import</span> <span class="n">patch</span>
<span class="nd">@mock</span><span class="o">.</span><span class="n">patch</span><span class="p">(</span><span class="s1">&#39;requests.get&#39;</span><span class="p">,</span>
            <span class="n">mock</span><span class="o">.</span><span class="n">Mock</span><span class="p">(</span><span class="n">side_effect</span><span class="o">=</span><span class="ne">Exception</span><span class="p">(</span><span class="s1">&#39;Intercepted unintended HTTP call&#39;</span><span class="p">)))</span>
</pre></div>
</div>
<p>Backend tests should also ensure that:</p>
<ul class="simple">
<li>the backend provides a unique URI scheme,</li>
<li>that it sets up the various providers (e.g. library, playback, etc.)</li>
</ul>
<div class="highlight-default"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">test_uri_schemes</span><span class="p">(</span><span class="n">config</span><span class="p">):</span>
    <span class="n">backend</span> <span class="o">=</span> <span class="n">get_backend</span><span class="p">(</span><span class="n">config</span><span class="p">)</span>

    <span class="k">assert</span> <span class="s1">&#39;my_scheme&#39;</span> <span class="ow">in</span> <span class="n">backend</span><span class="o">.</span><span class="n">uri_schemes</span>


<span class="k">def</span> <span class="nf">test_init_sets_up_the_providers</span><span class="p">(</span><span class="n">config</span><span class="p">):</span>
    <span class="n">backend</span> <span class="o">=</span> <span class="n">get_backend</span><span class="p">(</span><span class="n">config</span><span class="p">)</span>

    <span class="k">assert</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">backend</span><span class="o">.</span><span class="n">library</span><span class="p">,</span> <span class="n">library</span><span class="o">.</span><span class="n">MyLibraryProvider</span><span class="p">)</span>
    <span class="k">assert</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">backend</span><span class="o">.</span><span class="n">playback</span><span class="p">,</span> <span class="n">playback</span><span class="o">.</span><span class="n">MyPlaybackProvider</span><span class="p">)</span>
</pre></div>
</div>
<p>Once you have a backend instance to work with, testing the various playback,
library, and other providers is straight forward and should not require any
special setup or processing.</p>
</div>
<div class="section" id="testing-libraries">
<h3>Testing libraries<a class="headerlink" href="#testing-libraries" title="Permalink to this headline"></a></h3>
<p>Library test cases should cover the implementations of the standard Mopidy
API (e.g. <code class="docutils literal"><span class="pre">browse</span></code>, <code class="docutils literal"><span class="pre">lookup</span></code>, <code class="docutils literal"><span class="pre">refresh</span></code>, <code class="docutils literal"><span class="pre">get_images</span></code>, <code class="docutils literal"><span class="pre">search</span></code>,
etc.)</p>
</div>
<div class="section" id="testing-playback-controllers">
<h3>Testing playback controllers<a class="headerlink" href="#testing-playback-controllers" title="Permalink to this headline"></a></h3>
<p>Testing <code class="docutils literal"><span class="pre">change_track</span></code> and <code class="docutils literal"><span class="pre">translate_uri</span></code> is probably the highest
priority, since these methods are used to prepare the track and provide its
audio URL to Mopidy&#8217;s core for playback.</p>
</div>
<div class="section" id="testing-frontends">
<h3>Testing frontends<a class="headerlink" href="#testing-frontends" title="Permalink to this headline"></a></h3>
<p>Because most frontends will interact with the Mopidy core, it will most likely
be necessary to have a full core running for testing purposes:</p>
<div class="highlight-default"><div class="highlight"><pre><span></span><span class="bp">self</span><span class="o">.</span><span class="n">core</span> <span class="o">=</span> <span class="n">core</span><span class="o">.</span><span class="n">Core</span><span class="o">.</span><span class="n">start</span><span class="p">(</span>
            <span class="n">config</span><span class="p">,</span> <span class="n">backends</span><span class="o">=</span><span class="p">[</span><span class="n">get_backend</span><span class="p">(</span><span class="n">config</span><span class="p">)])</span><span class="o">.</span><span class="n">proxy</span><span class="p">()</span>
</pre></div>
</div>
<p>It may be advisable to take a quick look at the
<a class="reference external" href="https://www.pykka.org/en/latest/">Pykka API</a> at this point to make sure that
you are familiar with <code class="docutils literal"><span class="pre">ThreadingActor</span></code>, <code class="docutils literal"><span class="pre">ThreadingFuture</span></code>, and the
<code class="docutils literal"><span class="pre">proxies</span></code> that allow you to access the attributes and methods of the actor
directly.</p>
<p>You&#8217;ll also need a list of <a class="reference internal" href="api/models.html#mopidy.models.Track" title="mopidy.models.Track"><code class="xref py py-class docutils literal"><span class="pre">Track</span></code></a> and a list of URIs in
order to populate the core with some simple tracks that can be used for
testing:</p>
<div class="highlight-default"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">BaseTest</span><span class="p">(</span><span class="n">unittest</span><span class="o">.</span><span class="n">TestCase</span><span class="p">):</span>
    <span class="n">tracks</span> <span class="o">=</span> <span class="p">[</span>
        <span class="n">models</span><span class="o">.</span><span class="n">Track</span><span class="p">(</span><span class="n">uri</span><span class="o">=</span><span class="s1">&#39;my_scheme:track:id1&#39;</span><span class="p">,</span> <span class="n">length</span><span class="o">=</span><span class="mi">40000</span><span class="p">),</span>  <span class="c1"># Regular track</span>
        <span class="n">models</span><span class="o">.</span><span class="n">Track</span><span class="p">(</span><span class="n">uri</span><span class="o">=</span><span class="s1">&#39;my_scheme:track:id2&#39;</span><span class="p">,</span> <span class="n">length</span><span class="o">=</span><span class="kc">None</span><span class="p">),</span>   <span class="c1"># No duration</span>
    <span class="p">]</span>

<span class="n">uris</span> <span class="o">=</span> <span class="p">[</span> <span class="s1">&#39;my_scheme:track:id1&#39;</span><span class="p">,</span> <span class="s1">&#39;my_scheme:track:id2&#39;</span><span class="p">]</span>
</pre></div>
</div>
<p>In the <code class="docutils literal"><span class="pre">setup()</span></code> method of your test class, you will then probably need to
monkey patch looking up tracks in the library (so that it will always use the
lists that you defined), and then populate the core&#8217;s tracklist:</p>
<div class="highlight-default"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">lookup</span><span class="p">(</span><span class="n">uris</span><span class="p">):</span>
    <span class="n">result</span> <span class="o">=</span> <span class="p">{</span><span class="n">uri</span><span class="p">:</span> <span class="p">[]</span> <span class="k">for</span> <span class="n">uri</span> <span class="ow">in</span> <span class="n">uris</span><span class="p">}</span>
    <span class="k">for</span> <span class="n">track</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">tracks</span><span class="p">:</span>
        <span class="k">if</span> <span class="n">track</span><span class="o">.</span><span class="n">uri</span> <span class="ow">in</span> <span class="n">result</span><span class="p">:</span>
            <span class="n">result</span><span class="p">[</span><span class="n">track</span><span class="o">.</span><span class="n">uri</span><span class="p">]</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">track</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">result</span>

<span class="bp">self</span><span class="o">.</span><span class="n">core</span><span class="o">.</span><span class="n">library</span><span class="o">.</span><span class="n">lookup</span> <span class="o">=</span> <span class="n">lookup</span>
<span class="bp">self</span><span class="o">.</span><span class="n">tl_tracks</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">core</span><span class="o">.</span><span class="n">tracklist</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">uris</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">uris</span><span class="p">)</span><span class="o">.</span><span class="n">get</span><span class="p">()</span>
</pre></div>
</div>
<p>With all of that done you should finally be ready to instantiate your frontend:</p>
<div class="highlight-default"><div class="highlight"><pre><span></span><span class="bp">self</span><span class="o">.</span><span class="n">frontend</span> <span class="o">=</span> <span class="n">frontend</span><span class="o">.</span><span class="n">MyFrontend</span><span class="o">.</span><span class="n">start</span><span class="p">(</span><span class="n">config</span><span class="p">(),</span> <span class="bp">self</span><span class="o">.</span><span class="n">core</span><span class="p">)</span><span class="o">.</span><span class="n">proxy</span><span class="p">()</span>
</pre></div>
</div>
<p>Keep in mind that the normal core and frontend methods will usually return
<code class="docutils literal"><span class="pre">pykka.ThreadingFuture</span></code> objects, so you will need to add <code class="docutils literal"><span class="pre">.get()</span></code> at
the end of most method calls in order to get to the actual return values.</p>
</div>
<div class="section" id="triggering-events">
<h3>Triggering events<a class="headerlink" href="#triggering-events" title="Permalink to this headline"></a></h3>
<p>There may be test case scenarios that require simulating certain event triggers
that your extension&#8217;s actors can listen for and respond on. An example for
patching the listener to store these events, and then play them back for your
actor, may look something like this:</p>
<div class="highlight-default"><div class="highlight"><pre><span></span><span class="bp">self</span><span class="o">.</span><span class="n">events</span> <span class="o">=</span> <span class="p">[]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">patcher</span> <span class="o">=</span> <span class="n">mock</span><span class="o">.</span><span class="n">patch</span><span class="p">(</span><span class="s1">&#39;mopidy.listener.send&#39;</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">send_mock</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">patcher</span><span class="o">.</span><span class="n">start</span><span class="p">()</span>

<span class="k">def</span> <span class="nf">send</span><span class="p">(</span><span class="n">cls</span><span class="p">,</span> <span class="n">event</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
    <span class="bp">self</span><span class="o">.</span><span class="n">events</span><span class="o">.</span><span class="n">append</span><span class="p">((</span><span class="n">event</span><span class="p">,</span> <span class="n">kwargs</span><span class="p">))</span>

<span class="bp">self</span><span class="o">.</span><span class="n">send_mock</span><span class="o">.</span><span class="n">side_effect</span> <span class="o">=</span> <span class="n">send</span>
</pre></div>
</div>
<p>Once all of the events have been captured, a method like
<code class="docutils literal"><span class="pre">replay_events()</span></code> can be called at the relevant points in the code to have
the events fire:</p>
<div class="highlight-default"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">replay_events</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">my_actor</span><span class="p">,</span> <span class="n">until</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
    <span class="k">while</span> <span class="bp">self</span><span class="o">.</span><span class="n">events</span><span class="p">:</span>
        <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">events</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="n">until</span><span class="p">:</span>
            <span class="k">break</span>
        <span class="n">event</span><span class="p">,</span> <span class="n">kwargs</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">events</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
        <span class="n">frontend</span><span class="o">.</span><span class="n">on_event</span><span class="p">(</span><span class="n">event</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span><span class="o">.</span><span class="n">get</span><span class="p">()</span>
</pre></div>
</div>
<p>For further details and examples, refer to the
<a class="reference external" href="https://github.com/mopidy/mopidy/tree/develop/tests">/tests</a>
directory on the Mopidy development branch.</p>
</div>
</div>
</div>


          </div>
        </div>
      </div>
      <div class="sphinxsidebar" role="navigation" aria-label="main navigation">
        <div class="sphinxsidebarwrapper">
  <h3><a href="index.html">Table Of Contents</a></h3>
  <ul>
<li><a class="reference internal" href="#">Extension development</a><ul>
<li><a class="reference internal" href="#anatomy-of-an-extension">Anatomy of an extension</a></li>
<li><a class="reference internal" href="#cookiecutter-project-template">cookiecutter project template</a></li>
<li><a class="reference internal" href="#example-readme-rst">Example README.rst</a></li>
<li><a class="reference internal" href="#example-setup-py">Example setup.py</a></li>
<li><a class="reference internal" href="#example-init-py">Example __init__.py</a></li>
<li><a class="reference internal" href="#example-frontend">Example frontend</a></li>
<li><a class="reference internal" href="#example-backend">Example backend</a></li>
<li><a class="reference internal" href="#example-command">Example command</a></li>
<li><a class="reference internal" href="#example-web-application">Example web application</a></li>
<li><a class="reference internal" href="#running-an-extension">Running an extension</a></li>
<li><a class="reference internal" href="#python-conventions">Python conventions</a></li>
<li><a class="reference internal" href="#use-of-mopidy-apis">Use of Mopidy APIs</a></li>
<li><a class="reference internal" href="#logging-in-extensions">Logging in extensions</a></li>
<li><a class="reference internal" href="#making-http-requests-from-extensions">Making HTTP requests from extensions</a><ul>
<li><a class="reference internal" href="#proxies">Proxies</a></li>
<li><a class="reference internal" href="#user-agent-strings">User-Agent strings</a></li>
<li><a class="reference internal" href="#example-using-requests-sessions">Example using Requests sessions</a></li>
</ul>
</li>
<li><a class="reference internal" href="#testing-extensions">Testing extensions</a><ul>
<li><a class="reference internal" href="#testing-approach">Testing approach</a></li>
<li><a class="reference internal" href="#testing-the-extension-definition">Testing the extension definition</a></li>
<li><a class="reference internal" href="#testing-backend-actors">Testing backend actors</a></li>
<li><a class="reference internal" href="#testing-libraries">Testing libraries</a></li>
<li><a class="reference internal" href="#testing-playback-controllers">Testing playback controllers</a></li>
<li><a class="reference internal" href="#testing-frontends">Testing frontends</a></li>
<li><a class="reference internal" href="#triggering-events">Triggering events</a></li>
</ul>
</li>
</ul>
</li>
</ul>
<div class="relations">
<h3>Related Topics</h3>
<ul>
  <li><a href="index.html">Documentation overview</a><ul>
      <li>Previous: <a href="codestyle.html" title="previous chapter">Code style</a></li>
      <li>Next: <a href="glossary.html" title="next chapter">Glossary</a></li>
  </ul></li>
</ul>
</div>
  <div role="note" aria-label="source link">
    <h3>This Page</h3>
    <ul class="this-page-menu">
      <li><a href="_sources/extensiondev.txt"
            rel="nofollow">Show Source</a></li>
    </ul>
   </div>
<div id="searchbox" style="display: none" role="search">
  <h3>Quick search</h3>
    <form class="search" action="search.html" method="get">
      <div><input type="text" name="q" /></div>
      <div><input type="submit" value="Go" /></div>
      <input type="hidden" name="check_keywords" value="yes" />
      <input type="hidden" name="area" value="default" />
    </form>
</div>
<script type="text/javascript">$('#searchbox').show(0);</script>
        </div>
      </div>
      <div class="clearer"></div>
    </div>
    <div class="footer">
      &copy;2009-2017, Stein Magnus Jodal and contributors.
      
      |
      Powered by <a href="http://sphinx-doc.org/">Sphinx 1.4.9</a>
      &amp; <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.8</a>
      
      |
      <a href="_sources/extensiondev.txt"
          rel="nofollow">Page source</a>
    </div>

    

    
  </body>
</html>