<html><head><meta http-equiv="Content-Type" content="text/html charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div class="">Hi Bruno,</div><div class=""><br class=""></div><div class="">thanks for the quick and helpful reply!</div><div class=""><br class=""></div><br class=""><div><blockquote type="cite" class=""><div class="">On 7 Dec 2016, at 22:23, Bruno Oliveira &lt;<a href="mailto:nicoddemus@gmail.com" class="">nicoddemus@gmail.com</a>&gt; wrote:</div><br class="Apple-interchange-newline"><div class=""><div dir="ltr" class=""><div class="markdown-here-wrapper" style=""><p style="margin:1.2em 0px!important" class="">Hi Christoph,</p><p style="margin:1.2em 0px!important" class="">On Wed, Dec 7, 2016 at 11:51 AM Christoph Deil <a href="http://mailto:deil.christoph@googlemail.com/" class="">deil.christoph@googlemail.com</a> wrote:</p><div style="margin: 1.2em 0px !important;" class=""><br class="webkit-block-placeholder"></div><div class="markdown-here-exclude"><div class=""><br class="webkit-block-placeholder"></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div style="word-wrap:break-word" class="gmail_msg"><div class="gmail_msg">Are there any real pros and cons for the two options? Or are both equally powerful and it’s a matter of taste?<br class=""></div></div></blockquote><div class=""><br class="webkit-block-placeholder"></div></div><div style="margin: 1.2em 0px !important;" class=""><br class="webkit-block-placeholder"></div><p style="margin:1.2em 0px!important" class="">IMHO it seems to be a matter of taste. The <code style="font-size:0.85em;font-family:Consolas,Inconsolata,Courier,monospace;margin:0px 0.15em;padding:0px 0.3em;white-space:pre-wrap;border:1px solid rgb(234,234,234);background-color:rgb(248,248,248);border-radius:3px;display:inline" class="">requires_dependency</code> function still requires you to explicitly import the module inside the test file to obtain the module object:</p>
<pre style="font-size:0.85em;font-family:Consolas,Inconsolata,Courier,monospace;font-size:1em;line-height:1.2em;margin:1.2em 0px" class=""><code class="language-python hljs" style="font-size:0.85em;font-family:Consolas,Inconsolata,Courier,monospace;margin:0px 0.15em;padding:0px 0.3em;white-space:pre-wrap;border:1px solid rgb(234,234,234);background-color:rgb(248,248,248);border-radius:3px;display:inline;white-space:pre;overflow:auto;border-radius:3px;border:1px solid rgb(204,204,204);padding:0.5em 0.7em;display:block!important;display:block;overflow-x:auto;padding:0.5em;background:rgb(35,36,31);color:rgb(248,248,242)"><span class="hljs-keyword" style="color:rgb(249,38,114)">from</span> gammapy.utils.testing <span class="hljs-keyword" style="color:rgb(249,38,114)">import</span> requires_dependency

<span class="hljs-decorator" style="color:rgb(117,113,94)">@requires_dependency('scipy')</span>
<span class="hljs-function" style="color:rgb(249,38,114)"><span class="hljs-keyword" style="color:rgb(249,38,114);color:rgb(102,217,239)">def</span> <span class="hljs-title" style="color:rgb(166,226,46)">test_using_scipy</span><span class="hljs-params" style="color:rgb(248,248,242)">()</span>:</span>
    <span class="hljs-keyword" style="color:rgb(249,38,114)">import</span> scipy
