Python 3.7 Variable Chained Method Calls - python

I've run into a similar problem as referenced here - Dynamic Method Call In Python 2.7 using strings of method names
Here's an example of what my working method call looks like, the method at the end is based on a given data type, in this case .string_value:
tag.fields[field["key"]].string_value = field["value"]
However I won't always be assigning just strings as there are methods for other data types. I attempted a solution similar to the one referenced in the linked thread:
typer = getattr(datacatalog_v1.types.TagField, f"{field['type']}_value")
tag.fields[field["key"]].typer = field["value"]
With typer being my new dynamic method call, but it's not working. I'm receiving this as an error - 'TagField' object has no attribute 'typer'.
Any suggestions?

This is quite interesting. I'm not sure what package/datatype ur working on, however it looks like you have 2 issues.
First, getattr returns a string, and you can't call a string, e.g. 'python'()
Second, if you remove the () after getattr(), typer will be a string data, and you cant use it like that. In
tag.fields[field["key"]].typer
typer must be a method/attribute of some sort rather than string. The best way is to build if statement or dict, combine different value of typer with different method/attribute calls.
type_methods = {'string_value': tag.fields[field["key"]].string_value,
'int_value': tag.fields[field["key"]].int_value,
'json_value': tag.fields[field["key"]].json_value}
typer = getattr(datacatalog_v1.types.TagField, f"{field['type']}_value")
type_method[type] = field["value"]
update:
There is a setattr(object, name, value) function
typer = getattr(datacatalog_v1.types.TagField, f"{field['type']}_value")
setattr(tag.fields[field['key']], typer, field['value'])

Related

What is the first parameter in conn.set_listener() used for?

Here's the code I'm looking at.
from stomp import *
c = Connection([('127.0.0.1', 62613)])
c.set_listener('print', PrintingListener())
c.start()
What is 'print' in c.set_listener('print', PrintingListener()) used for?
I checked some docs here https://jasonrbriggs.github.io/stomp.py/stomp.html#module-stomp.listener
but was unable to find out.
Mostly I just want to be sure that passing in an empty string there or the same value for multiple listeners is okay.
The first parameter in set_listener is simply the name of the listener instance. You can use this same name later when invoking get_listener and remove_listener (which both take a name parameter). It's also useful if you want to set multiple listeners on a single connection (i.e. with different names). The documentation refers to this saying:
Note that listeners can be named so you can use more that one type of listener at the same time
To be clear, an empty string (i.e. '') is a valid name just like any other string would be.

Python function's parameter default value is imported from another module

Let's consider the following example:
import module
def function(param=module.value):
pass
Is it good practice to set the default value of a function argument to an identifier imported from another module? I know it works, but I am asking rather about clean code approach and possible problems. I can see here a potential vulnerability: the value of the variable imported from module can be mutable in next release. What do you think about that?
I suggest setting the default to None then programatically test for that in your code:
import module
def function(param = None):
if param is None:
param = module.value
It could be that a straight assignment (which just copies a reference) is not appropriate.
For example, you mention that the class of object could change from immutable to mutable between releases. If that occurs then you probably need to use copy.deepcopy() rather than an assignment.
However, not all classes have their own __deepcopy__(), and so copying objects can be problematic. It could be that a new object should be created using the values from the original. It is hard to say what should be used without knowing the class of the object and what you are going to do with param.
You can write wrapper for your purpose. If the library change or deprecate value, you can still get the same value.
Just write a simple wrapper moduleWrapper
def getValue():
try:
return module.value
except:
return None
Or you can set your default value that you want and then you can set this function as param.
import moduleWrapper
def function(param=moduleWrapper.getValue()):
pass
This way if module change, you can still get your code work.

Mocking inherited methods

