Decorator which skips `self` for methods
up vote
0
down vote
favorite
Let's say we have multiple functions which all accept an URL as their first argument and this URL needs to be validated. This can be nicely solved with a decorator
def validate_url(f):
def validated(url, *args, **kwargs):
assert len(url.split('.')) == 3 # trivial example
return f(url, *args, **kwargs)
return validated
@validate_url
def some_func(url, some_other_arg, *some_args, **some_kwargs):
pass
This approach will work and allow me to factor the validation behavior out of many instances of similar functions. But now I would want to write a class method which also takes a validated URL. However, the naive approach will not work
class SomeClass:
@validate_url
def some_method(self, url, some_other_args):
pass
because we will end up attempting to validate self
and not url
. My question is how to write a single decorator which will work for both functions and methods with the minimum amount of boilerplate.
Note 1: I am aware why this happens, it's just that I don't know how to work around this in the most elegant manner.
Note 2: The URL validation problem is just an example, so checking if isinstance(args[0], str)
is not a good solution.
python
add a comment |
up vote
0
down vote
favorite
Let's say we have multiple functions which all accept an URL as their first argument and this URL needs to be validated. This can be nicely solved with a decorator
def validate_url(f):
def validated(url, *args, **kwargs):
assert len(url.split('.')) == 3 # trivial example
return f(url, *args, **kwargs)
return validated
@validate_url
def some_func(url, some_other_arg, *some_args, **some_kwargs):
pass
This approach will work and allow me to factor the validation behavior out of many instances of similar functions. But now I would want to write a class method which also takes a validated URL. However, the naive approach will not work
class SomeClass:
@validate_url
def some_method(self, url, some_other_args):
pass
because we will end up attempting to validate self
and not url
. My question is how to write a single decorator which will work for both functions and methods with the minimum amount of boilerplate.
Note 1: I am aware why this happens, it's just that I don't know how to work around this in the most elegant manner.
Note 2: The URL validation problem is just an example, so checking if isinstance(args[0], str)
is not a good solution.
python
Not sure whether this can be applied here
– Abdul Niyas P M
Sep 18 at 17:28
Could you define a@staticmethod
likedef validate_url(url, *args)
that runs inside ofsome_func
? You could then decoratevalidate_url
. Seems hacky and non-Pythonic, but that's the first thing I'd try
– C.Nivs
Sep 18 at 19:27
add a comment |
up vote
0
down vote
favorite
up vote
0
down vote
favorite
Let's say we have multiple functions which all accept an URL as their first argument and this URL needs to be validated. This can be nicely solved with a decorator
def validate_url(f):
def validated(url, *args, **kwargs):
assert len(url.split('.')) == 3 # trivial example
return f(url, *args, **kwargs)
return validated
@validate_url
def some_func(url, some_other_arg, *some_args, **some_kwargs):
pass
This approach will work and allow me to factor the validation behavior out of many instances of similar functions. But now I would want to write a class method which also takes a validated URL. However, the naive approach will not work
class SomeClass:
@validate_url
def some_method(self, url, some_other_args):
pass
because we will end up attempting to validate self
and not url
. My question is how to write a single decorator which will work for both functions and methods with the minimum amount of boilerplate.
Note 1: I am aware why this happens, it's just that I don't know how to work around this in the most elegant manner.
Note 2: The URL validation problem is just an example, so checking if isinstance(args[0], str)
is not a good solution.
python
Let's say we have multiple functions which all accept an URL as their first argument and this URL needs to be validated. This can be nicely solved with a decorator
def validate_url(f):
def validated(url, *args, **kwargs):
assert len(url.split('.')) == 3 # trivial example
return f(url, *args, **kwargs)
return validated
@validate_url
def some_func(url, some_other_arg, *some_args, **some_kwargs):
pass
This approach will work and allow me to factor the validation behavior out of many instances of similar functions. But now I would want to write a class method which also takes a validated URL. However, the naive approach will not work
class SomeClass:
@validate_url
def some_method(self, url, some_other_args):
pass
because we will end up attempting to validate self
and not url
. My question is how to write a single decorator which will work for both functions and methods with the minimum amount of boilerplate.
Note 1: I am aware why this happens, it's just that I don't know how to work around this in the most elegant manner.
Note 2: The URL validation problem is just an example, so checking if isinstance(args[0], str)
is not a good solution.
python
python
edited Nov 21 at 21:32
asked Sep 18 at 17:23
Jatentaki
818
818
Not sure whether this can be applied here
– Abdul Niyas P M
Sep 18 at 17:28
Could you define a@staticmethod
likedef validate_url(url, *args)
that runs inside ofsome_func
? You could then decoratevalidate_url
. Seems hacky and non-Pythonic, but that's the first thing I'd try
– C.Nivs
Sep 18 at 19:27
add a comment |
Not sure whether this can be applied here
– Abdul Niyas P M
Sep 18 at 17:28
Could you define a@staticmethod
likedef validate_url(url, *args)
that runs inside ofsome_func
? You could then decoratevalidate_url
. Seems hacky and non-Pythonic, but that's the first thing I'd try
– C.Nivs
Sep 18 at 19:27
Not sure whether this can be applied here
– Abdul Niyas P M
Sep 18 at 17:28
Not sure whether this can be applied here
– Abdul Niyas P M
Sep 18 at 17:28
Could you define a
@staticmethod
like def validate_url(url, *args)
that runs inside of some_func
? You could then decorate validate_url
. Seems hacky and non-Pythonic, but that's the first thing I'd try– C.Nivs
Sep 18 at 19:27
Could you define a
@staticmethod
like def validate_url(url, *args)
that runs inside of some_func
? You could then decorate validate_url
. Seems hacky and non-Pythonic, but that's the first thing I'd try– C.Nivs
Sep 18 at 19:27
add a comment |
1 Answer
1
active
oldest
votes
up vote
1
down vote
accepted
One solution would be to somehow detect whether the decorated function is a class method or not — which seems to be difficult if not impossible (as far as I can tell anyway) to do so cleanly. The inspect
module's ismethod()
and isfunction()
don't work inside a decorator used inside a class definition.
Given that, here's a somewhat hacky way of doing it which checks to see if the decorated callable's first argument has been given the name "self"
, which is the coding convention for it in class methods (although it is not a requirement, so caveat emptor and use at your own risk).
The following code seems to work in both Python 2 and 3. However in Python 3 it may raise DeprecationWarning
s depending on exactly what sub-version is being used—so they have been suppressed in a section of the code below.
from functools import wraps
import inspect
import warnings
def validate_url(f):
@wraps(f)
def validated(*args, **kwargs):
with warnings.catch_warnings():
# Suppress DeprecationWarnings in this section.
warnings.simplefilter('ignore', category=DeprecationWarning)
# If "f"'s first argument is named "self",
# assume it's a method.
if inspect.getargspec(f).args[0] == 'self':
url = args[1]
else: # Otherwise assume "f" is a ordinary function.
url = args[0]
print('testing url: {!r}'.format(url))
assert len(url.split('.')) == 3 # Trivial "validation".
return f(*args, **kwargs)
return validated
@validate_url
def some_func(url, some_other_arg, *some_args, **some_kwargs):
print('some_func() called')
class SomeClass:
@validate_url
def some_method(self, url, some_other_args):
print('some_method() called')
if __name__ == '__main__':
print('** Testing decorated function **')
some_func('xxx.yyy.zzz', 'another arg')
print(' URL OK')
try:
some_func('https://bogus_url.com', 'another thing')
except AssertionError:
print(' INVALID URL!')
print('n** Testing decorated method **')
instance = SomeClass()
instance.some_method('aaa.bbb.ccc', 'something else') # -> AssertionError
print(' URL OK')
try:
instance.some_method('foo.bar', 'arg 2') # -> AssertionError
except AssertionError:
print(' INVALID URL!')
Output:
** Testing decorated function **
testing url: 'xxx.yyy.zzz'
some_func() called
URL OK
testing url: 'https://bogus_url.com'
INVALID URL!
** Testing decorated method **
testing url: 'aaa.bbb.ccc'
some_method() called
URL OK
testing url: 'foo.bar'
INVALID URL!
add a comment |
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
1
down vote
accepted
One solution would be to somehow detect whether the decorated function is a class method or not — which seems to be difficult if not impossible (as far as I can tell anyway) to do so cleanly. The inspect
module's ismethod()
and isfunction()
don't work inside a decorator used inside a class definition.
Given that, here's a somewhat hacky way of doing it which checks to see if the decorated callable's first argument has been given the name "self"
, which is the coding convention for it in class methods (although it is not a requirement, so caveat emptor and use at your own risk).
The following code seems to work in both Python 2 and 3. However in Python 3 it may raise DeprecationWarning
s depending on exactly what sub-version is being used—so they have been suppressed in a section of the code below.
from functools import wraps
import inspect
import warnings
def validate_url(f):
@wraps(f)
def validated(*args, **kwargs):
with warnings.catch_warnings():
# Suppress DeprecationWarnings in this section.
warnings.simplefilter('ignore', category=DeprecationWarning)
# If "f"'s first argument is named "self",
# assume it's a method.
if inspect.getargspec(f).args[0] == 'self':
url = args[1]
else: # Otherwise assume "f" is a ordinary function.
url = args[0]
print('testing url: {!r}'.format(url))
assert len(url.split('.')) == 3 # Trivial "validation".
return f(*args, **kwargs)
return validated
@validate_url
def some_func(url, some_other_arg, *some_args, **some_kwargs):
print('some_func() called')
class SomeClass:
@validate_url
def some_method(self, url, some_other_args):
print('some_method() called')
if __name__ == '__main__':
print('** Testing decorated function **')
some_func('xxx.yyy.zzz', 'another arg')
print(' URL OK')
try:
some_func('https://bogus_url.com', 'another thing')
except AssertionError:
print(' INVALID URL!')
print('n** Testing decorated method **')
instance = SomeClass()
instance.some_method('aaa.bbb.ccc', 'something else') # -> AssertionError
print(' URL OK')
try:
instance.some_method('foo.bar', 'arg 2') # -> AssertionError
except AssertionError:
print(' INVALID URL!')
Output:
** Testing decorated function **
testing url: 'xxx.yyy.zzz'
some_func() called
URL OK
testing url: 'https://bogus_url.com'
INVALID URL!
** Testing decorated method **
testing url: 'aaa.bbb.ccc'
some_method() called
URL OK
testing url: 'foo.bar'
INVALID URL!
add a comment |
up vote
1
down vote
accepted
One solution would be to somehow detect whether the decorated function is a class method or not — which seems to be difficult if not impossible (as far as I can tell anyway) to do so cleanly. The inspect
module's ismethod()
and isfunction()
don't work inside a decorator used inside a class definition.
Given that, here's a somewhat hacky way of doing it which checks to see if the decorated callable's first argument has been given the name "self"
, which is the coding convention for it in class methods (although it is not a requirement, so caveat emptor and use at your own risk).
The following code seems to work in both Python 2 and 3. However in Python 3 it may raise DeprecationWarning
s depending on exactly what sub-version is being used—so they have been suppressed in a section of the code below.
from functools import wraps
import inspect
import warnings
def validate_url(f):
@wraps(f)
def validated(*args, **kwargs):
with warnings.catch_warnings():
# Suppress DeprecationWarnings in this section.
warnings.simplefilter('ignore', category=DeprecationWarning)
# If "f"'s first argument is named "self",
# assume it's a method.
if inspect.getargspec(f).args[0] == 'self':
url = args[1]
else: # Otherwise assume "f" is a ordinary function.
url = args[0]
print('testing url: {!r}'.format(url))
assert len(url.split('.')) == 3 # Trivial "validation".
return f(*args, **kwargs)
return validated
@validate_url
def some_func(url, some_other_arg, *some_args, **some_kwargs):
print('some_func() called')
class SomeClass:
@validate_url
def some_method(self, url, some_other_args):
print('some_method() called')
if __name__ == '__main__':
print('** Testing decorated function **')
some_func('xxx.yyy.zzz', 'another arg')
print(' URL OK')
try:
some_func('https://bogus_url.com', 'another thing')
except AssertionError:
print(' INVALID URL!')
print('n** Testing decorated method **')
instance = SomeClass()
instance.some_method('aaa.bbb.ccc', 'something else') # -> AssertionError
print(' URL OK')
try:
instance.some_method('foo.bar', 'arg 2') # -> AssertionError
except AssertionError:
print(' INVALID URL!')
Output:
** Testing decorated function **
testing url: 'xxx.yyy.zzz'
some_func() called
URL OK
testing url: 'https://bogus_url.com'
INVALID URL!
** Testing decorated method **
testing url: 'aaa.bbb.ccc'
some_method() called
URL OK
testing url: 'foo.bar'
INVALID URL!
add a comment |
up vote
1
down vote
accepted
up vote
1
down vote
accepted
One solution would be to somehow detect whether the decorated function is a class method or not — which seems to be difficult if not impossible (as far as I can tell anyway) to do so cleanly. The inspect
module's ismethod()
and isfunction()
don't work inside a decorator used inside a class definition.
Given that, here's a somewhat hacky way of doing it which checks to see if the decorated callable's first argument has been given the name "self"
, which is the coding convention for it in class methods (although it is not a requirement, so caveat emptor and use at your own risk).
The following code seems to work in both Python 2 and 3. However in Python 3 it may raise DeprecationWarning
s depending on exactly what sub-version is being used—so they have been suppressed in a section of the code below.
from functools import wraps
import inspect
import warnings
def validate_url(f):
@wraps(f)
def validated(*args, **kwargs):
with warnings.catch_warnings():
# Suppress DeprecationWarnings in this section.
warnings.simplefilter('ignore', category=DeprecationWarning)
# If "f"'s first argument is named "self",
# assume it's a method.
if inspect.getargspec(f).args[0] == 'self':
url = args[1]
else: # Otherwise assume "f" is a ordinary function.
url = args[0]
print('testing url: {!r}'.format(url))
assert len(url.split('.')) == 3 # Trivial "validation".
return f(*args, **kwargs)
return validated
@validate_url
def some_func(url, some_other_arg, *some_args, **some_kwargs):
print('some_func() called')
class SomeClass:
@validate_url
def some_method(self, url, some_other_args):
print('some_method() called')
if __name__ == '__main__':
print('** Testing decorated function **')
some_func('xxx.yyy.zzz', 'another arg')
print(' URL OK')
try:
some_func('https://bogus_url.com', 'another thing')
except AssertionError:
print(' INVALID URL!')
print('n** Testing decorated method **')
instance = SomeClass()
instance.some_method('aaa.bbb.ccc', 'something else') # -> AssertionError
print(' URL OK')
try:
instance.some_method('foo.bar', 'arg 2') # -> AssertionError
except AssertionError:
print(' INVALID URL!')
Output:
** Testing decorated function **
testing url: 'xxx.yyy.zzz'
some_func() called
URL OK
testing url: 'https://bogus_url.com'
INVALID URL!
** Testing decorated method **
testing url: 'aaa.bbb.ccc'
some_method() called
URL OK
testing url: 'foo.bar'
INVALID URL!
One solution would be to somehow detect whether the decorated function is a class method or not — which seems to be difficult if not impossible (as far as I can tell anyway) to do so cleanly. The inspect
module's ismethod()
and isfunction()
don't work inside a decorator used inside a class definition.
Given that, here's a somewhat hacky way of doing it which checks to see if the decorated callable's first argument has been given the name "self"
, which is the coding convention for it in class methods (although it is not a requirement, so caveat emptor and use at your own risk).
The following code seems to work in both Python 2 and 3. However in Python 3 it may raise DeprecationWarning
s depending on exactly what sub-version is being used—so they have been suppressed in a section of the code below.
from functools import wraps
import inspect
import warnings
def validate_url(f):
@wraps(f)
def validated(*args, **kwargs):
with warnings.catch_warnings():
# Suppress DeprecationWarnings in this section.
warnings.simplefilter('ignore', category=DeprecationWarning)
# If "f"'s first argument is named "self",
# assume it's a method.
if inspect.getargspec(f).args[0] == 'self':
url = args[1]
else: # Otherwise assume "f" is a ordinary function.
url = args[0]
print('testing url: {!r}'.format(url))
assert len(url.split('.')) == 3 # Trivial "validation".
return f(*args, **kwargs)
return validated
@validate_url
def some_func(url, some_other_arg, *some_args, **some_kwargs):
print('some_func() called')
class SomeClass:
@validate_url
def some_method(self, url, some_other_args):
print('some_method() called')
if __name__ == '__main__':
print('** Testing decorated function **')
some_func('xxx.yyy.zzz', 'another arg')
print(' URL OK')
try:
some_func('https://bogus_url.com', 'another thing')
except AssertionError:
print(' INVALID URL!')
print('n** Testing decorated method **')
instance = SomeClass()
instance.some_method('aaa.bbb.ccc', 'something else') # -> AssertionError
print(' URL OK')
try:
instance.some_method('foo.bar', 'arg 2') # -> AssertionError
except AssertionError:
print(' INVALID URL!')
Output:
** Testing decorated function **
testing url: 'xxx.yyy.zzz'
some_func() called
URL OK
testing url: 'https://bogus_url.com'
INVALID URL!
** Testing decorated method **
testing url: 'aaa.bbb.ccc'
some_method() called
URL OK
testing url: 'foo.bar'
INVALID URL!
edited Nov 21 at 23:40
answered Sep 18 at 20:00
martineau
64.8k887174
64.8k887174
add a comment |
add a comment |
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f52391727%2fdecorator-which-skips-self-for-methods%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Not sure whether this can be applied here
– Abdul Niyas P M
Sep 18 at 17:28
Could you define a
@staticmethod
likedef validate_url(url, *args)
that runs inside ofsome_func
? You could then decoratevalidate_url
. Seems hacky and non-Pythonic, but that's the first thing I'd try– C.Nivs
Sep 18 at 19:27