How does Pytest figure out that the monkeypatch fixture is for monkeypatching? - pytest

Consider this simple example from pytest's docs
# contents of test_module.py with source code and the test
from pathlib import Path
def getssh():
"""Simple function to return expanded homedir ssh path."""
return Path.home() / ".ssh"
def test_getssh(monkeypatch):
# mocked return function to replace Path.home
# always return '/abc'
def mockreturn():
return Path("/abc")
# Application of the monkeypatch to replace Path.home
# with the behavior of mockreturn defined above.
monkeypatch.setattr(Path, "home", mockreturn)
# Calling getssh() will use mockreturn in place of Path.home
# for this test with the monkeypatch.
x = getssh()
assert x == Path("/abc/.ssh")
How does Pytest figure out that the param monkeypatch is the one for monkeypatching?

Related

Qgis Plugin QgsMapToolEmitPoint QgsMapToolIdentifyFeature initialization

I'm working on a custom plugin in Qgis 3.22.3 Qt 5.15.2. My goal is to have a tool run when I click on a button in my dock widget that can either identify or select features from a layer so I can pass those attributes to a database function. I have tried creating subclasses of both QgsMapToolEmitPoint and QgsMapToolIdentifyFeature and the init method does get called but the button 'does nothing'. Slightly modifying my code to create a subclass of "QgsMapTool" does in fact start the tool and responds to logging from canvasPress events, etc. I'm still new to Qgis and figuring out how everything plays together, so I'm not sure if I can return features from QgsMaptool or not. I would love it if someone could help offer some suggestions or guidance because I haven't been able to find clarity, and any examples I've tried modifying seem like standalone scripts that are run directly from Qgis. Here is where my buttons connect(which gets called in init_gui), the subclass definition, and the function that gets called on button push. Code is very messy because of all the different iterations I've tried, but left it all here in case I have the right pieces. Again any leads would be helpful.
def connect_the_buttons(self):
# # get spots
b = self.dock.select_spots_tool
b.setEnabled(True)
b.setCheckable(True)
b.clicked.connect(self.getSpots)
def getSpots(self, checked):
if checked:
spot_id_List.clear()
t = self.selectSpots(self.iface)
#t = gs.selectSpots(self.iface)
self.iface.mapCanvas().setMapTool(t)
QApplication.setOverrideCursor(Qt.CrossCursor)
spots = QgsProject.instance().mapLayersByName('spots')[0]
self.iface.setActiveLayer(spots)
tType = type(t)
log(f'what is t? >> {type(t)}')
log(f'{tType}')
log(f'type({t})')
log(f'{self.selectSpots}')
else:
log('not checked')
QApplication.restoreOverrideCursor()
spot_id_List.clear()
class selectSpots(QgsMapToolEmitPoint):
#canvasClicked = pyqtSignal('QgsPointXY')
# def __new__(cls, *args, **kwargs):
# return super(selectSpots, cls).__new__(cls, *args, **kwargs)
def __init__(self, canvas):
log('selectSpots __init__ function is running...')
self.canvas = self.iface.mapCanvas()
#QgsMapTool.__init__(self, canvas)
#self.layer = self.iface.activeLayer()
#self.iface.canvasClicked.connect(self.id_spots)
#c = iface.mapCanvas()
#activeSpots = iface.activeLayer()
QgsMapToolEmitPoint.__init__(self, self.canvas)
#self.iface.layer().connect(self.getLayerId)
#self.iface.currentLayerChanged.connect(self.active_changed)
#QApplication.setOverrideCursor(Qt.CrossCursor)
# # def getLayerId(self):
# # log('connected_to_click layer event')
# # # def id_spots(self):
# # # log('this happend when i clicked')
def activate(self):
log('The activate method was called.')
def canvasDoubleClickEvent(self, e):
log('Double Clicked')
log(f'double click event: {e}')
# # def active_changed(self, layer):
# # activeSpots.removeSelection()
# # spot_id_List.clear()
def canvasReleaseEvent(self, event):
log('canvas release event recorded')
def canvasPressEvent(self, event):
log('canvas press event recorded')
point = event.mapPoint()
self.canvasClicked.emit(point)
log(f'point: {point}')
# # log(f'event: {event}')
# # #self.handle_spot_selection()
# # log(f'on press: spot_id_List: {spot_id_List}')
# # # selectedLayer = self.iface.activeLayer()
# # # selection = selectedLayer.selectedFeatures()
# # #self.iface.actionSelect().trigger()
# # for f in selection:
# # spot_id_List.append(f.attribute('id'))
# # log(f'after press: spot_id_List: {spot_id_List}')

