Skip to content
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

Move CLI's logging initialization into a dedicated module #5275

Draft
wants to merge 17 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
21 changes: 5 additions & 16 deletions conda_build/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import fnmatch
import json
import logging
import os
import random
import re
Expand Down Expand Up @@ -89,6 +90,8 @@
if on_win:
from . import windows

log = logging.getLogger(__name__)

if "bsd" in sys.platform:
shell_path = "/bin/sh"
elif utils.on_win:
Expand Down Expand Up @@ -265,7 +268,6 @@ def have_prefix_files(files, prefix):
try:
fi = open(path, "rb+")
except OSError:
log = utils.get_logger(__name__)
log.warn("failed to open %s for detecting prefix. Skipping it." % f)
continue
try:
Expand Down Expand Up @@ -1034,7 +1036,6 @@ def copy_test_source_files(m, destination):
clobber=True,
)
except OSError as e:
log = utils.get_logger(__name__)
log.warn(
f"Failed to copy {f} into test files. Error was: {str(e)}"
)
Expand Down Expand Up @@ -1414,7 +1415,6 @@ def write_about_json(m):
extra = m.get_section("extra")
# Add burn-in information to extra
if m.config.extra_meta:
log = utils.get_logger(__name__)
log.info(
"Adding the following extra-meta data to about.json: %s",
m.config.extra_meta,
Expand Down Expand Up @@ -1721,7 +1721,6 @@ def post_process_files(m: MetaData, initial_prefix_files):
if not os.path.exists(os.path.join(host_prefix, f)):
missing.append(f)
if len(missing):
log = utils.get_logger(__name__)
log.warning(
f"The install/build script(s) for {package_name} deleted the following "
f"files (from dependencies) from the prefix:\n{missing}\n"
Expand Down Expand Up @@ -1804,7 +1803,6 @@ def post_process_files(m: MetaData, initial_prefix_files):


def bundle_conda(output, metadata: MetaData, env, stats, **kw):
log = utils.get_logger(__name__)
log.info("Packaging %s", metadata.dist())
get_all_replacements(metadata.config)
files = output.get("files", [])
Expand Down Expand Up @@ -2265,7 +2263,6 @@ def _write_activation_text(script_path, m):
elif os.path.splitext(script_path)[1].lower() == ".sh":
_write_sh_activation_text(fh, m)
else:
log = utils.get_logger(__name__)
log.warn(
f"not adding activation to {script_path} - I don't know how to do so for "
"this file type"
Expand Down Expand Up @@ -2408,7 +2405,6 @@ def build(
print(utils.get_skip_message(m))
return default_return

log = utils.get_logger(__name__)
host_precs = []
build_precs = []
output_metas = []
Expand Down Expand Up @@ -2943,7 +2939,6 @@ def _construct_metadata_for_test_from_recipe(recipe_dir, config):
metadata = expand_outputs(
render_recipe(recipe_dir, config=config, reset_build_id=False)
)[0][1]
log = utils.get_logger(__name__)
log.warn(
"Testing based on recipes is deprecated as of conda-build 3.16.0. Please adjust "
"your code to pass your desired conda package to test instead."
Expand Down Expand Up @@ -2981,8 +2976,6 @@ def _construct_metadata_for_test_from_package(package, config):
# This is still necessary for computing the hash correctly though
config.variant = hash_input

log = utils.get_logger(__name__)

# get absolute file location
local_pkg_location = os.path.normpath(os.path.abspath(os.path.dirname(package)))

Expand Down Expand Up @@ -3183,7 +3176,6 @@ def _write_test_run_script(
shell_files,
trace,
):
log = utils.get_logger(__name__)
with open(test_run_script, "w") as tf:
tf.write(
'{source} "{test_env_script}"\n'.format(
Expand Down Expand Up @@ -3342,7 +3334,6 @@ def test(
:param m: Package's metadata.
:type m: Metadata
"""
log = utils.get_logger(__name__)
# we want to know if we're dealing with package input. If so, we can move the input on success.
hash_input = {}

Expand Down Expand Up @@ -3625,7 +3616,6 @@ def tests_failed(package_or_metadata, move_broken, broken_dir, config):
dest = join(broken_dir, os.path.basename(pkg))

if move_broken:
log = utils.get_logger(__name__)
try:
shutil.move(pkg, dest)
log.warn(
Expand Down Expand Up @@ -3784,7 +3774,6 @@ def build_tree(
)
]
)
log = utils.get_logger(__name__)
# downstreams can be a dict, for adding capability for worker labels
if hasattr(downstreams, "keys"):
downstreams = list(downstreams.keys())
Expand Down Expand Up @@ -4121,11 +4110,11 @@ def handle_pypi_upload(wheels, config):
try:
utils.check_call_env(args + [f])
except:
utils.get_logger(__name__).warn(
log.warn(
"wheel upload failed - is twine installed?"
" Is this package registered?"
)
utils.get_logger(__name__).warn(f"Wheel file left in {f}")
log.warn(f"Wheel file left in {f}")

else:
print(f"anaconda_upload is not set. Not uploading wheels: {wheels}")
Expand Down
76 changes: 76 additions & 0 deletions conda_build/cli/logging.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Copyright (C) 2014 Anaconda, Inc
# SPDX-License-Identifier: BSD-3-Clause
from __future__ import annotations

import logging
import logging.config
import os
import os.path
import sys
from pathlib import Path
from typing import TYPE_CHECKING

from conda.base.context import context
from yaml import safe_load

if TYPE_CHECKING:
from logging import LogRecord


# https://stackoverflow.com/a/31459386/1170370
class LessThanFilter(logging.Filter):
def __init__(self, exclusive_maximum: int, name: str = "") -> None:
super().__init__(name)
self.max_level = exclusive_maximum

def filter(self, record: LogRecord) -> bool:
return record.levelno < self.max_level


class GreaterThanFilter(logging.Filter):
def __init__(self, exclusive_minimum: int, name: str = "") -> None:
super().__init__(name)
self.min_level = exclusive_minimum

def filter(self, record: LogRecord) -> bool:
return record.levelno > self.min_level


class DuplicateFilter(logging.Filter):
msgs: set[str] = set()

def filter(self, record: LogRecord) -> bool:
try:
return record.msg not in self.msgs
finally:
self.msgs.add(record.msg)


def init_logging() -> None:
"""
Default initialization of logging for conda-build CLI.

When using conda-build as a CLI tool (not as a library) we wish to limit logging to
avoid duplication and to otherwise offer some default behavior.
"""
config_file = context.conda_build.get("log_config_file")
if config_file:
config_file = Path(os.path.expandvars(config_file)).expanduser().resolve()
logging.config.dictConfig(safe_load(config_file.read_text()))

log = logging.getLogger("conda_build")

# we don't want propagation in CLI, but we do want it in tests
# this is a pytest limitation: https://github.com/pytest-dev/pytest/issues/3697
log.propagate = "PYTEST_CURRENT_TEST" in os.environ

if not log.handlers:
log.addHandler(stdout := logging.StreamHandler(sys.stdout))
stdout.addFilter(LessThanFilter(logging.WARNING))
stdout.addFilter(DuplicateFilter())
stdout.setFormatter(logging.Formatter("%(levelname)s: %(message)s"))

log.addHandler(stderr := logging.StreamHandler(sys.stderr))
stderr.addFilter(GreaterThanFilter(logging.INFO))
stderr.addFilter(DuplicateFilter())
stderr.setFormatter(logging.Formatter("%(levelname)s: %(message)s"))
5 changes: 5 additions & 0 deletions conda_build/cli/main_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
)
from ..utils import LoggingContext
from .actions import KeyValueAction
from .logging import init_logging
from .main_render import get_render_parser

if TYPE_CHECKING:
Expand All @@ -33,6 +34,8 @@

from ..conda_interface import ArgumentParser

log = logging.getLogger(__name__)


def parse_args(args: Sequence[str] | None) -> tuple[ArgumentParser, Namespace]:
parser = get_render_parser()
Expand Down Expand Up @@ -524,6 +527,8 @@ def check_action(recipe, config):


def execute(args: Sequence[str] | None = None) -> int:
init_logging()

_, parsed = parse_args(args)
config = get_or_merge_config(None, **parsed.__dict__)
build.check_external()
Expand Down
5 changes: 4 additions & 1 deletion conda_build/cli/main_convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@

from .. import api
from ..conda_interface import ArgumentParser
from .logging import init_logging

if TYPE_CHECKING:
from argparse import Namespace
from typing import Sequence

logging.basicConfig(level=logging.INFO)
log = logging.getLogger(__name__)

epilog = """

Expand Down Expand Up @@ -124,6 +125,8 @@ def parse_args(args: Sequence[str] | None) -> tuple[ArgumentParser, Namespace]:


def execute(args: Sequence[str] | None = None) -> int:
init_logging()

_, parsed = parse_args(args)
files = parsed.files
del parsed.__dict__["files"]
Expand Down
5 changes: 4 additions & 1 deletion conda_build/cli/main_debug.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@
from .. import api
from ..utils import on_win
from . import validators as valid
from .logging import init_logging
from .main_render import get_render_parser

if TYPE_CHECKING:
from argparse import ArgumentParser
from typing import Sequence

logging.basicConfig(level=logging.INFO)
log = logging.getLogger(__name__)


def get_parser() -> ArgumentParser:
Expand Down Expand Up @@ -92,6 +93,8 @@ def get_parser() -> ArgumentParser:


def execute(args: Sequence[str] | None = None) -> int:
init_logging()

parser = get_parser()
parsed = parser.parse_args(args)

Expand Down
5 changes: 4 additions & 1 deletion conda_build/cli/main_develop.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@

from .. import api
from ..conda_interface import ArgumentParser, add_parser_prefix
from .logging import init_logging

if TYPE_CHECKING:
from argparse import Namespace
from typing import Sequence

logging.basicConfig(level=logging.INFO)
log = logging.getLogger(__name__)


def parse_args(args: Sequence[str] | None) -> tuple[ArgumentParser, Namespace]:
Expand Down Expand Up @@ -80,6 +81,8 @@ def parse_args(args: Sequence[str] | None) -> tuple[ArgumentParser, Namespace]:


def execute(args: Sequence[str] | None = None) -> int:
init_logging()

_, parsed = parse_args(args)
prefix = determine_target_prefix(context, parsed)
api.develop(
Expand Down
5 changes: 4 additions & 1 deletion conda_build/cli/main_inspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@

from .. import api
from ..conda_interface import ArgumentParser, add_parser_prefix
from .logging import init_logging

if TYPE_CHECKING:
from argparse import Namespace
from typing import Sequence

logging.basicConfig(level=logging.INFO)
log = logging.getLogger(__name__)


def parse_args(args: Sequence[str] | None) -> tuple[ArgumentParser, Namespace]:
Expand Down Expand Up @@ -188,6 +189,8 @@ def parse_args(args: Sequence[str] | None) -> tuple[ArgumentParser, Namespace]:


def execute(args: Sequence[str] | None = None) -> int:
init_logging()

parser, parsed = parse_args(args)

if not parsed.subcommand:
Expand Down
5 changes: 4 additions & 1 deletion conda_build/cli/main_metapackage.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@

from .. import api
from ..conda_interface import ArgumentParser, add_parser_channels
from .logging import init_logging

if TYPE_CHECKING:
from argparse import Namespace
from typing import Sequence

logging.basicConfig(level=logging.INFO)
log = logging.getLogger(__name__)


def parse_args(args: Sequence[str] | None) -> tuple[ArgumentParser, Namespace]:
Expand Down Expand Up @@ -114,6 +115,8 @@ def parse_args(args: Sequence[str] | None) -> tuple[ArgumentParser, Namespace]:


def execute(args: Sequence[str] | None = None) -> int:
init_logging()

_, args = parse_args(args)
channel_urls = args.__dict__.get("channel") or args.__dict__.get("channels") or ()
api.create_metapackage(channel_urls=channel_urls, **args.__dict__)
Expand Down
3 changes: 3 additions & 0 deletions conda_build/cli/main_render.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from ..config import get_channel_urls, get_or_merge_config
from ..utils import LoggingContext
from ..variants import get_package_variants, set_language_env_vars
from .logging import init_logging

if TYPE_CHECKING:
from argparse import Namespace
Expand Down Expand Up @@ -193,6 +194,8 @@ def parse_args(args: Sequence[str] | None) -> tuple[ArgumentParser, Namespace]:


def execute(args: Sequence[str] | None = None) -> int:
init_logging()

_, parsed = parse_args(args)

config = get_or_merge_config(None, **parsed.__dict__)
Expand Down