</code></pre><p style="margin:1.2em 0px!important" class="">Compared with <code style="font-size:0.85em;font-family:Consolas,Inconsolata,Courier,monospace;margin:0px 0.15em;padding:0px 0.3em;white-space:pre-wrap;border:1px solid rgb(234,234,234);background-color:rgb(248,248,248);border-radius:3px;display:inline" class="">pytest.importorskip</code>:</p>
<pre style="font-size:0.85em;font-family:Consolas,Inconsolata,Courier,monospace;font-size:1em;line-height:1.2em;margin:1.2em 0px" class=""><code class="language-python hljs" style="font-size:0.85em;font-family:Consolas,Inconsolata,Courier,monospace;margin:0px 0.15em;padding:0px 0.3em;white-space:pre-wrap;border:1px solid rgb(234,234,234);background-color:rgb(248,248,248);border-radius:3px;display:inline;white-space:pre;overflow:auto;border-radius:3px;border:1px solid rgb(204,204,204);padding:0.5em 0.7em;display:block!important;display:block;overflow-x:auto;padding:0.5em;background:rgb(35,36,31);color:rgb(248,248,242)"><span class="hljs-keyword" style="color:rgb(249,38,114)">import</span> pytest

<span class="hljs-function" style="color:rgb(249,38,114)"><span class="hljs-keyword" style="color:rgb(249,38,114);color:rgb(102,217,239)">def</span> <span class="hljs-title" style="color:rgb(166,226,46)">test_using_scipy</span><span class="hljs-params" style="color:rgb(248,248,242)">()</span>:</span>
    scipy = pytest.importorskip(<span class="hljs-string" style="color:rgb(230,219,116)">'scipy'</span>)
