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
7 changes: 1 addition & 6 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -153,9 +153,4 @@ dmypy.json
# Cython debug symbols
cython_debug/

# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
tests/test_root
11 changes: 11 additions & 0 deletions .idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions .idea/modules.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions .idea/python-common-utility.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions common_utility/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
from .fileDownloader import *
from .configLoader import *
from .rateLimiter import *
from .interfaceResolver import *
37 changes: 37 additions & 0 deletions common_utility/interfaceResolver.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from enum import Enum

import netifaces
from context_logger import get_logger

log = get_logger('InterfaceResolver')


class AddressFamily(Enum):
IPv4 = netifaces.AF_INET
IPv6 = netifaces.AF_INET6
MAC = netifaces.AF_LINK


class IInterfaceResolver:

def resolve(self, interface: str, family: AddressFamily = AddressFamily.IPv4) -> str | None:
raise NotImplementedError()


class InterfaceResolver(IInterfaceResolver):

def resolve(self, interface: str, family: AddressFamily = AddressFamily.IPv4) -> str | None:
interfaces = netifaces.interfaces()
if interface in interfaces:
inet_address = netifaces.ifaddresses(interface).get(family.value)
if inet_address and len(inet_address) > 0:
if address := inet_address[0].get('addr'):
return address
else:
log.error('Address not found for interface', interface=interface, family=family.name)
else:
log.error('Address family not found for interface', interface=interface, family=family.name)
else:
log.error('Selected interface not found', interface=interface, interfaces=interfaces)

return None
37 changes: 37 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
[project]
name = "python-common-utility"
description = "Common utility packages for Python projects"
authors = [
{ name = "Ferenc Nandor Janky & Attila Gombos", email = "info@effective-range.com" }
]
maintainers = [
{ name = "Ferenc Nandor Janky & Attila Gombos", email = "info@effective-range.com" }
]
dependencies = [
"requests",
"pydantic",
"jinja2",
"netifaces",
"python-context-logger @ git+https://github.com/EffectiveRange/python-context-logger.git@latest",
]
dynamic = ["version"]

[tool.setuptools]
package-dir = {"" = "."}
packages = ["common_utility", "test_utility"]

[tool.setuptools.package-data]
hello = ["py.typed"]

[build-system]
requires = ["setuptools>=61", "setuptools_scm"]
build-backend = "setuptools.build_meta"

[tool.setuptools_scm]
version_scheme = "guess-next-dev"
local_scheme = "node-and-date"

[tool.pytest]
addopts = ["--verbose", "--capture=no"]
python_files = ["*Test.py"]
python_classes = ["*Test"]
18 changes: 0 additions & 18 deletions setup.py

This file was deleted.

135 changes: 135 additions & 0 deletions tests/interfaceResolverTest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import unittest
from unittest import TestCase
from unittest.mock import patch

from context_logger import setup_logging

from common_utility import InterfaceResolver, AddressFamily


class InterfaceResolverTest(TestCase):
resolver = InterfaceResolver()

@classmethod
def setUpClass(cls):
setup_logging('python-common-utility', 'DEBUG', warn_on_overwrite=False)

def setUp(self):
print()

@patch('common_utility.interfaceResolver.netifaces')
def test_returns_ipv4_address(self, mock_netifaces):
# Given
self._setup_mocks(mock_netifaces)

# When
address = self.resolver.resolve('lo')

# Then
self.assertEqual('127.0.0.1', address)

@patch('common_utility.interfaceResolver.netifaces')
def test_returns_ipv6_address(self, mock_netifaces):
# Given
self._setup_mocks(mock_netifaces)

# When
address = self.resolver.resolve('lo', AddressFamily.IPv6)

# Then
self.assertEqual('::1', address)

@patch('common_utility.interfaceResolver.netifaces')
def test_returns_mac_address(self, mock_netifaces):
# Given
self._setup_mocks(mock_netifaces)

# When
address = self.resolver.resolve('lo', AddressFamily.MAC)

# Then
self.assertEqual('00:00:00:00:00:00', address)

@patch('common_utility.interfaceResolver.netifaces')
def test_returns_none_when_interface_not_exists(self, mock_netifaces):
# Given
self._setup_mocks(mock_netifaces)
mock_netifaces.interfaces.return_value = ['lo', 'wlan0']

# When
address = self.resolver.resolve('eth0')

# Then
self.assertIsNone(address)

@patch('common_utility.interfaceResolver.netifaces')
def test_returns_none_when_address_family_not_exists_for_interface(self, mock_netifaces):
# Given
self._setup_mocks(mock_netifaces)
mock_netifaces.ifaddresses.return_value = {
10: [{'addr': '::1'}],
17: [{'addr': '00:00:00:00:00:00'}],
}

# When
address = self.resolver.resolve('lo')

# Then
self.assertIsNone(address)

@patch('common_utility.interfaceResolver.netifaces')
def test_returns_none_when_address_not_exists_for_address_family(self, mock_netifaces):
# Given
self._setup_mocks(mock_netifaces)
mock_netifaces.ifaddresses.return_value = {
2: [],
}

# When
address = self.resolver.resolve('wlan0')

# Then
self.assertIsNone(address)

@patch('common_utility.interfaceResolver.netifaces')
def test_returns_none_when_addr_key_not_exists_for_address(self, mock_netifaces):
# Given
self._setup_mocks(mock_netifaces)
mock_netifaces.ifaddresses.return_value = {
2: [{}],
}

# When
address = self.resolver.resolve('eth0')

# Then
self.assertIsNone(address)

@patch('common_utility.interfaceResolver.netifaces')
def test_returns_none_when_addr_value_not_exists_for_address(self, mock_netifaces):
# Given
self._setup_mocks(mock_netifaces)
mock_netifaces.ifaddresses.return_value = {
2: [{'addr': ''}],
}

# When
address = self.resolver.resolve('eth0')

# Then
self.assertIsNone(address)

def _setup_mocks(self, mock_netifaces):
mock_netifaces.interfaces.return_value = ['lo', 'eth0', 'wlan0']
mock_netifaces.AF_INET = 2
mock_netifaces.AF_INET6 = 10
mock_netifaces.AF_INET6 = 17
mock_netifaces.ifaddresses.return_value = {
2: [{'addr': '127.0.0.1'}],
10: [{'addr': '::1'}],
17: [{'addr': '00:00:00:00:00:00'}],
}


if __name__ == '__main__':
unittest.main()
Loading