Python: How to define a class attribute's value dynamically? - python

I need to set a class attribute dynamically at run time. How can this be achieved? There is no way that I have found to set a class attribute outside of the instance init which means that it runs each time a an instance is created. This only needs to happen one time (when the class definition is loaded).
REVISED EXAMPLE.
Ideally i need to do something like this, but it seems this is not possible:
# Note modules are python modules which extend the functionality of my reusable app.
class Test():
active_modules = Test.get_active_moduels()
#classmethod
def get_active_moduels(cls):
#logic for collecting installed modules
return installed
I could get the job done like this, but it seems silly and causes major problems. If i try to access active_modules before a Test class is instantiated i get [] even though there are active_modules.
# Note modules are python modules which extend the functionality of my reusable app.
class Test():
active_modules = []
def __init__ (self):
Test.get_active_moduels()
#classmethod
def get_active_moduels(cls):
if not cls.active_modules:
#logic for collecting installed modules
.....
cls.active_modules = installed_modules
else:
return cls.active_modules
I could get this done by creating another registry of active_modules, but I would think there is a way to accomplish this within the class...

It would be much easier to help if you used concrete terminology. What is a "sub"? In any case, your code can be simplified to:
def compute_defaults():
they = []
they.append('default_1')
they.append('default_2')
return they
class Test():
active_subs = compute_defaults()
def __init__(self, sub=None):
if sub:
self.active_subs.append(sub)
Then you can use your code as you wish:
t1 = Test()
#active will have the installed subs.
active = t1.active_subs
Don't over-complicate your code with getters and setters needlessly.

Related

Why is my pytest fixture with function scope returning an object that isn't resetting it's class variables in a new test?

