3

With urllib2, the latest versions allow to use an optional parameter "context" when calling urlopen.

I put together some code to make use of that:

# For Python 3.0 and later
from urllib.request import urlopen, HTTPError, URLError
except ImportError:
# Fall back to Python 2's urllib2
from urllib2 import urlopen, HTTPError, URLError
import ssl

context = ssl.create_default_context()
context.check_hostname = False
context.verify_mode = ssl.CERT_NONE
response = urlopen(url=url, context=context)

Running that with my python 2.78 ... I get:

Traceback (most recent call last):
  File "test.py", line 5, in <module>
  context = ssl.create_default_context()
  AttributeError: 'module' object has no attribute 'create_default_context'

So I thought: lets go for python3 then; and now I get:

Traceback (most recent call last):
  File "test.py", line 15, in <module>
    response = urlopen(url=url, context=context)
TypeError: urlopen() got an unexpected keyword argument 'context'

It took me a while to figure that using that named argument context ... also requires a newer version of python than the 3.4.0 installed on my ubuntu 14.04.

My question: what is the "canonical" way to check if "context" can be used when calling urlopen? Just call it and expect the TypeError? Or do a exact version check on the python I am running in?

I did search here and with google, but maybe I am just missing the right search term ... as I couldnt find anything helpful ...

GhostCat
  • 137,827
  • 25
  • 176
  • 248

3 Answers3

2

How to check a function's signature is described here: How can I read a function's signature including default argument values?

However, some functions have generic signatures like:

def myfunc(**kwargs):
    print kwargs.items()

    if kwargs.has_key('foo'):
        ...

    if kwargs.has_key('bar'):
        ...

for which it is impossible to know which arguments they use before calling them. For example matplotlib / pylab has many such functions using kwargs.

Community
  • 1
  • 1
Andre Holzner
  • 18,333
  • 6
  • 54
  • 63
  • Although you don't actually say so, your answer just reinforces [@meuh's](http://stackoverflow.com/a/31367685/355230). – martineau Jul 12 '15 at 12:41
  • I understand the argument about **EAFP**; but I have a very strong java background; and for it is mentally much easier to ask for permission first. I just can't stand the idea of raising an exception ... where I have to worry in the future that some subtle detail changes later on; and I am catching the wrong thing or whatever. So `inspect.getfullargspec` pointed out by your answer is exactly what I will be using. – GhostCat Jul 12 '15 at 18:23
  • @Jägermeister: Every time you use a `for` loop, it ends by raising and then catching `StopIteration`. You will need to get used to using exceptions heavily in Python. – Kevin Jul 12 '15 at 18:44
2

Use try/except. See the python glossary:

EAFP

Easier to ask for forgiveness than permission. This common Python coding style assumes the existence of valid keys or attributes and catches exceptions if the assumption proves false. This clean and fast style is characterized by the presence of many try and except statements. The technique contrasts with the LBYL style common to many other languages such as C.

meuh
  • 11,500
  • 2
  • 29
  • 45
1

Check the version of Python:

import sys

if sys.hexversion >= 0x03050000:
    urlopen = urllib.urlopen
else:
    def urlopen (*args, context=None, **kwargs):
        return urllib.urlopen(*args, **kwargs)

Now just use urlopen() instead of urllib.urlopen().

This will break in early alphas of 3.5, I think, but alphas are meant to break, so I don't care enough to track down the precise version which introduced this argument.

Kevin
  • 28,963
  • 9
  • 62
  • 81