Skip to content

Commit 7504b22

Browse files
committed
Release 2.3.0
2 parents 7bc5949 + a417f60 commit 7504b22

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

87 files changed

+1514
-227
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ The manual (including install instructions) can be found in the doc/ directory.
3939

4040
## Important update notes
4141

42+
*2.3.0 and up*: Python 3 support was fixed. Creation date parsing for contacts was fixed; correct timestamps will now be returned, rather than unformatted ones - if your application relies on the broken variant, you'll need to change your code. Some additional parameters were added to the `net` and `parse` methods to facilitate NIC handle lookups; the defaults are backwards-compatible, and these changes should not have any consequences for your code. Thai WHOIS parsing was implemented, but is a little spotty - data may occasionally be incorrectly split up. Please submit a bug report if you run across any issues.
43+
4244
*2.2.0 and up*: The internal workings of `get_whois_raw` have been changed, to better facilitate parsing of WHOIS data from registries that may return multiple partial matches for a query, such as `whois.verisign-grs.com`. This change means that, by default, `get_whois_raw` will now strip out the part of such a response that does not pertain directly to the requested domain. If your application requires an unmodified raw WHOIS response and is calling `get_whois_raw` directly, you should use the new `never_cut` parameter to keep pythonwhois from doing this post-processing. As this is a potentially breaking behaviour change, the minor version has been bumped.
4345

4446
## It doesn't work!

doc/usage.html

Lines changed: 2 additions & 2 deletions
Large diffs are not rendered by default.

doc/usage.zpy

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ To start using pythonwhois, use `import pythonwhois`.
160160

161161
## When you need more control...
162162

163-
^ pythonwhois.net.get_whois_raw(**domain**[, **server=""**, **rfc3490=True**, **never_cut=False**])
163+
^ pythonwhois.net.get_whois_raw(**domain**[, **server=""**, **rfc3490=True**, **never_cut=False**, **with_server_list=False**])
164164

165165
Retrieves the raw WHOIS data for the specified domain, and returns it as a list of responses (one element for each WHOIS server queried). This method will keep following redirects, until it ends up at the right server (and all responses it picks up in the meantime, will be included). Raises `pythonwhois.shared.WhoisException` if no root server for the TLD could be found.
166166

@@ -175,6 +175,9 @@ To start using pythonwhois, use `import pythonwhois`.
175175

176176
never_cut::
177177
**Optional.** If set to `True`, pythonwhois will never strip out data from the raw WHOIS responses, **even** if that data relates to a partial match, rather than the requested domain.
178+
179+
with_server_list::
180+
**Optional.** If set to `True`, `pythonwhois` will return a `(response, server_list)` tuple instead of just `response`, where the `server_list` contains a list of all the WHOIS servers that were queried to collect the raw data. The order of the list is from first (root) server to last server.
178181

179182

180183
^ pythonwhois.net.get_root_server(**domain**)
@@ -184,7 +187,7 @@ To start using pythonwhois, use `import pythonwhois`.
184187
domain::
185188
The domain whose TLD you want to know the root WHOIS server for.
186189

187-
^ pythonwhois.parse.parse_raw_whois(**raw_data**[, **normalized**])
190+
^ pythonwhois.parse.parse_raw_whois(**raw_data**[, **normalized**, **never_query_handles=True**, **handle_server=""**])
188191

189192
Parses the specified raw WHOIS data. It's useful to call this method manually if you receive your WHOIS data from elsewhere, and don't need the retrieval capabilities of pythonwhois. Returns an object like `pythonwhois.get_whois` does.
190193

@@ -193,4 +196,9 @@ To start using pythonwhois, use `import pythonwhois`.
193196

194197
normalized::
195198
**Optional.** Like for `pythonwhois.get_whois`. Empty list or omitted to normalize nothing, `True` to normalize all supported fields, a list of keys if you only want certain keys to be normalized.
196-
199+
200+
never_handle_queries::
201+
**Optional.** If this is enabled, `pythonwhois` won't try to resolve handles that require additional queries. Some WHOIS servers only provide NIC handles by default and require additional lookups to retrieve the associated contact information. If you are running an offline application (or have your own load distribution mechanism) and you don't want `pythonwhois` to make queries on your behalf, set this to `True` (the default). When using the `get_whois` method, this is set to `False` behind the scenes, and handles will be automatically resolved.
202+
203+
handle_server::
204+
**Optional.** When `never_handle_queries` is set to `False`, you should specify the server responsible for handle lookups here. This is usually the last server in a WHOIS chain. `get_whois` will set this for you automatically.

