I have the following code:
import xmlrpc.client as xc
class AristaSwitch():
def __init__(self,devicename,user='admin',password='xxxxxx')
self.url="https://"+user+":"+password+"#"+devicename+"/command-api"
self.Server = xc.Server(self.url) **<----i know this is not correct**
more code below here
I would like to be able to write my code like below:
as = AristaSwitch("192.168.1.1")
as.runCmds(1, [ "show hostname" ] )
The way they do it is:
import xmlrpc.client as xc
switch = xc.Server( "https://admin:admin#172.16.130.16/command-api" )
response = switch.runCmds( 1, [ "show hostname" ] )
Update
I think that adding this to the init function should do it
self.InitializeRPCServer()
def InitializeRPCServer():
switch=xc.Server(self.url)
return switch
It seems you're just trying to wrap around xc.Server. Just use a function instead of a class.
import xmlrpc.client as xc
def AristaSwitch(devicename, user='admin', password='xxxxxx'):
url="https://"+user+":"+password+"#"+devicename+"/command-api"
Server = xc.Server(url)
return Server
Then just do your thing:
as = AristaSwitch("192.168.1.1")
as.runCmds(1, [ "show hostname" ] )
If you're talking about customizing the xc.Server object, you can just inherit it:
class AristaSwitch(xc.Server):
def __init__(self, devicename, user='admin', password='xxxxxx'):
self.url="https://"+user+":"+password+"#"+devicename+"/command-api"
super().__init__(self.url)
You will need to update the def __init__ to your customized url input, but you should be rather savvy about the original implementation since you might be inadvertently overwriting some attributes or implementation details from the super class xc.Server.
With this use case, AristaSwitch is basically xc.Server with a customized instantiation method, and you can compliment it with your own methods later if you wish.
Related
I am trying to test with my code by mocking the PyGithub library.
I want to create a repository for an organization. So first I need to get it and on the "Organization" returned object, I need to make another call.
It fails when trying to assert that my second method was called.
I am very new to python and I am guessing that there is a missing connection between the mocks but I cannot figure out what.
class GithubService:
def __init__(self, token: str) -> None:
self.__github__ = Github(token)
self.__token__ = token
def create_repo_extra(self, repo_name, description, organization_name, team_name):
try:
organization = self.__github__.get_organization(organization_name)
repo = organization.create_repo(name=repo_name,
description=description,
private=True,
has_issues=False,
has_wiki=False,
has_projects=False,
allow_merge_commit=False)
# do other things with the returned repo.....
return True
except GithubException as ex:
print(ex.data)
return False
Here is the test:
import unittest
from unittest.mock import patch, MagicMock, ANY
from github.Organization import Organization
from github.Repository import Repository
from src.github_service import GithubService
class TestGithubService(unittest.TestCase):
#patch('github.Organization.Organization.create_repo',
side_effect=MagicMock(return_value=Repository(ANY, {}, {}, True)))
#patch('github.MainClass.Github.get_organization',
return_value=MagicMock(return_value=Organization(ANY, {}, {}, True)))
def test_create_repo_returns_true(self, get_organization, create_repo):
sut = GithubService("token")
actual = sut.create_repo_extra('repo-name', 'description', 'organization-name', 'team-name')
get_organization.assert_called() # ok
create_repo.assert_called() # failed
self.assertTrue(actual)
Since you mock your Github.get_organization you can use the MagicMock it returns directly rather than trying to mock another layer.
In this, I patch the same Github.get_organization, but avoid giving it a side effect or return value, and therefore pass it as an arg (like you did).
Then I create a convenience mock_organization and it will be the return value of the patched Github.get_organization.
Finally, the patch is checked like you did, and through the convenience mock_organization I check the create_repo method is called as well.
class TestGithubService(unittest.TestCase):
#patch("github.MainClass.Github.get_organization")
def test_create_repo_returns_true(self, mock_get_organization):
mock_organization = MagicMock()
mock_get_organization.return_value = mock_organization
sut = GithubService("token")
actual = sut.create_repo_extra(
"repo-name", "description", "organization-name", "team-name"
)
mock_get_organization.assert_called() # ok
mock_organization.create_repo.assert_called() # ok
self.assertTrue(actual)
Without seeing more of your code I am not sure why patching Organization did not work, but this is simpler, cleaner and just as effective.
I'm not sure if I used the right terms in the title. This maybe a known way to program interface functions for a subsystem or module but because I don't know the keywords, I'm not finding the results in my search queries.
I want to create a function whose intention can be clearly described in the functions name but the parameters are flexible. I want to write the function to be generic enough so that the function can complete the intention with whatever parameters it receives from whichever caller.
Let's take a function do_foo.
do_foo can take in some_obj whose attributes allows do_foo to do its work. Additionally, do_foo can just take in the individual attributes it cares about like obj_attr0 or obj_attr1 and perform the same work. In both cases, the expected result is the same as well.
So this would look something like this:
Class SomeObj():
def __init__(self, obj_attr0, obj_attr1, obj_attrN):
self.obj_attr0 = None
self.obj_attr1 = None
self.obj_attrN = None # denotes an N number of attributes
def do_foo(params)
# unpack params. do_foo requires obj_attr0 and obj_attr1 and so its searching it in the params iterable
# determine which data is passed in
# execute the work the same way regardless of what form the data is passed in
pass
obj_attr0 = None
obj_attr1 = None
obj_attrN = None
some_obj = SomeObj(obj_attr0, obj_attr1, obj_attrN)
# One can either call with a subset of attributes that would make up SomeObj or SomeObj itself if all the information is there. E.g.:
params = (some_obj)
do_foo(params)
# OR
params = (obj_att0, obj_attr1)
do_foo(params)
I know python offers *args and **kwargs facilities that offer the flexibility above. I'm looking for some examples of where the implementation lends itself to reducing pitfalls. What is a good way to implement the above? And if there are any resources out there what are examples/articles/or terms that describe the above style of programming? Clearly, I'm trying to write my interface functions to be generic and usable in multiple logic paths where the users has its data in different forms where sticking to a specific parameter list is limiting.
Short answer:
You can use function decorators to do this
Long answer:
I have a concrete example for you. It might not be the prettiest code but it does something similar to what you are asking for.
Mini HTTP Testing library
I made a mini HTTP testing library because I make my REST http tests in python, and I realized that I always write the same code again and again. So I made a more general setup
The core
The core is kind of ugly and this is the part I don't want to write again and again.
Just skip this part quick and check how it is used in the interface section.
Then if you like it you can go back and try to understand how it is all tied together.
# base.py
import json, requests, inspect
# This function drops invallid parameters
def request(*args, **kwargs):
allowed = inspect.signature(requests.Session.request).parameters
return {k:v for (k,v) in kwargs.items() if k in allowed}
def response(r, code):
if r.status_code != code:
print(r.text)
return
data = r.json()
if data:
print(json.dumps(data, indent=2, ensure_ascii=False))
return data
# This is the core function it is not pretty but it creates all the abstaction in multiple levels of decorations.
def HTTP(base_url):
def outer(func_one):
def over(*args_one, **kwargs_one):
req, url, code = func_one(*args_one, **kwargs_one)
url = base_url + url
def inner(func_two):
def under(*args_two, **kwargs_two):
allowed = inspect.signature(func_two).parameters
kwparams = {k:v for (k,v) in kwargs_two.items() if k in allowed}
from_inner = func_two(*args_two, **kwparams)
u = url.format(id=kwargs_two.pop('_id')) if '{id}' in url else url
r = req(u, **request(**kwargs_two, **from_inner))
return response(r, code)
return under
return inner
return over
return outer
The interface
The interface functions are all each decorated by the HTTP function which makes them a HTTP caller function, it is still abstract since it will return a function.
Note: interface is just what I call it but it is really just functions which returns functions based on the HTTP decorator
BASE_URL = "https://example.com"
#HTTP(BASE_URL)
def POST(url, code=200): return requests.post, url, code
#HTTP(BASE_URL)
def PUT(url, code=200): return requests.put, url, code
#HTTP(BASE_URL)
def DELETE(url, code=200): return requests.delete, url, code
#HTTP(BASE_URL)
def GET(url, code=200): return requests.get, url, code
A middleware function
When one of the interface functions are decorated with this one then they need a token.
def AUTH(func):
def inner(token, *args, **kwargs):
headers = {'Authorization': f'bearer {token}'}
return func(*args, **kwargs, headers=headers)
return inner
The implementation
The interface can be used for many implementations.
Here I use the interface of POST, PUT, GET and DELETE for the user model.
This is the final decoration, and the functions returned will actually return content instead of other functions.
# users.py
from httplib.base import (
POST,
GET,
DELETE,
PUT,
AUTH,
request
)
#POST('/users',200)
def insert(user):
return request(json=user)
#AUTH
#GET('/users')
def find(_filter={}):
return request(params=_filter)
#AUTH
#GET('/users/{id}')
def find_one(_id):
return request()
#AUTH
#DELETE('/users/{id}')
def delete(_id):
return request()
#AUTH
#PUT('/users/{id}')
def update(_id, updates={}):
return request(json=updates)
Operation
Here you can see how the users delete insert and find functions work.
from httplib import users
def create_and_delete_users(token, n): return [
users.delete(token, _id=x['user']['id'])
for x in [
users.insert(user={
'username' : f'useruser{str(i).zfill(2)}',
'password' : 'secretpassword',
'email' : f'useruser{str(i).zfill(2)}#mail.com',
'gender' : 'male',
}) for i in range(n)]
]
def find_all_and_then_find_each(token): return [
users.find_one(token, _id=x['id'])
for x in users.find(token)['data']
]
I hope this was helpful.
Let's say we have such situation:
from django_tenants.utils import schema_context
def do_something(context):
print("do_something")
def my_callable():
tenant = "db_tenant"
with schema_context(tenant):
context = {"a": 1, "b": 2}
do_something(context)
my_callable()
And question is: It's possible to access current tenant name in do_something function without passing it as parameter or store it as global variable
I found a solution but i don't know if it's stable. So, current tenant name (or schema_name) can be accessed through django.db connection as follow:
from django.db import connection
schema_name = connection.schema_name
No this is not possible, or at least will require some magical engineering to do so.
I'm assuming the only reason you wouldn't want to pass it as a parameter is because other things may be calling do_something as well that wouldn't be passing tenant as a parameter. In this case do:
def do_something(context, tentant=None):
if tenant:
print (tenant)
else:
print ("do_something")
Now you can call do_something with do_something(context, tenant='Bob') or do_something(context) and either will be fine.
I have view function that uses nmap to scan the devices in the network.
views.py
import nmap
def home(request):
y=nmap.PortScanner()
data = y.scan(hosts="192.168.1.*", arguments="-sP")
context[status]=data['status']['addresses']['ipv4']
return render_template('home.html',context)
Now i want to test this for no devices, 1 device connected and 2 or more device connected. I need to override data in tests.py.
I was thinking that it can be done using mock function. I can override it in tests.py but when simulate responses it not get override in view function.
How can i test this nmap function ?
Monkey patching would be a good solution in your case.
Also have a look at this SO question about monkey patching
here is a possible implementation, of course you need to integrate this into your test framework.
import your_module
class MockPortScanner(object):
# by setting this class member
# before a test case
# you can determine how many result
# should be return from your view
count = 0
def scan(self, *args, **kwargs):
return {
'status': {
'addresses': {
'ipv4': [i for i in range(self.count)]
}
}
}
def your_test_method():
MockPortScanner.count = 5
request = None # create a Mock Request if you need
# here is the mocking
your_module.nmap.PortScanner = MockPortScanner
# call your view as a regular function
rv = your_module.home(request)
# check the response
UPDATE
To have the original PortScanner later in other parts of tests, save it in the tests after importing nmap.
import nmap
OriginalPortScanner = nmap.PortScanner
Then, you will able to select the PortScanner (either original or mock) like:
views.nmap.PortScanner = OriginalPortScanner
I'm new to python and pylons although experienced in PHP.
I'm trying to write a model class which will act as a my data access to my database (couchdb). My problem is simple
My model looks like this and is called models/BlogModel.py
from couchdb import *
class BlogModel:
def getTitles(self):
# code to get titles here
def saveTitle(self):
# code to save title here
My controller is called controllers/main.py
import logging
from pylons import request, response, session, tmpl_context as c
from pylons.controllers.util import abort, redirect_to
from billion.lib.base import BaseController, render
log = logging.getLogger(__name__)
from billion.model import BlogModel
class MainController(BaseController):
def index(self):
return render('/main.mako')
In my index action, how do I access the method getTitles() in BlogModel?
I've tried
x = BlogModel()
x.getTitles()
But i get
TypeError: 'module' object is not callable
Also
BlogModel.getTitles() results in
AttributeError: 'module' object has no attribute 'getTitles'
Is this down to the way I'm including the class ? Can someone tell me the best way to do this ?
thanks
x = BlogModel.BlogModel()
Or, more verbosely:
After you did the import, you have an object in your namespace called 'BlogModel'. That object is the BlogModel module. (The module name comes from the filename.) Inside that module, there is a class object called 'BlogModel', which is what you were after. (The class name comes from the source code you wrote.)
Instead of:
from billion.model import BlogModel
You could use:
from billion.model.BlogModel import BlogModel
then your
x = BlogModel()
would work.