Prevent function modifcation after import in Python - python

I have created a Python package, let's call it 'mypackage'. There is a function in that package that does a check depending on a environment variable, and returns a boolean. That function is used in another function. Here is an example :
def check():
if env_var == "value":
return True
return False
# check is used in foo
def foo():
if check():
raise PermissionError()
else:
return "foo"
My problem here is that the check function can be overwritten just by doing this
import mypackage
def other_func():
return False
mypackage.check = other_func
How can I prevent a user from doing so, or make the "check" function unmodifiable ?

Related

How to verify whether a function has been hit with pytest

I have tried this:
def test_send_confirm_hit(monkeypatch):
hit = False
def called():
global hit
hit = True
monkeypatch.setattr("web.email.send_confirm", called)
# ... some event that will cause web.email.send_confirm to be hit
assert hit # verify send_confirm was hit
Which appears to work although I would rather not use a global variable. What is the best way to do this?
If you use a "proper" mock, it comes with an .assert_called() method:
import unittest.mock
def test_send_confirm_hit(monkeypatch):
mock_send_confirm = unittest.mock.Mock()
monkeypatch.setattr("web.email.send_confirm", mock_send_confirm)
# ... some event that will cause web.email.send_confirm to be hit
mock_send_confirm.assert_called()
You could use a Mock instead of the function:
from unittest.mock import MagicMock
def test_send_confirm_hit(monkeypatch):
called = MagicMock()
monkeypatch.setattr("web.email.send_confirm", called)
# ... some event that will cause web.email.send_confirm to be hit
assert called.call_count == 1 # verify send_confirm was hit
If you really need to perform some custom logic, like you would do with a function, you can add a side_effect to the mock:
def test_send_confirm_hit(monkeypatch):
def side_effect():
return 42
called = MagicMock(side_effect=side_effect)

name not defined in python module

I see python throwing the below error
"errorMessage": "name 'get_client' is not defined",
Below is my code
get_client()
def get_client():
# optional fields
level = 'INFO'
verify = True
You are trying to call or execute the function before it was defined.
Try to alter the order of the 2 parts:
def get_client():
# optional fields
level = 'INFO'
verify = True
get_client()
Does that work for you?
Code is read from top to bottom by the python internpreter; at this point the functions that are defined are registered in the global namespace, but the content of those functions does not get executed yet (just syntax checking is done) unless they are specifically invoked.
For example:
def f1():
print('1')
print('A')
f1()
print('B')
def f2():
print('2')
Here f1() is registered and then it is executed even before f2() has been registered in the namespace. The output of running the example would be:
A
1
B
f2() never gets called in this code, so its print statement never writes anything to the output.
You are calling on a function before you have defined it.
Try:
def get_client():
# optional fields
level = 'INFO'
verify = True
get_client()
Python interpreter reads from top to bottom. Thus, you need to call your function AFTER you have defined it:
def get_client():
# optional fields
level = 'INFO'
verify = True
get_client()

Call a Function by alias in a Decorator

The current code I have, allows the function to call the wrapper decorator, and uses the function name in its code. However, I'm looking for a way to give the function a 'alias' in a way as an argument. Here's the current code:
import os, sys
# Take user input
message = input('type command: ')
# Command wrapper
ALLCOMMANDS = {}
def command(function):
ALLCOMMANDS[function.__name__] = function
return function
# Commands
#command
def foo():
print("bar")
#command
def goo():
print('ber')
# Run appropriate command
if message in ALLCOMMANDS:
ALLCOMMANDS[message]()
For example I would want to be able to call the function by a name such as '!foo' from the user input, so maybe the argument would look like #command(name='!foo'), I just don't know where to go from there to use that argument in the decorator since it already has an argument.
I attempted
# Command wrapper
ALLCOMMANDS = {}
def command(name):
ALLCOMMANDS[name] = name
return name
but keep getting errors and I assume I am missing something
You should read up a bit more on python decorators. You're getting an error with:
def command(name):
ALLCOMMANDS[name] = name
return name
Because of the return name.
Decorators are just syntactic sugar. This:
#command
def foo():
print('bar')
Is equivalent to:
def foo():
print('bar')
foo = command(foo)
From this you can see why your original decorator works. At the end you return function.
Things get a little tricker when you have a decorator that takes arguments. Desugared the following:
#command('nickname')
def foo():
print('bar')
Looks like this:
def foo():
print('bar')
foo = command('nickname')(foo)
So, to write a decorator that takes arguments, the decorator needs to return a function that takes the function to decorate as an argument:
def command(nickname):
def wrapped(f):
ALLCOMMANDS[nickname] = f
return f
return wrapped
Also consider making ALLCOMMANDS an attribute on your command instead of a global (UPPER_SNAKE is usually reserved for constants):
def command(nickname):
def wrapped(f):
command._functions[nickname] = f
return f
return wrapped
command._functions = {}

Python, conditional use of a decorator (package not installed in all env)

