Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ python-hosts

This is a python library for managing a hosts file.
It enables you to add and remove entries, or import them from a file or URL.
Utility functions have been streamlined for easier maintenance.
It remains compatible with Python 2.7 as well as modern Python 3 releases.

Documentation
Expand Down
64 changes: 27 additions & 37 deletions python_hosts/hosts.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,50 +196,38 @@ def write(self, path=None, mode='w'):
:param path: override the write path
:return: Dictionary containing counts
"""
written_count = 0
comments_written = 0
blanks_written = 0
ipv4_entries_written = 0
ipv6_entries_written = 0
if path:
output_file_path = path
else:
output_file_path = self.path

counters = {
'total_written': 0,
'comments_written': 0,
'blanks_written': 0,
'ipv4_entries_written': 0,
'ipv6_entries_written': 0,
}
output_file_path = path if path else self.path
try:
with open(output_file_path, mode) as hosts_file:
for written_count, line in enumerate(self.entries):
if line.entry_type == 'comment':
hosts_file.write(line.comment + "\n")
comments_written += 1
if line.entry_type == 'blank':
for entry in self.entries:
if entry.entry_type == 'comment':
hosts_file.write(entry.comment + "\n")
counters['comments_written'] += 1
elif entry.entry_type == 'blank':
hosts_file.write("\n")
blanks_written += 1
if line.entry_type == 'ipv4':
hosts_file.write(
"{0}\t{1}{2}\n".format(
line.address,
' '.join(line.names),
" # " + line.comment if line.comment else ""
)
)
ipv4_entries_written += 1
if line.entry_type == 'ipv6':
counters['blanks_written'] += 1
else:
hosts_file.write(
"{0}\t{1}{2}\n".format(
line.address,
' '.join(line.names),
" # " + line.comment if line.comment else ""
entry.address,
' '.join(entry.names),
" # " + entry.comment if entry.comment else "",
)
)
ipv6_entries_written += 1
key = 'ipv6_entries_written' if entry.entry_type == 'ipv6' else 'ipv4_entries_written'
counters[key] += 1
counters['total_written'] += 1
except Exception:
raise UnableToWriteHosts()
return {'total_written': written_count + 1,
'comments_written': comments_written,
'blanks_written': blanks_written,
'ipv4_entries_written': ipv4_entries_written,
'ipv6_entries_written': ipv6_entries_written}

return counters
@staticmethod
def get_hosts_by_url(url=None):
"""
Expand All @@ -264,8 +252,10 @@ def exists(self, address=None, names=None, comment=None):
if self.find_all_matching(address=address, name=name, comment=comment):
return True

return any(entry.entry_type == 'comment' and entry.comment == comment
for entry in self.entries)
if comment:
return any(entry.entry_type == 'comment' and entry.comment == comment
for entry in self.entries)
return False

def remove_all_matching(self, address=None, name=None, comment=None):
"""
Expand Down
47 changes: 13 additions & 34 deletions python_hosts/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,55 +9,34 @@


def is_ipv4(entry):
"""
Check if the string provided is a valid ipv4 address
:param entry: A string representation of an IP address
:return: True if valid, False if invalid
"""
"""Return ``True`` if ``entry`` is a valid IPv4 address."""
try:
if socket.inet_aton(entry):
return True
socket.inet_aton(entry)
except socket.error:
return False
return True


def is_ipv6(entry):
"""
Check if the string provided is a valid ipv6 address
:param entry: A string representation of an IP address
:return: True if valid, False if invalid
"""
"""Return ``True`` if ``entry`` is a valid IPv6 address."""
try:
if socket.inet_pton(socket.AF_INET6, entry):
return True
socket.inet_pton(socket.AF_INET6, entry)
except socket.error:
return False
return True


def valid_hostnames(hostname_list):
"""
Check if the supplied list of strings are valid hostnames
:param hostname_list: A list of strings
:return: True if the strings are valid hostnames, False if not
"""
for entry in hostname_list:
if len(entry) > 255:
return False
allowed = re.compile(r'(?!-)[A-Z\d-]{1,63}(?<!-)$', re.IGNORECASE)
if not all(allowed.match(x) for x in entry.split(".")):
return False
return True
"""Return ``True`` if all items in ``hostname_list`` are valid hostnames."""
allowed = re.compile(r'(?!-)[A-Z\d-]{1,63}(?<!-)$', re.IGNORECASE)
return all(len(entry) <= 255 and
all(allowed.match(x) for x in entry.split('.'))
for entry in hostname_list)


def is_readable(path=None):
"""
Test if the supplied filesystem path can be read
:param path: A filesystem path
:return: True if the path is a file that can be read. Otherwise, False
"""
if os.path.isfile(path) and os.access(path, os.R_OK):
return True
return False
"""Return ``True`` if ``path`` exists and is readable."""
return os.path.isfile(path) and os.access(path, os.R_OK)


def dedupe_list(seq):
Expand Down
Loading