Is it possible to mock the string module from Python? - python

For instance, if I have a call to the split method (i.e. some_string.split(":") )
Is is possible to mock this. I wanted to assert that the split function is called using assert_called_once_with

I confirm you can't do that because split() is a built-in attribute of str object and you can't set attributes of built-in or extension because they are readonly.
Below some inconclusive tests after trying into a Python 2.7.10 interpreter
>>> __builtins__.str.split
<method 'split' of 'str' objects>
>>> type(__builtins__.str.split)
<type 'method_descriptor'>
Trying to override it using a function
>>> type(lambda f:f)
<type 'function'>
>>> __builtins__.str.split = lambda f: f
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can't set attributes of built-in/extension type 'str'
Trying to override it using a callable (function or method)
>>> type(callable)
<type 'builtin_function_or_method'>
>>> __builtins__.str.split = callable
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can't set attributes of built-in/extension type 'str'
After having a look more deeply into the CPython source code here [1]. It's a limitation in Objects/typeobject.c introduce by the function list below. This function check if we try to set a readonly attribute and raise TypeError.
type_setattro(PyTypeObject *type, PyObject *name, PyObject *value)
{
if (!(type->tp_flags & Py_TPFLAGS_HEAPTYPE)) {
PyErr_Format(
PyExc_TypeError,
"can't set attributes of built-in/extension type '%s'",
type->tp_name);
return -1;
}
if (PyObject_GenericSetAttr((PyObject *)type, name, value) < 0)
return -1;
return update_slot(type, name);
}
[1] https://hg.python.org/cpython/file/tip/Objects/typeobject.c#l3022

Yes it is with a couple of caviats.
In my case I have successfully mocked str in python3 so I can assert that split is being called with a specific input
There are two caviats
With patch, I replaced the original str class with a new class that inherits from str
In the code that I was testing, I had to do a redundant string casting like str(str_val).split
Here's how one can do it:
class MyStr(str):
def split(self, sep=None, maxsplit=-1)):
expected_str = "some_input_mutated_inside_fn_before_split_called"
self.assertEqual(self, expected_str)
return super().split(sep=sep, maxsplit=maxsplit)
with patch('mymodule.str', new=MyStr):
output = mymodule.function_that_calls_string_split(
"some_input"
)

Related

Is everything in Python castable to a string?

I'm trying to find an example of something in Python that can't be cast to a string.
>>> str(None)
'None'
>>> str(False)
'False'
>>> str(5)
'5'
>>> str(object)
"<class 'object'>"
>>> class Test:
... pass
...
>>> str(Test)
"<class '__main__.Test'>"
>>> str(Test())
'<__main__.Test object at 0x7f7e88a13630>'
Is there anything the entire Python universe that cannot be cast to str?
From the __str__ documentation:
The default implementation defined by the built-in type object
calls object.__repr__().
and object.__repr__ prints object name and address (at least in cpython). That's where your output '<__main__.Test object at 0x7f7e88a13630>' comes from. A class would have to override __str__ and raise an exception (or have a bug) to fail. There is little reason to do this and you'd be hard-pressed to find one that wasn't built to purpose.
Is everything in Python castable to a string?
Nope!
>>> class MyObject():
... def __str__(self):
... raise NotImplementedError("You can't string me!")
...
>>> str(MyObject())
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in __str__
NotImplementedError: You can't string me!

How to get a function's name as string?

In Python, how do I get a function's name as a string?
I want to get the name of the str.capitalize() function as a string. It appears that the function has a __name__ attribute. When I do
print str.__name__
I get this output, as expected:
str
But when I run str.capitalize().__name__ I get an error instead of getting the name "capitalize".
> Traceback (most recent call last):
> File "string_func.py", line 02, in <module>
> print str.capitalize().__name__
> TypeError: descriptor 'capitalize' of 'str' object needs an argument
Similarly,
greeting = 'hello, world'
print greeting.capitalize().__name__
gives this error:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute '__name__'
What went wrong?
greeting.capitalize is a function object, and that object has a .__name__ attribute that you can access. But greeting.capitalize() calls the function object and returns the capitalized version of the greeting string, and that string object doesn't have a .__name__ attribute. (But even if it did have a .__name__, it'd be the name of the string, not the name of the function used to create the string). And you can't do str.capitalize() because when you call the "raw" str.capitalize function you need to pass it a string argument that it can capitalize.
So you need to do
print str.capitalize.__name__
or
print greeting.capitalize.__name__
Let's start from the error
Traceback (most recent call last):
File "", line 1, in
AttributeError: 'str' object has no attribute 'name'
Specific
AttributeError: 'str' object has no attribute 'name'
You are trying
greeting = 'hello, world'
print greeting.capitalize().__name__
Which will capitalize hello world and return it as a string.
As the error states, string don't have attribute _name_
capitalize() will execute the function immediately and use the result whereas capitalize will represent the function.
If you want to see a workaround in JavaScript,
Check the below snippet
function abc(){
return "hello world";
}
console.log(typeof abc); //function
console.log(typeof abc());
So, don't execute.
Simply use
greeting = 'hello, world'
print greeting.capitalize.__name__
You don't need to call this function and simply use name
>>> str.capitalize.__name__