I am using New Relic to monitor my Python (2.7) app, but only in my Production environment. I want to use their function_trace decorator.
In wsgi.py
try:
import newrelic.agent
newrelic.agent.initialize('/path/')
except Exception as e:
pass
In views.py
if settings.ENV == "test":
pass
else:
import newrelic
#newrelic.agent.function_trace()
def my_function():
....
This works great in Production, but fails in Test, of course. I can't put if-else directly around a decorator so I was thinking that I could use a conditional decorator:
def newrelic_conditional_decorator(function)
# What magic goes in here...
if settings.ENV == "test":
just return function
else:
use #newrelic.agent.function_trace() decorator
#newrelic_conditional_decorator
def my_function():
....
I'm always a bit hazy on decorators so am hoping for some help here! (Or another way to handle not having the same package in Test as in Production.)
A decorator takes in a function and returns a new function from it. So if you want a conditional decorator, all you need is to return the initial function when you do not want the decorator to be applied.
Your specific case
def newrelic_conditional_decorator(function):
if settings.ENV == "test":
# We do not apply the decorator to the function
return function
else:
# We apply the decorator and return the new function
return newrelic.agent.function_trace()(function)
#newrelic_conditional_decorator
def my_function():
...
A bit of generality
You could abstract this to make a more general conditional decorator which applies a given generator provided some condition function returns True.
def conditional_decorator(decorator, condition, *args):
def wrapper(function):
if condition(*args):
return decorator(function)
else:
return function
return wrapper
#conditional_decorator(
newrelic.agent.function_trace(),
lambda settings: settings.ENV != "test",
settings)
def my_function():
...

Preventing function (or decorator) from being nested

I've got some code in a decorator that I only want run once. Many other functions (utility and otherwise) will be called later down the line, and I want to ensure that other functions that may have this decorator aren't accidentally used way down in the nest of function calls.
I also want to be able to check, at any point, whether or not the current code has been wrapped in the decorator or not.
I've written this, but I just wanted to see if anyone else can think of a better/more elegant solution than checking for the (hopefully!) unique function name in the stack.
import inspect
def my_special_wrapper(fn):
def my_special_wrapper(*args, **kwargs):
""" Do some magic, only once! """
# Check we've not done this before
for frame in inspect.stack()[1:]: # get stack, ignoring current!
if frame[3] == 'my_special_wrapper':
raise StandardError('Special wrapper cannot be nested')
# Do magic then call fn
# ...
fn(*args, **kwargs)
return my_special_wrapper
def within_special_wrapper():
""" Helper to check that the function has been specially wrapped """
for frame in inspect.stack():
if frame[3] == 'my_special_wrapper':
return True
return False
#my_special_wrapper
def foo():
print within_special_wrapper()
bar()
print 'Success!'
#my_special_wrapper
def bar():
pass
foo()
Here is an example of using a global for this task - in what I believe is a relatively safe way:
from contextlib import contextmanager
from functools import wraps
_within_special_context = False
#contextmanager
def flag():
global _within_special_context
_within_special_context = True
try:
yield
finally:
_within_special_context = False
#I'd argue this would be best replaced by just checking the variable, but
#included for completeness.
def within_special_wrapper():
return _within_special_context
def my_special_wrapper(f):
#wraps(f)
def internal(*args, **kwargs):
if not _within_special_context:
with flag():
...
f(*args, **kwargs)
else:
raise Exception("No nested calls!")
return internal
#my_special_wrapper
def foo():
print(within_special_wrapper())
bar()
print('Success!')
#my_special_wrapper
def bar():
pass
foo()
Which results in:
True
Traceback (most recent call last):
File "/Users/gareth/Development/so/test.py", line 39, in <module>
foo()
File "/Users/gareth/Development/so/test.py", line 24, in internal
f(*args, **kwargs)
File "/Users/gareth/Development/so/test.py", line 32, in foo
bar()
File "/Users/gareth/Development/so/test.py", line 26, in internal
raise Exception("No nested calls!")
Exception: No nested calls!
Using a context manager ensures that the variable is unset. You could just use try/finally, but if you want to modify the behaviour for different situations, the context manager can be made to be flexible and reusable.
The obvious solution is to have special_wrapper set a global flag, and just skip its magic if the flag is set.
This is about the only good use of a global variable - to allow a single piece of code to store information that is only used within that code, but which needs to survive the life of execution in that code.
It doesn't need to be set in global scope. The function could set the flag on itself, for example, or on any object or class, as long as nothing else will touch it.
As noted by Lattyware in comments, you'll want to use either a try/except, or perhaps even better, a context manager to ensure the variable is unset.
Update: If you need the wrapped code to be able to check if it is wrapped, then provide a function which returns the value of the flag. You might want to wrap it all up with a class for neatness.
Update 2: I see you're doing this for transaction management. There are probably already libraries which do this. I strongly recommend that you at least look at their code.
While my solution technically works, it requires a manual reset of the decorator, but you could very well modify things such that the outermost function is instead a class (with the instances being the wrappers of the decorated functions passed to it in __init__), and have reset() being called in __exit__(), which would then allow you to use the with statement to create the decorator to be usable only once within the context. Also note that it requires Python 3 due to the nonlocal keyword, but that can easily be adapted to 2.7 with a dict in place of the flag variable.
def once_usable(decorator):
"Apply this decorator function to the decorator you want to be usable only once until it is reset."
def outer_wrapper():
flag = False
def inner_wrapper(*args, **kwargs):
nonlocal flag
if not flag:
flag = True
return decorator(*args, **kwargs)
else:
print("Decorator currently unusable.") # raising an Error also works
def decorator_reset():
nonlocal flag
flag = False
return (inner_wrapper, decorator_reset)
return outer_wrapper()
Testing:
>>> def a(aa):
return aa*2
>>> def b(bb):
def wrapper(*args, **kwargs):
print("Decorated.")
return bb(*args, **kwargs)
return wrapper
>>> dec, reset = once_usable(b)
>>> aa = dec(a)
>>> aa(22)
Decorated.
44
>>> aaa = dec(a)
Decorator currently unusable.
>>> reset()
>>> aaa = dec(a)
>>> aaa(11)
Decorated.
22

Categories

Resources