pwhois

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import argparse, pythonwhois, json, datetime
44
try:
55
from collections import OrderedDict
6-
except ImportError, e:
6+
except ImportError as e:
77
from ordereddict import OrderedDict
88

99
parser = argparse.ArgumentParser(description="Retrieves and parses WHOIS data for a domain name.")
@@ -20,18 +20,22 @@ def json_fallback(obj):
2020
return obj
2121

2222
if args.file is None:
23-
data = pythonwhois.net.get_whois_raw(args.domain[0])
23+
data, server_list = pythonwhois.net.get_whois_raw(args.domain[0], with_server_list=True)
2424
else:
25+
server_list = []
2526
with open(args.file, "r") as f:
2627
data = f.read().split("\n--\n")
2728

2829
if args.raw == True:
29-
print "\n--\n".join(data)
30+
print("\n--\n".join([x.encode("utf-8") for x in data]))
3031
else:
31-
parsed = pythonwhois.parse.parse_raw_whois(data, normalized=True)
32+
if len(server_list) > 0:
33+
parsed = pythonwhois.parse.parse_raw_whois(data, normalized=True, never_query_handles=False, handle_server=server_list[-1])
34+
else:
35+
parsed = pythonwhois.parse.parse_raw_whois(data, normalized=True)
3236

3337
if args.json == True:
34-
print json.dumps(parsed, default=json_fallback)
38+
print(json.dumps(parsed, default=json_fallback))
3539
else:
3640
data_map = OrderedDict({})
3741

@@ -46,18 +50,18 @@ else:
4650
data_map["emails"] = ("E-mail address", "+")
4751

4852
widest_label = 0
49-
for key, value in data_map.iteritems():
53+
for key, value in data_map.items():
5054
if len(value[0]) > widest_label:
5155
widest_label = len(value[0])
5256

53-
for key, value in data_map.iteritems():
57+
for key, value in data_map.items():
5458
if key in parsed and parsed[key] is not None:
5559
label = value[0] + (" " * (widest_label - len(value[0]))) + " :"
5660
if value[1] == 1:
57-
print "%s %s" % (label, parsed[key][0])
61+
print("%s %s" % (label, parsed[key][0]))
5862
elif value[1] == "+":
5963
for item in parsed[key]:
60-
print "%s %s" % (label, item)
64+
print("%s %s" % (label, item))
6165

6266
if parsed["contacts"] is not None:
6367
# This defines the contacts shown in the output
@@ -80,23 +84,24 @@ else:
8084
data_map["email"] = "E-mail address"
8185
data_map["phone"] = "Phone number"
8286
data_map["fax"] = "Fax number"
87+
data_map["creationdate"] = "Created"
8388
data_map["changedate"] = "Last changed"
8489

8590
for contact in contacts_map:
8691
if parsed["contacts"][contact] is not None:
8792
contact_data = parsed["contacts"][contact]
8893

89-
print "\n" + contacts_map[contact]
94+
print("\n" + contacts_map[contact])
9095

91-
for key, value in data_map.iteritems():
96+
for key, value in data_map.items():
9297
if len(value) > widest_label:
9398
widest_label = len(value)
9499

95-
for key, value in data_map.iteritems():
100+
for key, value in data_map.items():
96101
if key in contact_data and contact_data[key] is not None:
97102
label = " " + value + (" " * (widest_label - len(value))) + " :"
98-
actual_data = str(contact_data[key])
103+
actual_data = contact_data[key]
99104
if "\n" in actual_data: # Indent multi-line values properly
100105
lines = actual_data.split("\n")
101106
actual_data = "\n".join([lines[0]] + [(" " * (widest_label + 7)) + line for line in lines[1:]])
102-
print "%s %s" % (label, actual_data)
107+
print("%s %s" % (label, actual_data))

pythonwhois/__init__.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
from . import net, parse
22