I have a class called Person(). It has a CURRENT_YEAR class variable, intended to be shared among all instances of the class.
I was hoping that each of my tests in a single module would get a fresh (new) object since I scoped the fixture as 'function'. However, when I change the CURRENT_YEAR in one test function, which happens using a class method that changes the Person.CURRENT_YEAR value, it persists into the next test function. So clearly the object isn't getting wiped out and recreated for each test.
The fixture is created in the conftest.py, accessible by all tests.
In the end, I broke it all down, and moved things around, but keep seeing the same thing. The Person() class is not getting instantiated more than once, as I would have expected. How should a fixture be created, so that each test_ function gets its own scope for the Class?
I've tried moving tests to separate modules, but it didn't help.
I tried making a second fixture, that returns a Person() object. No difference.
I've really stripped it down in the code below, so it's hopefully clear what I'm trying and why I'm confused.
project_root/tests/test_temp.py
import os,sys
tests = os.path.dirname(__file__)
project = os.path.dirname(tests)
sys.path.insert(0,project)
import pytest
from app.person import *
def test_current_year_changes(person_fixture):
import pytest
p = person_fixture
print(f"CY is {p.CURRENT_YEAR} and age is {p.get_age()}")
p.add_years_to_age(20)
print(f"CY is {p.CURRENT_YEAR} and age is {p.get_age()}")
assert p.CURRENT_YEAR == 20
def test_current_year_changes2(person_fixture2):
import pytest
p = person_fixture2
print(f"CY is {p.CURRENT_YEAR} and age is {p.get_age()}")
p.add_years_to_age(20)
print(f"CY is {p.CURRENT_YEAR} and age is {p.get_age()}")
assert p.CURRENT_YEAR == 20
#pytest.fixture(scope='function')
def person_fixture():
p = Person()
return p
#pytest.fixture(scope='function')
def person_fixture2():
p = Person()
return p
project_root/app/person.py
class Person(object):
CURRENT_YEAR = 0
def __init__(self, name=""):
self.name = name
self.birth_year = Person.CURRENT_YEAR
def add_years_to_age(self, years=1):
Person.CURRENT_YEAR += years
def get_age(self):
return Person.CURRENT_YEAR - self.birth_year
The code looks like both tests should be pretty independent. But the second test function shows that the CURRENT_YEAR is not starting with a new class variable.
The assert fails showing that the Person.CURRENT_YEAR is 40, instead of 20
The fixture scope just defines when the function that is decorated with #pytest.fixture is run. It's just a way to factor common test code into a separate function.
So in your case it's "function" so the fixture will execute the function for each test function (that uses the fixture) and it creates a Person instance. Similarly it would run once per test module if the scope were "module".
And that is working exactly as intended. It's not just working as intended by pytest but also as intended by yourself - remember that you actually wanted to share CURRENT_YEAR between different instances!
How should a fixture be created, so that each test_ function gets its own scope for the Class?
You really shouldn't use global or static variables (and class variables are just global variables hidden behind a class) exactly because it makes testing really hard (and make the program non-thread-safe). Also remember that pytest cannot provide the infrastructure to reset your program if you don't provide it! Think about it: What exactly should happen? Should it create a new interpreter session for each test function? Should it reload modules? Should it reload the class definition? Should it just set Person.CURRENT_YEAR to zero?
One way to solve this is to abstract the class variables for example with an environment class (the current year also doesn't seem a good fit for a Person class anyway):
class Environment(object):
def __init__(self):
self.CURRENT_YEAR = 0
class Person(object):
def __init__(self, environment, name=""):
self.environment = environment
self.name = name
self.birth_year = self.environment.CURRENT_YEAR
def add_years_to_age(self, years=1):
self.environment.CURRENT_YEAR += years
def get_age(self):
return self.environment.CURRENT_YEAR - self.birth_year
And then let the fixture create a new environment and person instance:
#pytest.fixture(scope='function')
def person_fixture():
e = Environment()
p = Person(e)
return p
At that point you probably need a global Environment instance in your code so that different Person instances can share it.
Note that this makes not much sense if it's just one variable and probably you end up with different classes for different environmental variables. If your app gets more complicated you probably need to think about dependency injection to manage that complexity.
However if you just want the CURRENT_YEAR to reset for each function that uses your person_fixture you could also just set it to 0 in the fixture:
#pytest.fixture(scope='function')
def person_fixture_with_current_year_reset():
Person.CURRENT_YEAR = 0
p = Person()
return p
That should work for now but at the time you run the tests in parallel you might see random failures because global variables (and class variables) are inherently non-thread-safe.

How do you decide which level a function should be at in python?

I have a file called file_parsers.py and it contains the following class:
class FileParser():
def __init__(self, file_text):
self.file_text = file_text
def do_something(self):
my_value = func_with_no_state()
I'm not sure what questions to ask when deciding whether func_with_no_state() should be inside the class or outside of the class as a file-level function?
Also, is it easier to stub this function when it is at a file-level or inside the class?
So... Does any other class use func_with_no_state? If not, it should be hidden within FileParser. If something else does use it, you have a bigger question. If OtherClass uses func_with_no_state pretty frequently (on par with FileParser) then it would be a good idea to keep func_with_no_state outside so that both classes can use it. But if FileParser is by far the main user, then OtherClass could just pull the function from FileParser's definition.

Python mockito - Mocking a class which is being instantiated from the testable function

I am bit lost while writing the test case for UserCompanyRateLimitValidation class. I am finding difficulty in mocking the class which is being instantiated from inside this class.
class UserCompanyRateLimitValidation:
def __init__(self, user_public_key):
self.adapter = UserAdapter(user_public_key)
container = self.adapter.get_user_company_rate_limit()
super(UserCompanyRateLimitValidation, self).__init__(container,\
UserCompanyRateLimitValidation.TYPE)
I have to test this class. I have written test case something like this. I have tried to mock the UserAdapter class but I am not able to do so completely.
def test_case_1():
self.user_public_key = 'TEST_USER_PUBLIC_KEY_XXXXXX1234567890XXXXX'
UserAdapter_mock = mock(UserAdapter)
when(UserAdapter_mock).get_user_company_rate_limit().\
thenReturn(get_fake_container_object())
self.test_obj = UserCompanyRateLimitValidation(self.user_public_key)
Here if you see I have mocked get_user_company_rate_limit() call from the testable function, container = self.adapter.get_user_company_rate_limit()
but I am still not able to figure out the way in which I can mock this call,
self.adapter = UserAdapter(user_public_key)
It is quite simple if you know the trick.
Creating an object in Python is very much like a function call to the class object. UserCompanyRateLimitValidation is 'invoking' UserAdapter(user_public_key). You want to stub the return value of that 'call' to return UserAdapter_mock.
You can stub this like you would stub a function in a module. The line you're missing is:
when(module_declaring_UserAdapter)\
.UserAdapter(self.user_public_key)\
.thenReturn(UserAdapter_mock)
After that, calling module_declaring_UserAdapter.UserAdapter(self.user_public_key) will return UserAdapter_mock.
Here's the link to the section in the manual: https://code.google.com/p/mockito-python/wiki/Stubbing#Modules
You have to be careful to choose the right module_declaring_UserAdapter, due to the way the from ... import ... statement works. From your code, I'd say you have to pick the module in which UserCompanyRateLimitValidation is declared.
Here is another way of looking at it. Say I have this code in which I would like to mock MyClass:
from some.module import MyClass
class AnotherClass:
def __init__(self):
self.my_class = MyClass()
One would typically call the imports as shown above. With some slight modification of the import, we can get it into a state where MyClass it can be mocked using mockito:
from some import module
class AnotherClass:
def __init__(self):
self.my_class = module.MyClass()
Then the mocking would work like so:
from some import module
when(module).MyClass().thenReturn(mock())

Nested function as attribute in Python

Not sure if this is a dupe or not. Here it goes.
I need to write some Python code that looks like:
class TestClass:
def test_case(self):
def get_categories(self):
return [“abc”,”bcd”]
# do the test here
and then have a test engine class that scans all these test classes, loads all the test_case functions and for each invokes get_categories to find out if the test belongs t the group of interest for the specific run.
The problem is that get_categories is not seen as an attribute of test_case, and even if I manually assign it
class TestClass:
def test_case(self):
def get_categories(self):
return [“abc”,”bcd”]
# do the test here
test_case.get_categories = get_categories
this is only going to happen when test_case first runs, too late for me.
The reason why this function can’t go on the class (or at least why I want it to be also available at the per-function level) is that a TestClass can have multiple test cases.
Since this is an already existing testing infrastructure, and the categories mechanism works (other than the categories-on-function scenario, which is of lesser importance), a rewrite is not in the plans.
Language tricks dearly appreciated.
Nested functions don't become attributes any more than any other assignment.
I suspect your test infrastructure is doing some severely weird things if this isn't supported (and uses old-style classes!), but you could just do this:
class TestClass:
def test_case(self):
# ...
def _get_categories(self):
return [...]
test_case.get_categories = _get_categories
del _get_categories
Class bodies are executable code like any other block.
What you need is nested classes. Functions aren't made to do what you are trying to do, so you have to move up a notch. Function attributes are mainly used as markup, whereas classes can have anything you want.
class TestClass(object):
class TestCase(object):
#classmethod
def get_categories(cls):
return ['abc', 'efg']
Note that I used #classmethod so that you could use it without instantiating TestCase(); modify if you want to do test_case = TestCase().

Python global/package alias implementation class

I have that strange feeling this is an easy question.
I want to be able to "alias" a class type so i can swap out the implementation at a package level. I dont want to have X amount of import X as bah scattered throughout my code...
Aka. How can I do something like the below:
class BaseClass(object):
def __init__(self): pass
def mymthod(self): pass
def mymthod1(self): pass
def mymthod2(self): pass
class Implementation(BaseClass):
def __init__(self):
BaseClass.__init__()
Seperate package...
#I dont want thse scattered through out modules,
#i want them in one place where i can change one and change implementations
#I tried putting it in the package init but no luck
import Implementation as BaseClassProxy
class Client(BaseClassImpl):
def __init__(self):
BaseClassImpl.__init__(self)
In any file (where this fits best is up to you, probably wherever Implementation was defined):
BaseClassProxy = Implementation
Since classes are first class objects in Python, you can pretty much bind them to any variable and use them the same way. In this case, you can make an alias for the class.
just put something like
BaseClassProxy = Implementation
in the module, then do:
from module import BaseClassProxy

Categories