Please forgive my noob status, but I have come across a construct I don't really understand and hope someone can explain it for me.
class Base(object):
def mogrify(self, column):
return self.mogrifiers.get(column.lower().strip()) or (lambda x: x)
...
class MyClass(some.package.Base):
def mogrifiers(self):
return {
'column1': (lambda x: datetime.datetime.fromtimestamp(int(x)))
}
...
class MyOtherClass(object):
def convert_columns:
...
new_row[colkey] = self.myclass.mogrify(colkey)(value)
This all works, but I'm trying to write a unit test and mock out MyClass.
As far as I can tell, mogrifiers returns a dictionary of all the columns and any transformations that are required.
The code I am testing calls mogrify (inherited from the Base class) with a specific column name in a string.
This tries to extract the column from the dictionary and returns the lambda function ? or if it doesn't exist in the dictionary, it returns a lambda that just gives the string back ?
So that just leaves me with the (value) bit in the code I'm trying to test. It's no clear what it does.
If I don't want to test the underlying conversion/transformation my mock could just return the simple lambda.
So I've done that, but it throws an exception on the call to mogrify saying:
E TypeError: 'str' object is not callable
Can anyone provide some clues what I'm missing here?
As far as I can tell, mogrifiers returns a dictionary of all the
columns and any transformations that are required.
That is correct, though as you've shown it it will create a fresh dictionary each time which seems unnecessary.
The code I am testing calls mogrify (inherited from the Base class)
with a specific column name in a string.
This tries to extract the column from the dictionary and returns the
lambda function ? or if it doesn't exist in the dictionary, it returns
a lambada that just gives the string back ?
Yes, that is also correct (except that a lambada is a dance, but I think you meant lambda again).
So that just leaves me with the (value) bit in the code I'm trying to
test. It's no clear what it does.
The call self.myclass.mogrify(colkey) returns a callable, the (value) simply calls it. It may be clearer if I rewrite like this:
fn = self.myclass.mogrify(colkey)
new_row[colkey] = fn(value)
splitting it into two lines will also make it clearer whether the problem is with the call self.myclass.mogrify(colkey) or fn(value). If as seems likely it is the fn(value) call it means your mocked mogrify is returning a str instead of returning a callable; it could however be that you got the mock wrong and the mocked mogrify method is actually a string.
I would suggest you rewrite as shown and also insert a print between the two lines and see what is actually being returned.

returning functions from a method in python

I have tried looking into the documentation and google search , but I am unable to find out the significance of the [clazz] at the end of method. Could someone help me understand the meaning of the [clazz] at the end of the method? Thanks.
def get_context_setter(context, clazz):
return {
int: context.setFieldToInt,
datetime: context.setFieldToDatetime
}[clazz]
setFieldToInt and setFieldToDatetime are methods inside context class.
This function returns one of two things. It returns either context.setFieldToInt or context.setFieldToDatetime. It does so by using a dictionary as what would be a switch statement in other programming languages.
It checks whether clazz is a reference to the class int or a reference to the class datetime, and then returns the appropriate method.
It's identical to this code:
def get_context_setter(context, clazz):
lookup_table = {int: context.setFieldToInt,
datetime: context.setFieldToDatetime
}
context_function = lookup_table[clazz] # figure out which to return
return context_function
Using a dict instead of a switch statement is pretty popular, see Replacements for switch statement in Python? .
More briefly.
The code presented is expecting the class of some object as a parameter poorly named as clazz.
It's then using that class as a dictionary key.
They're essentially trying to accept two different types and call a method on the object type.
class is a keyword in Python.
The author of the code you show chose to use a strange spelling instead of a longer snake_case parameter name like obj_class.
The parameters really should have been named obj, obj_class
Or
instance, instance_class
Even better, the class really need not be a separate parameter.

Executing code in a python module as soon as it is included

I am attempting to use a module called interface.py which defines a list of conditions and a few functions to check arguments against those conditions. There are many thousands of conditions however, and so I want to use a dictionary instead of a list to prevent needing to look at all of them. To do this I'm using the following code:
def listToDictionary(list):
"""This function takes a list of conditions and converts it to a dictionary
that uses the name of the condition as a key."""
d = {}
for condition in list:
if condition.name.lower() not in d:
d[condition.name.lower()] = []
d[condition.name.lower()].append(condition)
return d
conditionList = listToDictionary(conditions.list) #the condition list comes from another module
Further into the file are the actual interface functions that take arguments to compare with the list of conditions - these functions are written assuming that conditionList will be a dictionary.
Unfortunately this isn't working. Giving error details is difficult because this code is being imported by a django page and I am trying to avoid talking about django so this question stays uncomplicated. Essentially the pages including this code will not load, and if I change it back to just using a list everything works fine.
My suspicion is that the problem has to do with how Python treats import statements. I need the listToDictionary conversion to run as soon as interface.py is imported, otherwise the interface functions will expect a dictionary and get a list instead. Is there any way to ensure that this is happening?
An educated guess: the list in conditions.list is not yet fully constructed when your module is being imported. As a result, you get a dictionary that is missing some entries or even empty, which is causing problems later. Try deferring the construction of the dict, like this:
conditionTable = None # shouldn't call it list if it's a dict
def get_cond_table():
global conditionTable
if conditionTable is None:
conditionTable = listToDictionary(conditions.list)
return conditionTable
Instead of referring to conditionList in your functions, refer to get_cond_table().
Alright, I found out that the problem was in another function that was still expecting the dictionary to be a list. The reason I couldn't see it right away is that Django left a very cryptic error message. I was able to get a better one using python manage.py shell and importing the module manually.
Thanks for your help everyone.

Categories