Skip to content

Enhance Logging and Update Test and Coverage Workflows #354

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Apr 12, 2025
Merged
Show file tree
Hide file tree
Changes from 9 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
2 changes: 2 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ exclude_lines =
if __name__ in u"__main__":
if __name__ .. .__main__.:
if __sys_path__ not in sys.path:
if __debug__
os.abort()
exit

Expand All @@ -52,6 +53,7 @@ partial_branches =
if __name__ in u'__main__':
if __name__ in u"__main__":
if __name__ in '__main__':
if __debug__
if __sys_path__ not in sys.path:
# don't complain about sys.modules
sys.modules
Expand Down
24 changes: 20 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
#!/usr/bin/env make -f

# Multicast Python Module
Expand Down Expand Up @@ -212,9 +212,17 @@
$(QUIET)$(RMDIR) ./dist/ 2>$(ERROR_LOG_PATH) || :
$(QUIET)$(RMDIR) ./.eggs/ 2>$(ERROR_LOG_PATH) || :

purge: legacy-purge
purge-test-reports::
$(QUIET)$(RM) ./test-reports/*.xml 2>$(ERROR_LOG_PATH) || :
$(QUIET)$(RMDIR) ./test-reports/ 2>$(ERROR_LOG_PATH) || :

purge-coverage-artifacts: legacy-purge
$(QUIET)$(RM) ./coverage_* 2>$(ERROR_LOG_PATH) || :
$(QUIET)$(RM) ./.coverage.* 2>$(ERROR_LOG_PATH) || :
$(QUIET)$(RM) ./coverage_doctests.xml 2>$(ERROR_LOG_PATH) || :

purge: purge-coverage-artifacts purge-test-reports
$(QUIET)$(WAIT)
$(QUIET)$(ECHO) "$@: Done."

test: just-test
Expand All @@ -223,6 +231,12 @@
$(QUIET)$(COVERAGE) report -m --include=* 2>$(ERROR_LOG_PATH) || : ;
$(QUIET)$(ECHO) "$@: Done."

test-mod: test-mat
$(QUIET)$(DO_FAIL) ;
$(QUIET)$(COVERAGE) combine 2>$(ERROR_LOG_PATH) || : ;
$(QUIET)$(COVERAGE) report -m --include=* 2>$(ERROR_LOG_PATH) || : ;
$(QUIET)$(ECHO) "$@: Done."

test-tox: build
$(QUIET)tox -v -- || tail -n 500 .tox/py*/log/py*.log 2>$(ERROR_LOG_PATH)
$(QUIET)$(COVERAGE) combine 2>$(ERROR_LOG_PATH) || : ;
Expand Down Expand Up @@ -253,7 +267,7 @@
$(QUIET)$(WAIT) ;
$(QUIET)$(DO_FAIL) ;

test-mat: test-mat-build test-mat-bootstrap test-mat-basic test-mat-say test-mat-hear test-mat-usage test-mat-doctests ## Run all minimum acceptance tests
test-mat: cleanup MANIFEST.in test-mat-build test-mat-bootstrap test-mat-basic test-mat-say test-mat-hear test-mat-usage test-mat-doctests ## Run all minimum acceptance tests
$(QUIET)$(WAIT) ;
$(QUIET)$(DO_FAIL) ;

Expand All @@ -263,8 +277,10 @@
$(ECHO) "Try 'make test-mat-doctests' instead."; \
else \
$(COVERAGE) run -p --source=multicast -m tests.run_selective --group mat --category doctests || DO_FAIL="exit 2" ; \
$(COVERAGE) combine 2>$(ERROR_LOG_PATH) || : ; \
$(COVERAGE) xml --include=* 2>$(ERROR_LOG_PATH) || : ; \
$(QUIET)$(WAIT) ; \
$(COVERAGE) combine --keep --data-file=coverage_doctests ./.coverage.* 2>$(ERROR_LOG_PATH) || : ; \
$(COVERAGE) report -m --include=* --data-file=coverage_doctests 2>$(ERROR_LOG_PATH) || : ; \
$(COVERAGE) xml -o coverage_doctests.xml --include=* --data-file=coverage_doctests 2>$(ERROR_LOG_PATH) || : ; \
fi
$(QUIET)$(WAIT) ;
$(QUIET)$(DO_FAIL) ;
Expand Down
25 changes: 25 additions & 0 deletions multicast/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import socket
import struct
import abc
import logging