33
def get_whois(domain, normalized=[]):
4-
raw_data = net.get_whois_raw(domain)
5-
return parse.parse_raw_whois(raw_data, normalized=normalized)
4+
raw_data, server_list = net.get_whois_raw(domain, with_server_list=True)
5+
# Unlisted handles will be looked up on the last WHOIS server that was queried. This may be changed to also query
6+
# other servers in the future, if it turns out that there are cases where the last WHOIS server in the chain doesn't
7+
# actually hold the handle contact details, but another WHOIS server in the chain does.
8+
return parse.parse_raw_whois(raw_data, normalized=normalized, never_query_handles=False, handle_server=server_list[-1])
69

710
def whois(*args, **kwargs):
811
raise Exception("The whois() method has been replaced by a different method (with a different API), since pythonwhois 2.0. Either install the older pythonwhois 1.2.3, or change your code to use the new API.")

pythonwhois/net.py

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import socket, re
1+
import socket, re, sys
22
from codecs import encode, decode
33
from . import shared
44

5-
def get_whois_raw(domain, server="", previous=[], rfc3490=True, never_cut=False):
5+
def get_whois_raw(domain, server="", previous=[], rfc3490=True, never_cut=False, with_server_list=False, server_list=[]):
66
# Sometimes IANA simply won't give us the right root WHOIS server
77
exceptions = {
88
".ac.uk": "whois.ja.net",
@@ -12,12 +12,16 @@ def get_whois_raw(domain, server="", previous=[], rfc3490=True, never_cut=False)
1212
}
1313

1414
if rfc3490:
15-
domain = encode( domain if type(domain) is unicode else decode(domain, "utf8"), "idna" )
15+
if sys.version_info < (3, 0):
16+
domain = encode( domain if type(domain) is unicode else decode(domain, "utf8"), "idna" )
17+
else:
18+
domain = encode(domain, "idna").decode("ascii")
1619

17-
if len(previous) == 0:
20+
if len(previous) == 0 and server == "":
1821
# Root query
22+
server_list = [] # Otherwise it retains the list on subsequent queries, for some reason.
1923
is_exception = False
20-
for exception, exc_serv in exceptions.iteritems():
24+
for exception, exc_serv in exceptions.items():
2125
if domain.endswith(exception):
2226
is_exception = True
2327
target_server = exc_serv
@@ -26,7 +30,7 @@ def get_whois_raw(domain, server="", previous=[], rfc3490=True, never_cut=False)
2630
target_server = get_root_server(domain)
2731
else:
2832
target_server = server
29-
if domain.endswith(".jp") and target_server == "whois.jprs.jp":
33+
if target_server == "whois.jprs.jp":
3034
request_domain = "%s/e" % domain # Suppress Japanese output
3135
elif domain.endswith(".de") and ( target_server == "whois.denic.de" or target_server == "de.whois-servers.net" ):
3236
request_domain = "-T dn,ace %s" % domain # regional specific stuff
@@ -52,14 +56,18 @@ def get_whois_raw(domain, server="", previous=[], rfc3490=True, never_cut=False)
5256
break
5357
if never_cut == False:
5458
new_list = [response] + previous
59+
server_list.append(target_server)
5560
for line in [x.strip() for x in response.splitlines()]:
5661
match = re.match("(refer|whois server|referral url|whois server|registrar whois):\s*([^\s]+\.[^\s]+)", line, re.IGNORECASE)
5762
if match is not None:
5863
referal_server = match.group(2)
59-
if referal_server != server:
64+
if referal_server != server and "://" not in referal_server: # We want to ignore anything non-WHOIS (eg. HTTP) for now.
6065
# Referal to another WHOIS server...
61-
return get_whois_raw(domain, referal_server, new_list)
62-
return new_list
66+
return get_whois_raw(domain, referal_server, new_list, server_list=server_list, with_server_list=with_server_list)
67+
if with_server_list:
68+
return (new_list, server_list)
69+
else:
70+
return new_list
6371

6472
def get_root_server(domain):
6573
data = whois_request(domain, "whois.iana.org")
@@ -73,11 +81,11 @@ def get_root_server(domain):
7381
def whois_request(domain, server, port=43):
7482
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
7583
sock.connect((server, port))
76-
sock.send("%s\r\n" % domain)
77-
buff = ""
84+
sock.send(("%s\r\n" % domain).encode("utf-8"))
85+
buff = b""
7886
while True:
7987
data = sock.recv(1024)
8088
if len(data) == 0:
8189
break
8290
buff += data
83-
return buff
91+
return buff.decode("utf-8")

0 commit comments

Comments
 (0)