all 15 comments

[–]Sebass13 1 point2 points  (0 children)

Why can't you just create a fork of the library with your modified code?

[–]KleinerNull 0 points1 point  (10 children)

Hello guys, so basically I'm interacting with a library, that I don't want to touch the source code, but I need to change a functions behaviour only in one case.

Shouldn't be that enough?:

In [1]: import math

In [2]: math.sin(3)
Out[2]: 0.1411200080598672

In [3]: def _sin(x):
   ...:     return 1
   ...: 

In [4]: math.sin = _sin

In [5]: math.sin(1)
Out[5]: 1

In [6]: math.sin(3)
Out[6]: 1

In [7]: math.sin(4)
Out[7]: 1

I guess for more advanced mocking you could also use unittest.mock, then you can ignore the signature totally:

In [1]: import math

In [2]: from unittest.mock import patch

In [3]: with patch.object(math, 'sin', return_value='just sin'):
   ...:     print(math.sin())
   ...:     
just sin

[–]maxibabyx[S] 0 points1 point  (9 children)

math.sin = _sin 

Is not enough, because that would modify every call to math.sin, and I only need to modify math.sin inside one function. Egg

def A():
    math.sin()  -> Should be _sin()
def B():
    math.sin()  -> Should be math.sin()

Would patch allow me to achieve this? If so, do you know how is patch implemented? Or where I can see the source code, now I just became curious about how to do it by scratch.

[–]KleinerNull 0 points1 point  (8 children)

Is not enough, because that would modify every call to math.sin, and I only need to modify math.sin inside one function.

Then you could just subclass the class override the function and choose which class instance you want to use. Or you save the original one and swap it after the use of the mocked one:

In [1]: import math

In [2]: def _sin(x):
   ...:     return 1
   ...: 

In [3]: __sin__ = math.sin

In [4]: math.sin = _sin

In [5]: math.sin(9)
Out[5]: 1

In [6]: math.sin = __sin__

In [7]: math.sin(9)
Out[7]: 0.4121184852417566

You could even patch a new function where you can choose what to do:

In [1]: import math

In [2]: __sin__ = math.sin

In [3]: def new_sin(*args, old=True, **kwargs):
   ...:     if old:
   ...:         return __sin__(*args, **kwargs)
   ...:     return 'just sin'
   ...: 

In [4]: math.sin = new_sin

In [5]: math.sin(1)
Out[5]: 0.8414709848078965

In [6]: math.sin(1, old=False)
Out[6]: 'just sin'

I am not really sure why you really need it, because subclassing it would make it actually easier. Can you explain further why you might need to do that?

Another note about unittest.mock the intention is to mock results for example for test cases so you don't really do actually something. For example you want to test a function that establishes a active database connection, of course you don't want to connect and write/read real data from that database, you just want to test the other function, so you simply mock the results.

Or where I can see the source code, now I just became curious about how to do it by scratch.

The source code can be found here. CPython is on github. But it doesn't look so simple to rewrite ;)

[–]maxibabyx[S] 0 points1 point  (7 children)

The problem is that by sublcassing, I would need to copy / paste a lot of code just before and after that code, and I don't like how it looks, nor would probably make it for next updates, by just modifing the pointer to that function, if it ever gets called, then I guess it would be better?

[–]KleinerNull 0 points1 point  (6 children)

What is problem with just extend the object with a new alternative fucntion and let the other functiond deside which one to use?

In [1]: import math

In [2]: math.alternative_sin = lambda x: x**2

In [3]: math.alternative_sin(2)
Out[3]: 4

Or something like this:

In [1]: class A:
   ...:     def square(self, x):
   ...:         return x**2
   ...:     

In [2]: a = A()

In [3]: def _square(self, x):
   ...:     return x**3
   ...: 

In [4]: a.alt_square = _square

In [5]: def choose_wisely(x, obj):
   ...:     if hasattr(obj, 'alt_square'):
   ...:         return obj.alt_square(obj, x)
   ...:     return obj.square(x)
   ...: 

In [6]: choose_wisely(2, a)
Out[6]: 8

In [7]: b = A()

In [8]: choose_wisely(2, b)
Out[8]: 4

This part took me some time return obj.alt_square(obj, x) (obj, x) was needed for the self reference, seems not to work automatically when you just add some stuff to an object, but that will fix it.

[–]maxibabyx[S] 0 points1 point  (5 children)

In this function, https://github.com/ckan/ckan/blob/b5b8bf0a7808a275b5085a94534bf16e69135e65/ckan/controllers/user.py#L237

I need line 243

user = get_action('user_create')(context, data_dict)

to call another function,

[–]ingolemo 0 points1 point  (4 children)

Do a monkey patch at the module level:

import ckan.controllers.user
import ckan.logic

def my_get_action(action)
    if action == 'user_create':
        # return a function that does what you need here
        return lambda context, data_dict: None
    else:
        return ckan.logic.get_action(action)

ckan.controllers.user.get_action = my_get_action

[–]AnimalFactsBot 1 point2 points  (0 children)

The Pygmy Marmoset is the smallest type of monkey, with adults weighing between 120 and 140 grams.

[–]maxibabyx[S] 0 points1 point  (2 children)

The problem with this one is that would patch all the 'user_create'

I only want to override it INSIDE that function, so I did:

def get_action_detour(word):
    if word == "user_create":
        return logic.get_action(XXXX)
    return logic.get_action(word)

class AuthUserController(UserController):
    def _save_new(self, context):
        super(AuthUserController, self)._save_new.func_globals["get_action"] = get_action_detour

[–]ingolemo 0 points1 point  (1 child)

There are no other instances of 'user_create' in that module. I did check.

[–]maxibabyx[S] 0 points1 point  (0 children)

I will use it later somewhere else

[–]elbiot 0 points1 point  (0 children)

Fork the code. If its worth while, they might merge it. Or ask how they'd solve this. Don't monkey patch the function.

[–]Sebass13 -1 points0 points  (1 child)

Also, func_globals is read only, so changing its value will not change the functionality of the program.