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
50 changes: 26 additions & 24 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,51 +14,53 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
steps:
- uses: actions/checkout@v4

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
- name: Install uv
uses: astral-sh/setup-uv@v7
with:
enable-cache: true
python-version: ${{ matrix.python-version }}

- name: Install nox
run: |
python -m pip install --upgrade pip
pip install nox
- name: Install dependencies
run: uv sync --group dev

- name: Lint and typing
- name: Lint and format
run: |
nox -rs lint typing
uv run --group lint ruff check digaws/ tests/
uv run --group lint ruff format --check digaws/ tests/
uv run --group typing mypy digaws/ tests/

- name: Tests
run: |
nox -rs tests -- -v
uv run --group test pytest tests/ -v
uv run --group test coverage report

build_publish:
runs-on: ubuntu-latest
needs: tests
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
environment:
name: pypi
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5

- name: Set up Python 3.12
uses: actions/setup-python@v5
- name: Install uv
uses: astral-sh/setup-uv@v7
with:
python-version: 3.12
enable-cache: true
python-version: 3.14

- name: Install tools
run: |
python -m pip install --upgrade pip
pip install twine wheel setuptools
- name: Install dependencies
run: uv sync

- name: Build
run: |
python setup.py sdist bdist_wheel
run: uv build

- name: Publish
run: |
export TWINE_USERNAME=${{ secrets.TWINE_USERNAME }}
export TWINE_PASSWORD=${{ secrets.TWINE_PASSWORD }}
twine upload dist/*
run: uv publish
14 changes: 12 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,23 @@ a cached version until a new version is published.

## Requirements

Python >= 3.8
Python >= 3.10

Tests are verified on Linux, macos and Windows.

## Install

### Using [pipx](https://pypa.github.io/pipx/#install-pipx) (this is the preferred way)
### Using [uv](https://docs.astral.sh/uv/) (recommended)

```bash
# Install globally
uv tool install digaws

# Run directly
uv tool run digaws <ip_address>
```

### Using [pipx](https://pypa.github.io/pipx/#install-pipx)

```bash
pipx install digaws
Expand Down
2 changes: 1 addition & 1 deletion digaws/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
__version__ = "1.0.7"
__version__ = "1.0.8"
__description__ = "Look up canonical information for AWS IP addresses and networks"
50 changes: 26 additions & 24 deletions digaws/digaws.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import sys
from datetime import datetime
from pathlib import Path
from typing import Any, Dict, List
from typing import Any

import requests
from dateutil import tz
Expand All @@ -27,7 +27,7 @@
logger.setLevel(logging.INFO)


def get_aws_ip_ranges() -> Dict:
def get_aws_ip_ranges() -> dict:
CACHE_DIR.mkdir(exist_ok=True)

headers = {}
Expand All @@ -49,14 +49,14 @@ def get_aws_ip_ranges() -> Dict:
logger.debug(f"reading cached file {CACHE_FILE}")
with open(CACHE_FILE) as ip_ranges:
return json.load(ip_ranges)
except (OSError, IOError, json.JSONDecodeError) as e:
except (OSError, json.JSONDecodeError) as e:
logger.debug(f"ERROR reading {CACHE_FILE}: {e}")
raise CachedFileException(str(e))
raise CachedFileError(str(e)) from e
elif response.status_code == 200:
try:
with open(CACHE_FILE, "w") as f:
f.write(response.text)
except (OSError, IOError) as e:
except OSError as e:
logger.warning(e)

return response.json()
Expand All @@ -66,41 +66,41 @@ def get_aws_ip_ranges() -> Dict:
f"{response.status_code}. Content: {response.text}"
)
logger.debug(msg)
raise UnexpectedRequestException(msg)
raise UnexpectedRequestError(msg)
except RequestException as e:
logger.debug(f"ERROR retrieving {AWS_IP_RANGES_URL}: {e}")
raise e


class CachedFileException(Exception):
class CachedFileError(Exception):
def __init__(self, message: str):
message = f"Error reading cached ranges {CACHE_FILE}: {message}"
super(CachedFileException, self).__init__(message)
super().__init__(message)


class UnexpectedRequestException(Exception):
class UnexpectedRequestError(Exception):
def __init__(self, message: str):
super(UnexpectedRequestException, self).__init__(message)
super().__init__(message)


class DigAWSPrettyPrinter:
def __init__(self, data: List[Dict], output_fields: List[str] = []):
def __init__(self, data: list[dict], output_fields: list[str] | None = None):
self.data = data
self.output_fields = output_fields
self.output_fields = output_fields if output_fields is not None else []

def plain_print(self) -> None:
for prefix in self.data:
if "prefix" in self.output_fields:
try:
print(f'Prefix: {prefix["ip_prefix"]}')
print(f"Prefix: {prefix['ip_prefix']}")
except KeyError:
print(f'IPv6 Prefix: {prefix["ipv6_prefix"]}')
print(f"IPv6 Prefix: {prefix['ipv6_prefix']}")
if "region" in self.output_fields:
print(f'Region: {prefix["region"]}')
print(f"Region: {prefix['region']}")
if "service" in self.output_fields:
print(f'Service: {prefix["service"]}')
print(f"Service: {prefix['service']}")
if "network_border_group" in self.output_fields:
print(f'Network border group: {prefix["network_border_group"]}')
print(f"Network border group: {prefix['network_border_group']}")
print("")

def json_print(self) -> None:
Expand All @@ -127,9 +127,11 @@ def json_print(self) -> None:


class DigAWS:
def __init__(self, *, ip_ranges: Dict, output: str = "plain", output_fields: List[str] = []):
def __init__(
self, *, ip_ranges: dict, output: str = "plain", output_fields: list[str] | None = None
):
self.output = output
self.output_fields = output_fields
self.output_fields = output_fields if output_fields is not None else []
self.ip_prefixes = [
{
"ip_prefix": ipaddress.IPv4Network(prefix["ip_prefix"]),
Expand All @@ -152,7 +154,7 @@ def __init__(self, *, ip_ranges: Dict, output: str = "plain", output_fields: Lis
def lookup(self, address: str) -> DigAWSPrettyPrinter:
return DigAWSPrettyPrinter(self._lookup_data(address), self.output_fields)

def _lookup_data(self, address: str) -> List[Dict]:
def _lookup_data(self, address: str) -> list[dict]:
addr: Any = None
try:
addr = ipaddress.IPv4Address(address)
Expand All @@ -176,7 +178,7 @@ def _lookup_data(self, address: str) -> List[Dict]:
if addr.subnet_of(prefix["ipv6_prefix"])
]
except (ipaddress.AddressValueError, ValueError):
raise ValueError(f"Wrong IP or CIDR format: {address}")
raise ValueError(f"Wrong IP or CIDR format: {address}") from None

return data

Expand Down Expand Up @@ -213,7 +215,7 @@ def arguments_parser() -> argparse.ArgumentParser:
parser.add_argument(
"--version",
action="version",
version="%(prog)s {version}".format(version=__version__),
version=f"%(prog)s {__version__}",
)
parser.add_argument(
"addresses",
Expand Down Expand Up @@ -256,8 +258,8 @@ def main():
RequestException,
ipaddress.AddressValueError,
ValueError,
CachedFileException,
UnexpectedRequestException,
CachedFileError,
UnexpectedRequestError,
) as e:
print(f"ERROR: {e}")
sys.exit(1)
Expand Down
35 changes: 0 additions & 35 deletions noxfile.py

This file was deleted.

77 changes: 77 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
[project]
name = "digaws"
version = "1.0.8"
description = "Look up canonical information for AWS IP addresses and networks"
readme = "README.md"
requires-python = ">=3.10"
license = { text = "Apache Software License" }
authors = [
{ name = "Daniel Carrillo", email = "daniel.carrillo@gmail.com" }
]
keywords = ["aws", "ip", "network", "dns", "lookup"]
classifiers = [
"Programming Language :: Python :: 3",
"License :: OSI Approved :: Apache Software License",
"Operating System :: OS Independent",
]
dependencies = [
"python-dateutil~=2.9",
"requests~=2.25",
]

[project.urls]
Homepage = "http://github.com/dcarrillo/digaws"
Repository = "http://github.com/dcarrillo/digaws"

[project.scripts]
digaws = "digaws.digaws:main"

[dependency-groups]
test = [
"coverage",
"pytest",
"pytest-cov",
"pytest-mock",
"pyfakefs",
]
lint = [
"ruff",
]
typing = [
"mypy",
"types-python-dateutil>=2.9.0.20241206",
"types-requests>=2.32.0.20241016",
]
dev = [
"coverage",
"pytest",
"pytest-cov",
"pytest-mock",
"pyfakefs",
"ruff",
"mypy",
]

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[tool.ruff]
line-length = 100

[tool.ruff.lint]
select = ["E", "W", "F", "I", "UP", "N", "B", "A", "C4"]
ignore = []

[tool.mypy]
ignore_missing_imports = true
install_types = true
non_interactive = true

[tool.pytest.ini_options]
addopts = "--cov=digaws --cov-report=term-missing"
testpaths = ["tests"]

[tool.coverage.report]
show_missing = true
fail_under = 80
2 changes: 0 additions & 2 deletions requirements.txt

This file was deleted.

6 changes: 0 additions & 6 deletions requirements_test.txt

This file was deleted.

Loading