pytest-dependency is not working in my test

There are 2 files, the code in the first one is:
import pytest
class TestXdist2():
#pytest.mark.dependency(name="aa")
def test_t1(self):
print("\ntest_1")
assert True
the code in the second file is:
import pytest
import sys, os
sys.path.append(os.getcwd())
from testcases.test_xdist_2 import TestXdist2
class TestXdist1():
def setup_class(self):
self.x = TestXdist2()
#pytest.mark.dependency(depends=["aa"], scope="module")
def test_t2(self):
print("\ntest_t2")
assert 1==1
if __name__ == "__main__":
pytest.main(["-s", "-v", f"{os.path.abspath('testcases')}/test_xdist_1.py"])
when I run the senond file, I thought test case "test_t1" should be ran firstly, then "test_t2" ran secondly, but the result is like this, "test_t2" is skipped, I don'y know why,
PS D:\gitProjects\selenium_pytest_demo> & D:/Python38/python.exe d:/gitProjects/selenium_pytest_demo/testcases/test_xdist_1.py
Test session starts (platform: win32, Python 3.8.7, pytest 6.2.2, pytest-sugar 0.9.4)
cachedir: .pytest_cache
metadata: {'Python': '3.8.7rc1', 'Platform': 'Windows-10-10.0.18362-SP0', 'Packages': {'pytest': '6.2.2', 'py': '1.10.0', 'pluggy': '0.13.1'}, 'Plugins': {'allure-pytest': '2.8.35', 'dependency': '0.5.1', 'forked': '1.3.0', 'html': '3.1.1', 'metadata': '1.11.0', 'rerunfailures': '9.1.1', 'sugar': '0.9.4', 'xdist': '2.2.1'}, 'JAVA_HOME': 'D:\\Java\\jdk-15.0.1'}
rootdir: D:\gitProjects\selenium_pytest_demo, configfile: pytest.ini
plugins: allure-pytest-2.8.35, dependency-0.5.1, forked-1.3.0, html-3.1.1, metadata-1.11.0, rerunfailures-9.1.1, sugar-0.9.4, xdist-2.2.1
collecting ...
testcases\test_xdist_1.py::TestXdist1.test_t2 s 50% █████
test_1
testcases\test_xdist_2.py::TestXdist2.test_t1 ✓ 100% ██████████
Results (0.04s):
1 passed
1 skipped
This is the expected behavior - pytest-dependency does not order testcases, it only skips testcases if the testcase they depend on is skipped or failed. There exists a PR that would change that, but is not merged
Until that, you can use pytest-order. If you just want the ordering, you can use relative markers. If you also want to skip tests if the test they depend on failed, you can use pytest-dependency as before, but use the pytest-order option --order-dependencies to order the tests additionally.
Disclaimer:
I'm the author of pytest-order (which is a fork of pytest-ordering).

Run pytest inside a script (if name...)

I have the next file with tests
import pytest
from httpx import AsyncClient
import sys
import config
from main import app
#pytest.mark.asyncio
async def test_register():
async with AsyncClient(app=app, base_url="http://test") as ac:
response = await ac.post("/register", )
assert response.status_code == 200
I want to run it like
if __name__ == '__main__':
pytest.run() # Or something alike
How I can do it? I need it to assign name 'main' to this module, because the main module (which import above) has a constraint like: if __name__ == '__main__', so without it tests will not be run indeed.
pytest.main() would run pytest in the current working directory and this would include your file depending on the filename (e.g. if the filename starts with test_). To run pytest on the current file only you can run:
if __name__ == "__main__":
pytest.main([__file__])

Using input function with remote files in snakemake