# skipcq
__all__ = [
Expand Down Expand Up @@ -404,6 +405,13 @@

"""

if logging.__name__ is not None: # pragma: no branch
logging.getLogger(__module__).addHandler(logging.NullHandler())
logging.getLogger(__module__).debug(
"Loading %s", # lazy formatting to avoid PYL-W1203
__module__,
)

if sys.__name__ is None:
raise ModuleNotFoundError(
"FAIL: we could not import sys. We're like in the matrix! ABORT."
Expand All @@ -418,6 +426,10 @@
if socket.__name__ is None:
raise ModuleNotFoundError("FAIL: we could not import socket. ABORT.") from None
else: # pragma: no branch
logging.getLogger(__module__).debug(
"Setting default packet timeout to %n", # lazy formatting to avoid PYL-W1203
_MCAST_DEFAULT_TTL,
)
socket.setdefaulttimeout(int(_MCAST_DEFAULT_TTL))

if struct.__name__ is None:
Expand Down Expand Up @@ -460,6 +472,7 @@
if _config is None:
raise ImportError("FAIL: we could not import environment. ABORT.") from None
else:
logging.getLogger(__module__).debug("Configuring overrides and defaults.")
_MCAST_DEFAULT_PORT = _config["port"]
_MCAST_DEFAULT_GROUP = _config["group"]
_MCAST_DEFAULT_TTL = _config["ttl"]
Expand All @@ -468,6 +481,14 @@
_MCAST_DEFAULT_BIND_IP = _config["bind_addr"]
global _MCAST_DEFAULT_GROUPS # skipcq: PYL-W0604
_MCAST_DEFAULT_GROUPS = _config["groups"]
if __debug__: # pragma: no branch
logging.getLogger(__module__).info("Overrides and defaults are configured.")
logging.getLogger(__module__).debug("Defaults:")
for key, value in _config.items():
logging.getLogger(__module__).debug(
"\t%s=%s", # lazy formatting to avoid PYL-W1203
key, value,
)

del _config # skipcq - cleanup any bootstrap/setup leaks early

Expand Down Expand Up @@ -543,6 +564,10 @@ def buildArgs(cls, calling_parser_group):

"""
if calling_parser_group is None: # pragma: no branch
logging.getLogger(__module__).debug(
"Building %s arguments.", # lazy formatting to avoid PYL-W1203
__name__,
)
calling_parser_group = argparse.ArgumentParser(
prog=str(cls.__name__ if cls.__proc__ is None else cls.__proc__),
description=cls.__prologue__,
Expand Down
58 changes: 57 additions & 1 deletion multicast/env.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@
try:
import os
import warnings
from . import logging # skipcq: PLY-C0414
from . import socket # skipcq: PYL-C0414
import ipaddress
except Exception as err:
Expand All @@ -98,6 +99,17 @@
raise baton from err


module_logger = logging.getLogger(__module__)
module_logger.debug(
"Loading %s", # lazy formatting to avoid PYL-W1203
__module__,
)
module_logger.debug(
"Initializing %s environment.", # lazy formatting to avoid PYL-W1203
__package__,
)


def validate_buffer_size(size: int) -> bool:
"""
Validate if the buffer size is a positive integer.
Expand Down Expand Up @@ -334,26 +346,34 @@ def load_buffer_size() -> int:
"""
# Import globals that we'll potentially update
from multicast import _MCAST_DEFAULT_BUFFER_SIZE
module_logger.debug("Looking for MULTICAST_BUFFER_SIZE in environment.")
try:
buffer_size = int(os.getenv(
"MULTICAST_BUFFER_SIZE",
_MCAST_DEFAULT_BUFFER_SIZE # skipcq: PYL-W1508
))
module_logger.debug("Done.")
except ValueError:
warnings.warn(
f"Invalid MULTICAST_BUFFER_SIZE value, using default {_MCAST_DEFAULT_BUFFER_SIZE}",
stacklevel=2
)
buffer_size = _MCAST_DEFAULT_BUFFER_SIZE # skipcq: PYL-W1508
# Validate and potentially update port
# Validate and potentially update buffer-size
module_logger.debug("Validating MULTICAST_BUFFER_SIZE.")
if validate_buffer_size(buffer_size):
globals()["_MCAST_DEFAULT_BUFFER_SIZE"] = buffer_size
module_logger.debug("Valid.")
else:
warnings.warn(
f"Invalid MULTICAST_BUFFER_SIZE {buffer_size}, using default {_MCAST_DEFAULT_BUFFER_SIZE}",
stacklevel=2
)
buffer_size = _MCAST_DEFAULT_BUFFER_SIZE
module_logger.debug(
"Loaded %s as internal multicast buffer size.", # lazy formatting to avoid PYL-W1203
str(buffer_size),
)
return buffer_size


Expand Down Expand Up @@ -435,22 +455,30 @@ def load_port() -> int:
"""
# Import globals that we'll potentially update
from multicast import _MCAST_DEFAULT_PORT
module_logger.debug("Looking for MULTICAST_PORT in environment.")
try:
port = int(os.getenv("MULTICAST_PORT", _MCAST_DEFAULT_PORT))
module_logger.debug("Done.")
except ValueError:
warnings.warn(
f"Invalid MULTICAST_PORT value, using default {_MCAST_DEFAULT_PORT}", stacklevel=2
)
port = _MCAST_DEFAULT_PORT
# Validate and potentially update port
module_logger.debug("Validating MULTICAST_PORT.")
if validate_port(port):
globals()["_MCAST_DEFAULT_PORT"] = port
module_logger.debug("Valid.")
else:
warnings.warn(
f"Port {port} is outside valid range (49152-65535), using default {_MCAST_DEFAULT_PORT}",
stacklevel=2
)
port = _MCAST_DEFAULT_PORT
module_logger.debug(
"Loaded %s as default multicast port.", # lazy formatting to avoid PYL-W1203
str(port),
)
return port


Expand Down Expand Up @@ -555,15 +583,22 @@ def load_group() -> ipaddress.IPv4Address:
"""
# Import globals that we'll potentially update
from multicast import _MCAST_DEFAULT_GROUP
module_logger.debug("Looking for any MULTICAST_GROUP in environment.")
group = os.getenv("MULTICAST_GROUP", _MCAST_DEFAULT_GROUP)
# Validate and potentially update group
module_logger.debug("Validating either MULTICAST_GROUP or default.")
if validate_multicast_address(group):
globals()["_MCAST_DEFAULT_GROUP"] = group
module_logger.debug("Valid.")
else:
warnings.warn(
f"Invalid multicast group {group}, using default {_MCAST_DEFAULT_GROUP}", stacklevel=2
)
group = _MCAST_DEFAULT_GROUP
module_logger.debug(
"Loaded %s as default multicast group.", # lazy formatting to avoid PYL-W1203
group,
)
return ipaddress.IPv4Address(group)


Expand Down Expand Up @@ -638,24 +673,34 @@ def load_TTL() -> int:
"""
# Import globals that we'll potentially update
from multicast import _MCAST_DEFAULT_TTL
module_logger.debug("Looking for MULTICAST_TTL in environment.")
try:
ttl = int(os.getenv("MULTICAST_TTL", _MCAST_DEFAULT_TTL))
module_logger.debug("Done.")
except ValueError:
warnings.warn(
f"Invalid MULTICAST_TTL value, using default {_MCAST_DEFAULT_TTL}", stacklevel=2
)
ttl = _MCAST_DEFAULT_TTL
# Validate and potentially update TTL
module_logger.debug("Validating MULTICAST_TTL.")
if validate_ttl(ttl):
globals()["_MCAST_DEFAULT_TTL"] = ttl
module_logger.debug("Valid.")
else:
warnings.warn(
f"TTL {ttl} is outside valid range (1-126), using default {_MCAST_DEFAULT_TTL}",
stacklevel=2
)
ttl = _MCAST_DEFAULT_TTL
module_logger.debug(
"Loaded %d as default multicast time-to-live.", # lazy formatting to avoid PYL-W1203
ttl,
)
# Update socket default timeout
module_logger.debug("Update socket default timeout.")
socket.setdefaulttimeout(int(ttl))
module_logger.debug("Updated.")
return ttl


Expand Down Expand Up @@ -857,13 +902,19 @@ def load_config() -> dict:

"""
# Load values from environment with defaults
module_logger.info("Loading multicast overrides from environment.")
port = load_port()
group = load_group()
ttl = load_TTL()
buffer_size = load_buffer_size()
module_logger.debug("Looking for MULTICAST_GROUPS in environment.")
groups_str = os.getenv("MULTICAST_GROUPS", "")
module_logger.debug("Done.")
module_logger.debug("Looking for MULTICAST_BIND_ADDR in environment.")
bind_addr = os.getenv("MULTICAST_BIND_ADDR", str(group)) # skipcq: PYL-W1508
module_logger.debug("Done.")
# Process and validate groups
module_logger.debug("Processing and validating groups.")
groups = set()
if groups_str:
for addr in groups_str.split():
Expand All @@ -875,9 +926,14 @@ def load_config() -> dict:
)
# Always include the primary group
groups.add(str(group))
module_logger.debug("Processed groups.")
# Include bind_addr if it's a valid multicast address
module_logger.debug("Processing and validating bind-address.")
if validate_multicast_address(bind_addr):
module_logger.debug("Adding multicast bind-address to groups.")
groups.add(str(bind_addr))
module_logger.debug("Processed bind-address.")
module_logger.debug("Overrides and defaults are ready to configure.")
return {
"port": port,
"group": str(group),
Expand Down
Loading