</code></pre><p style="margin:1.2em 0px!important" class="">So I think <code style="font-size:0.85em;font-family:Consolas,Inconsolata,Courier,monospace;margin:0px 0.15em;padding:0px 0.3em;white-space:pre-wrap;border:1px solid rgb(234,234,234);background-color:rgb(248,248,248);border-radius:3px;display:inline" class="">requires_dependency</code> has the following cons:</p>
<ul style="margin:1.2em 0px;padding-left:2em" class="">
<li style="margin:0.5em 0px" class="">It might be more explicit, since <code style="font-size:0.85em;font-family:Consolas,Inconsolata,Courier,monospace;margin:0px 0.15em;padding:0px 0.3em;white-space:pre-wrap;border:1px solid rgb(234,234,234);background-color:rgb(248,248,248);border-radius:3px;display:inline" class="">pytest.importorskip</code> might be used deep inside the test function and easy to miss, while the decorator stands out;</li>
<li style="margin:0.5em 0px" class="">It is more friendly to IDEs like PyCharm/PyDev, with code-completion, documentation, etc.</li>
</ul><p style="margin:1.2em 0px!important" class="">The only cons I can think of is that it requires one extra line and the extra import (since it is common for tests to import <code style="font-size:0.85em;font-family:Consolas,Inconsolata,Courier,monospace;margin:0px 0.15em;padding:0px 0.3em;white-space:pre-wrap;border:1px solid rgb(234,234,234);background-color:rgb(248,248,248);border-radius:3px;display:inline" class="">pytest</code> for other reasons as well).</p><div class=""><br class=""></div></div></div></div></blockquote><div><br class=""></div><div>Just to clarify: the way we handle optional dependencies in the (non-test) code is to put them as delayed imports only inside the function or method where they are used.</div><div><br class=""></div><div>E.g. we have classes that have much functionality for computation, and then one plot method and only that requires “import matplotlib”, because that import is in the plot method.</div><div><br class=""></div>So the extra “import scipy” line in the test code example you give above is never there in our tests.<br class=""><blockquote type="cite" class=""><div class=""><div dir="ltr" class=""><div class="markdown-here-wrapper" style=""><div style="margin: 1.2em 0px !important;" class=""><br class="webkit-block-placeholder"></div><div class="markdown-here-exclude"><div class=""><br class="webkit-block-placeholder"></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div style="word-wrap:break-word" class="gmail_msg"><div class="gmail_msg">Do you think such a decorator would be appropriate for a pytest plugin?<br class=""></div></div></blockquote><div class=""><br class="webkit-block-placeholder"></div></div><div style="margin: 1.2em 0px !important;" class=""><br class="webkit-block-placeholder"></div><p style="margin:1.2em 0px!important" class="">If others find it useful, I don’t see why not. :)</p><div style="margin: 1.2em 0px !important;" class=""><br class="webkit-block-placeholder"></div><div class="markdown-here-exclude"><div class=""><br class="webkit-block-placeholder"></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div style="word-wrap:break-word" class="gmail_msg"><div class="gmail_msg"></div><div class="gmail_msg">Or even to be proposed as a convenience for pytest core?</div></div></blockquote><div class=""><br class="webkit-block-placeholder"></div></div><div style="margin: 1.2em 0px !important;" class=""><br class="webkit-block-placeholder"></div><p style="margin:1.2em 0px!important" class="">I personally would be -0 on this because I feel it seems too similar to using <code style="font-size:0.85em;font-family:Consolas,Inconsolata,Courier,monospace;margin:0px 0.15em;padding:0px 0.3em;white-space:pre-wrap;border:1px solid rgb(234,234,234);background-color:rgb(248,248,248);border-radius:3px;display:inline" class="">pytest.importorskip</code> method so I would avoid having two ways of doing the same thing. On the other hand, the implementation would be really simple so it wouldn’t be a maintenance burden if others really like it.</p><div class=""><br class=""></div></div></div></div></blockquote><div><br class=""></div><div>Makes sense.</div><blockquote type="cite" class=""><div class=""><div dir="ltr" class=""><div class="markdown-here-wrapper" style=""><div style="margin: 1.2em 0px !important;" class=""><br class="webkit-block-placeholder"></div><div class="markdown-here-exclude"><div class=""><br class="webkit-block-placeholder"></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div style="word-wrap:break-word" class="gmail_msg"><div class="gmail_msg">Or would you just recommend we implement what we want in our own `utils.py` instead of trying to generalise a solution for this?</div></div></blockquote><div class=""><br class="webkit-block-placeholder"></div></div><div style="margin: 1.2em 0px !important;" class=""><br class="webkit-block-placeholder"></div><p style="margin:1.2em 0px!important" class="">I think it is really commendable of you to try to provide a general solution to the community, so please don’t get discouraged by my <code style="font-size:0.85em;font-family:Consolas,Inconsolata,Courier,monospace;margin:0px 0.15em;padding:0px 0.3em;white-space:pre-wrap;border:1px solid rgb(234,234,234);background-color:rgb(248,248,248);border-radius:3px;display:inline" class="">-0</code>. And since the implementation is simple and <a href="https://github.com/pytest-dev/cookiecutter-pytest-plugin" class="">cookiecutter-pytest-plugin</a> makes creating plugins a snap, you should definitely go with it if you really prefer the decoration method.</p><div class=""><br class=""></div></div></div></div></blockquote><div><br class=""></div><div>I quickly looked through&nbsp;<a href="http://plugincompat.herokuapp.com/" class="">http://plugincompat.herokuapp.com/</a>&nbsp;and couldn’t find a plugin that does something like this, i.e. provide a decorator that can be used like this:</div><div>@requires_dependency(‘scipy”)</div><div>and maybe (to be discussed) a little more fancy strings and multiple checks like</div><div>@requires_dependency(‘scipy&gt;=0.15, matplotlib’)</div><div><br class=""></div><div>I’ll check out the pytest cookiecutter.</div><div><br class=""></div><div>One basic question I still have is what the advantage is of having this as a Pytest plugin, instead of just a single-file .py module from which people can import the requires_dependency decorator? Would this allow me to expose the decorator in the top-level pytest or some other namespace? Is this a good idea?</div><div><br class=""></div><div><div>Are there one or a few examples of plugins that I could look at before getting started that are similar to what I’m trying to do?</div><div><br class=""></div><div>Cheers, Christoph</div><div class=""><br class=""></div></div></div><div><blockquote type="cite" class=""><div class=""><div dir="ltr" class=""><div class="markdown-here-wrapper" style=""><p style="margin:1.2em 0px!important" class="">Cheers,<br class="">Bruno.</p>
<div title="MDH:SGkgQ2hyaXN0b3BoLDxkaXY+PGJyPjwvZGl2PjxkaXY+PGJyPjwvZGl2PjxkaXYgY2xhc3M9Imdt
YWlsX3F1b3RlIj48ZGl2IGRpcj0ibHRyIj5PbiBXZWQsIERlYyA3LCAyMDE2IGF0IDExOjUxIEFN
IENocmlzdG9waCBEZWlsICZsdDtkZWlsLmNocmlzdG9waEBnb29nbGVtYWlsLmNvbSZndDsgd3Jv
dGU6PGJyPjwvZGl2PjxibG9ja3F1b3RlIGNsYXNzPSJnbWFpbF9xdW90ZSIgc3R5bGU9Im1hcmdp
bjowIDAgMCAuOGV4O2JvcmRlci1sZWZ0OjFweCAjY2NjIHNvbGlkO3BhZGRpbmctbGVmdDoxZXg7
Ij48ZGl2IHN0eWxlPSJ3b3JkLXdyYXA6YnJlYWstd29yZCIgY2xhc3M9ImdtYWlsX21zZyI+PGRp
diBjbGFzcz0iZ21haWxfbXNnIj5BcmUgdGhlcmUgYW55IHJlYWwgcHJvcyBhbmQgY29ucyBmb3Ig
dGhlIHR3byBvcHRpb25zPyBPciBhcmUgYm90aCBlcXVhbGx5IHBvd2VyZnVsIGFuZCBpdOKAmXMg
YSBtYXR0ZXIgb2YgdGFzdGU/PGJyPjwvZGl2PjwvZGl2PjwvYmxvY2txdW90ZT48ZGl2Pjxicj48
L2Rpdj48ZGl2PklNSE8gaXQgc2VlbXMgdG8gYmUgYSBtYXR0ZXIgb2YgdGFzdGUuIFRoZSBgcmVx
dWlyZXNfZGVwZW5kZW5jeWAgZnVuY3Rpb24gc3RpbGwgcmVxdWlyZXMgeW91IHRvIGV4cGxpY2l0
bHkgaW1wb3J0IHRoZSBtb2R1bGUgaW5zaWRlIHRoZSB0ZXN0IGZpbGUgdG8gb2J0YWluIHRoZSBt
b2R1bGUgb2JqZWN0OjwvZGl2PjxkaXY+PGJyPjwvZGl2PjxkaXY+YGBgcHl0aG9uPC9kaXY+PGRp
dj48ZGl2PmZyb20gZ2FtbWFweS51dGlscy50ZXN0aW5nIGltcG9ydCByZXF1aXJlc19kZXBlbmRl
bmN5PC9kaXY+PGRpdj48YnI+PC9kaXY+PGRpdj5AcmVxdWlyZXNfZGVwZW5kZW5jeSgnc2NpcHkn
KTwvZGl2PjxkaXY+ZGVmIHRlc3RfdXNpbmdfc2NpcHkoKTo8L2Rpdj48ZGl2PiZuYnNwOyAmbmJz
cDsgaW1wb3J0IHNjaXB5PC9kaXY+PC9kaXY+PGRpdj5gYGA8L2Rpdj48ZGl2Pjxicj48L2Rpdj48
ZGl2PkNvbXBhcmVkIHdpdGggYHB5dGVzdC5pbXBvcnRvcnNraXBgOjwvZGl2PjxkaXY+PGJyPjwv
ZGl2PjxkaXY+YGBgcHl0aG9uPC9kaXY+PGRpdj48ZGl2PmltcG9ydCBweXRlc3Q8L2Rpdj48ZGl2
Pjxicj48L2Rpdj48ZGl2PmRlZiB0ZXN0X3VzaW5nX3NjaXB5KCk6PC9kaXY+PGRpdj4mbmJzcDsg
Jm5ic3A7IHNjaXB5ID0gcHl0ZXN0LmltcG9ydG9yc2tpcCgnc2NpcHknKTwvZGl2PjwvZGl2Pjxk
aXY+YGBgPC9kaXY+PGRpdj48YnI+PC9kaXY+PGRpdj5TbyBJIHRoaW5rIGByZXF1aXJlc19kZXBl
bmRlbmN5YCBoYXMgdGhlIGZvbGxvd2luZyBjb25zOjwvZGl2PjxkaXY+PGJyPjwvZGl2PjxkaXY+
KiBJdCBtaWdodCBiZSBtb3JlIGV4cGxpY2l0LCBzaW5jZSBgcHl0ZXN0LmltcG9ydG9yc2tpcGAg
bWlnaHQgYmUgdXNlZCBkZWVwIGluc2lkZSB0aGUgdGVzdCBmdW5jdGlvbiBhbmQgZWFzeSB0byBt
aXNzLCB3aGlsZSB0aGUgZGVjb3JhdG9yIHN0YW5kcyBvdXQ7PC9kaXY+PGRpdj4qIEl0IGlzIG1v
cmUgZnJpZW5kbHkgdG8gSURFcyBsaWtlIFB5Q2hhcm0vUHlEZXYsIHdpdGggY29kZS1jb21wbGV0
aW9uLCBkb2N1bWVudGF0aW9uLCBldGMuPC9kaXY+PGRpdj48YnI+PC9kaXY+PGRpdj5UaGUgb25s
eSBjb25zIEkgY2FuIHRoaW5rIG9mIGlzIHRoYXQgaXQgcmVxdWlyZXMgb25lIGV4dHJhIGxpbmUg
YW5kIHRoZSBleHRyYSBpbXBvcnQgKHNpbmNlIGl0IGlzIGNvbW1vbiBmb3IgdGVzdHMgdG8gaW1w
b3J0IGBweXRlc3RgIGZvciBvdGhlciByZWFzb25zIGFzIHdlbGwpLjwvZGl2PjxkaXY+PGJyPjwv
ZGl2PjxibG9ja3F1b3RlIGNsYXNzPSJnbWFpbF9xdW90ZSIgc3R5bGU9Im1hcmdpbjowIDAgMCAu
OGV4O2JvcmRlci1sZWZ0OjFweCAjY2NjIHNvbGlkO3BhZGRpbmctbGVmdDoxZXg7Ij48ZGl2IHN0
eWxlPSJ3b3JkLXdyYXA6YnJlYWstd29yZCIgY2xhc3M9ImdtYWlsX21zZyI+PGRpdiBjbGFzcz0i
Z21haWxfbXNnIj5EbyB5b3UgdGhpbmsgc3VjaCBhIGRlY29yYXRvciB3b3VsZCBiZSBhcHByb3By
aWF0ZSBmb3IgYSBweXRlc3QgcGx1Z2luPzxicj48L2Rpdj48L2Rpdj48L2Jsb2NrcXVvdGU+PGRp
dj48YnI+PC9kaXY+PGRpdj5JZiBvdGhlcnMgZmluZCBpdCB1c2VmdWwsIEkgZG9uJ3Qgc2VlIHdo
eSBub3QuIDopPC9kaXY+PGRpdj4mbmJzcDs8L2Rpdj48YmxvY2txdW90ZSBjbGFzcz0iZ21haWxf
cXVvdGUiIHN0eWxlPSJtYXJnaW46MCAwIDAgLjhleDtib3JkZXItbGVmdDoxcHggI2NjYyBzb2xp
ZDtwYWRkaW5nLWxlZnQ6MWV4OyI+PGRpdiBzdHlsZT0id29yZC13cmFwOmJyZWFrLXdvcmQiIGNs
YXNzPSJnbWFpbF9tc2ciPjxkaXYgY2xhc3M9ImdtYWlsX21zZyI+PC9kaXY+PGRpdiBjbGFzcz0i
Z21haWxfbXNnIj5PciBldmVuIHRvIGJlIHByb3Bvc2VkIGFzIGEgY29udmVuaWVuY2UgZm9yIHB5
dGVzdCBjb3JlPzwvZGl2PjwvZGl2PjwvYmxvY2txdW90ZT48ZGl2Pjxicj48L2Rpdj48ZGl2Pkkg
cGVyc29uYWxseSB3b3VsZCBiZSAtMCBvbiB0aGlzIGJlY2F1c2UgSSBmZWVsIGl0IHNlZW1zIHRv
byBzaW1pbGFyIHRvIHVzaW5nIGBweXRlc3QuaW1wb3J0b3Jza2lwYCBtZXRob2Qgc28gSSB3b3Vs
ZCBhdm9pZCBoYXZpbmcgdHdvIHdheXMgb2YgZG9pbmcgdGhlIHNhbWUgdGhpbmcuIE9uIHRoZSBv
dGhlciBoYW5kLCB0aGUgaW1wbGVtZW50YXRpb24gd291bGQgYmUgcmVhbGx5IHNpbXBsZSBzbyBp
dCB3b3VsZG4ndCBiZSBhIG1haW50ZW5hbmNlIGJ1cmRlbiBpZiBvdGhlcnMgcmVhbGx5IGxpa2Ug
aXQuPC9kaXY+PGRpdj48YnI+PC9kaXY+PGJsb2NrcXVvdGUgY2xhc3M9ImdtYWlsX3F1b3RlIiBz
dHlsZT0ibWFyZ2luOjAgMCAwIC44ZXg7Ym9yZGVyLWxlZnQ6MXB4ICNjY2Mgc29saWQ7cGFkZGlu
Zy1sZWZ0OjFleDsiPjxkaXYgc3R5bGU9IndvcmQtd3JhcDpicmVhay13b3JkIiBjbGFzcz0iZ21h
aWxfbXNnIj48ZGl2IGNsYXNzPSJnbWFpbF9tc2ciPk9yIHdvdWxkIHlvdSBqdXN0IHJlY29tbWVu
ZCB3ZSBpbXBsZW1lbnQgd2hhdCB3ZSB3YW50IGluIG91ciBvd24gYHV0aWxzLnB5YCBpbnN0ZWFk
IG9mIHRyeWluZyB0byBnZW5lcmFsaXNlIGEgc29sdXRpb24gZm9yIHRoaXM/PC9kaXY+PC9kaXY+
PC9ibG9ja3F1b3RlPjxkaXY+PGJyPjwvZGl2PjxkaXY+SSB0aGluayBpdCBpcyByZWFsbHkgY29t
bWVuZGFibGUgb2YgeW91IHRvIHRyeSB0byBwcm92aWRlIGEgZ2VuZXJhbCBzb2x1dGlvbiB0byB0
aGUgY29tbXVuaXR5LCBzbyBwbGVhc2UgZG9uJ3QgZ2V0IGRpc2NvdXJhZ2VkIGJ5IG15IGAtMGAu
IEFuZCBzaW5jZSB0aGUgaW1wbGVtZW50YXRpb24gaXMgc2ltcGxlIGFuZCBbY29va2llY3V0dGVy
LXB5dGVzdC1wbHVnaW5dKGh0dHBzOi8vZ2l0aHViLmNvbS9weXRlc3QtZGV2L2Nvb2tpZWN1dHRl
ci1weXRlc3QtcGx1Z2luKSBtYWtlcyBjcmVhdGluZyBwbHVnaW5zIGEgc25hcCwgeW91IHNob3Vs
ZCBkZWZpbml0ZWx5IGdvIHdpdGggaXQgaWYgeW91IHJlYWxseSBwcmVmZXIgdGhlIGRlY29yYXRp
b24gbWV0aG9kLjwvZGl2PjxkaXY+PGJyPjwvZGl2PjxkaXY+Q2hlZXJzLDwvZGl2PjxkaXY+QnJ1
bm8uPC9kaXY+PC9kaXY+" style="height:0;width:0;max-height:0;max-width:0;overflow:hidden;font-size:0em;padding:0;margin:0" class="">​</div></div></div>
</div></blockquote></div><br class=""></body></html>