I want to use a function to read inputs file paths from a dataframe and send them to my snakemake rule. I also have a helper function to select the remote from which to pull the files.
from snakemake.remote.GS import RemoteProvider as GSRemoteProvider
from snakemake.remote.SFTP import RemoteProvider as SFTPRemoteProvider
from os.path import join
import pandas as pd
configfile: "config.yaml"
units = pd.read_csv(config["units"]).set_index(["library", "unit"], drop=False)
TMP= join('data', 'tmp')
def access_remote(local_path):
""" Connnects to remote as defined in config file"""
provider = config['provider']
if provider == 'GS':
GS = GSRemoteProvider()
remote_path = GS.remote(join("gs://" + config['bucket'], local_path))
elif provider == 'SFTP':
SFTP = SFTPRemoteProvider(
username=config['user'],
private_key=config['ssh_key']
)
remote_path = SFTP.remote(
config['host'] + ":22" + join(base_path, local_path)
)
else:
remote_path = local_path
return remote_path
def get_fastqs(wc):
"""
Get fastq files (units) of a particular library - sample
combination from the unit sheet.
"""
fqs = units.loc[
(units.library == wc.library) &
(units.libtype == wc.libtype),
"fq1"
]
return {
"r1": list(map(access_remote, fqs.fq1.values)),
}
# Combine all fastq files from the same sample / library type combination
rule combine_units:
input: unpack(get_fastqs)
output:
r1 = join(TMP, "reads", "{library}_{libtype}.end1.fq.gz")
threads: 12
run:
shell("cat {i1} > {o1}".format(i1=input['r1'], o1=output['r1']))
My config file contains the bucket name and provider, which are passed to the function. This works as expected when running simply snakemake.
However, I would like to use the kubernetes integration, which requires passing the provider and bucket name in the command line. But when I run:
snakemake -n --kubernetes --default-remote-provider GS --default-remote-prefix bucket-name
I get this error:
ERROR :: MissingInputException in line 19 of Snakefile:
Missing input files for rule combine_units:
bucket-name/['bucket-name/lib1-unit1.end1.fastq.gz', 'bucket-name/lib1-unit2.end1.fastq.gz', 'bucket-name/lib1-unit3.end1.fastq.gz']
The bucket is applied twice (once mapped correctly to each element, and once before the whole list (which gets converted to a string). Did I miss something ? Is there a good way to work around this ?

Paging stdout output in IPython

Is it possible in an (interactive) IPython session to pass the stdout output through a pager, like less? If so, how?
For example, in
In [1]: from some_module import function_that_prints_a_lot
In [2]: function_that_prints_a_lot()
... everything scrolls away ...
I would like to page through the stdout output of function_that_prints_a_lot.
Another example:
In [1]: %run script_that_prints_a_lot.py
I've looked through IPython magic commands but didn't find any solution.
As discussed in chat there is no simple way of doing this. Since the function prints the values, the only thing you can do is Capture output + Then page output. There are few issues on jupyter that you might be interested in
https://github.com/jupyter/notebook/issues/2049
https://github.com/ipython/ipython/issues/6516
Capturing output
Output capturing can be done multiple ways
1. Overload Print Method
import sys
data = ""
def myprint(value, *args, sep=' ', end='\n', file=sys.stdout, flush=False):
global data
current_text = value + " ".join(map(str, args)) + "\n"
data += current_text
original_print = print
print = myprint
def testing():
for i in range(1,1000):
print ("i =", i)
testing()
original_print("The output from testing function is", data)
2. Capture output using StringIO
from cStringIO import StringIO
import sys
class Capturing(list):
def __enter__(self):
self._stdout = sys.stdout
sys.stdout = self._stringio = StringIO()
return self
def __exit__(self, *args):
self.extend(self._stringio.getvalue().splitlines())
del self._stringio # free up some memory
sys.stdout = self._stdout
Usage:
with Capturing() as output:
do_something(my_object)
3. Capture output using redirect_stdout
import io
from contextlib import redirect_stdout
f = io.StringIO()
with redirect_stdout(f):
do_something(my_object)
out = f.getvalue()
4. Capture using %%capture magic command
Paging Output
You can use magin %page
%page -r <variablename>
https://ipython.readthedocs.io/en/stable/interactive/magics.html#magic-page
Or you can use Ipython code
from IPython.core import page
page.page(variable)
For more details refer to below
PS: Some helpful threads
How to capture stdout output from a Python function call?
How can I redirect print output of a function in python
https://github.com/ipython/ipython/wiki/Cookbook:-Sending-built-in-help-to-the-pager
overload print python
Using bits and pieces from various sources, but essentially it's from IPython's cookbook and Defining custom magics from IPython official documentation
In [1]: from IPython.core.magic import register_line_magic
In [2]: #register_line_magic
...: def my_pager(line):
...: "my line magic"
...: import io
...: from IPython.core import page
...: from contextlib import redirect_stdout
...: f = io.StringIO()
...: with redirect_stdout(f):
...: eval(line)
...: page.pager_page(f.getvalue())
...: del my_pager # don't pollute my namespace
In [3]: def pippo(): print('\n'.join(str(i)for i in range(80)))
In [4]: %my_pager pippo()
This approach has a serious drawback: if the function call that is the argument to %my_pager returns a value, said value is lost (no, %mypager result=print_and_return() won't work...)