Object Location Change on Blender Python

I was following an animation example with Python in Blender 2.69, by typing a line by line.
obj = bpy.context.object
obj.location[2] = 0.0
obj.keyframe_insert(data_path="location", frame=10.0, index=2)
obj.location[2] = 1.0
obj.keyframe_insert(data_path="location", frame=20.0, index=2)
But I have encountered an error on the 3rd line, which is saying
Traceback (most recent call last):
File "<blender_console>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'location'
I am confused because I just followed a simple example.
Why is it saying the object has no attribute 'location'?
I'll be appreciated for your help, thanks.
You'll find that the error would be reported after the second line because the variable obj has not been set. Most likely this would be from a small typo.
You can verify this by looking at the type of the variable in the python console. When getting the error you will see -
>>> type(obj)
<class 'NoneType'>
While if it had been set correctly you will get -
>>> type(obj)
<class 'bpy_types.Object'>

implementing a deferred exception in Python

I would like to implement a deferred exception in Python that is OK to store somewhere but as soon as it is used in any way, it raises the exception that was deferred. Something like this:
# this doesn't work but it's a start
class DeferredException(object):
def __init__(self, exc):
self.exc = exc
def __getattr__(self, key):
raise self.exc
# example:
mydict = {'foo': 3}
try:
myval = obtain_some_number()
except Exception as e:
myval = DeferredException(e)
mydict['myval'] = myval
def plus_two(x):
print x+2
# later on...
plus_two(mydict['foo']) # prints 5
we_dont_use_this_val = mydict['myval'] # Always ok to store this value if not used
plus_two(mydict['myval']) # If obtain_some_number() failed earlier,
# re-raises the exception, otherwise prints the value + 2.
The use case is that I want to write code to analyze some values from incoming data; if this code fails but the results are never used, I want it to fail quietly; if it fails but the results are used later, then I'd like the failure to propagate.
Any suggestions on how to do this? If I use my DeferredException class I get this result:
>>> ke = KeyError('something')
>>> de = DeferredException(ke)
>>> de.bang # yay, this works
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 6, in __getattr__
KeyError: 'something'
>>> de+2 # boo, this doesn't
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'DeferredException' and 'int'
Read section 3.4.12 of the docs, "Special method lookup for new-style classes." It explains exactly the problem you have encountered. The normal attribute lookup is bypassed by the interpreter for certain operators, such as addition (as you found out the hard way). Thus the statement de+2 in your code never calls your getattr function.
The only solution, according to that section, is to insure that "the special method must be set on the class object itself in order to be consistently invoked by the interpreter."
Perhaps you'd be better off storing all your deferred exceptions in a global list, wrapping your entire program in a try:finally: statement, and printing out the whole list in the finally block.

How do I properly use Python's C API and exceptions?

if I do something like
>>> x = int(1,2,3,4,5)
I immediately get a fatal error (one that would end program execution if it was in a pre-written script)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: int() takes at most 2 arguments (5 given)
and x remains undefined:
>>> x
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined
How would I go about implementing that in Python's C API? I found some documentation for it, but I am not sure that I know how to use it correctly.
Here is what I have been trying:
Print:
if(something) {
PyErr_SetString(PyExc_TypeError, "Oh no!");
PyErr_Print();
}
This, unfortunately, only prints the exception and the program continues. Additionally,—if I understand it correctly—PyErr_Print() removes the exception from some sort of queue so Python thinks that it is handled. This is what it looks like:
>>> import awesomemod
>>> x = awesomemod.thing()
TypeError: Oh no!
>>> x # x is defined because the function returns None eventually
>>>
PyErr_Occurred():
if(something) {
PyErr_SetString(PyExc_TypeError, "Oh no!");
PyErr_Occurred();
}
Behavior:
>>> import awesomemod
>>> awesomemod.thing()
>>>
TypeError: Oh no!
>>>
So it does it kind of late...
return PyErr_Occurred():
if(something) {
PyErr_SetString(PyExc_TypeError, "Oh no!");
return PyErr_Occurred();
}
Behavior:
>>> import awesomemod
>>> awesomemod.thing()
<type 'exceptions.TypeError'>
>>>
TypeError: Oh no!
This one is just really weird.
What do I need to do to get the behavior of built-in functions?
Edit: I tried what #user2864740 suggested in a comment and it worked perfectly!
if(something) {
PyErr_SetString(PyExc_TypeError, "Oh no!");
return (PyObject *) NULL;
}
Raising an exception in C is done by setting the exception object or string and then returning NULL from the function.
As Ignacio Vazquez-Abrams said:
Raising an exception in C is done by setting the exception object or string and then returning NULL from the function.
There are convenience functions which make this easy to do for common exception types. For example, PyErr_NoMemory can be used like this:
PyObject *my_function(void)
{
return PyErr_NoMemory(); // Sets the exception and returns NULL
}

Categories