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

[WIP] Centralize yaml configurations into conda_build.yaml #5285

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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
14 changes: 8 additions & 6 deletions conda_build/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,15 @@
from pathlib import Path

import conda_package_handling.api
import yaml
from bs4 import UnicodeDammit
from conda import __version__ as conda_version
from conda.base.context import context, reset_context
from conda.core.prefix_data import PrefixData
from conda.exceptions import CondaError, NoPackagesFoundError, UnsatisfiableError
from conda.models.channel import Channel

from conda_build import yaml

from . import __version__ as conda_build_version
from . import environ, noarch_python, source, tarcheck, utils
from .conda_interface import (
Expand Down Expand Up @@ -882,7 +883,7 @@ def copy_recipe(m):

# dump the full variant in use for this package to the recipe folder
with open(os.path.join(recipe_dir, "conda_build_config.yaml"), "w") as f:
yaml.dump(m.config.variant, f)
yaml.safe_dump(m.config.variant, f)


def copy_readme(m):
Expand Down Expand Up @@ -918,11 +919,12 @@ def jsonify_info_yamls(m):
except:
pass
with open(file) as i, open(dst, "w") as o:
import yaml

yaml = yaml.full_load(i)
json.dump(
yaml, o, sort_keys=True, indent=2, separators=(",", ": ")
yaml.safe_load(i),
o,
sort_keys=True,
indent=2,
separators=(",", ": "),
)
res.append(
join(os.path.basename(m.config.info_dir), ijd, bn + ".json")
Expand Down
3 changes: 3 additions & 0 deletions conda_build/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
# SPDX-License-Identifier: BSD-3-Clause
import textwrap

from .deprecations import deprecated

SEPARATOR = "-" * 70

indent = lambda s: textwrap.fill(textwrap.dedent(s))
Expand Down Expand Up @@ -42,6 +44,7 @@ def indented_exception(self):
return f"Error Message:\n--> {indent(orig)}\n\n"


@deprecated("24.5", "24.7", addendum="Unused.")
class UnableToParseMissingJinja2(UnableToParse):
def error_body(self):
return "\n".join(
Expand Down
61 changes: 12 additions & 49 deletions conda_build/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@
from conda.gateways.disk.read import compute_sum
from frozendict import deepfreeze

from . import exceptions, utils, variants
from . import exceptions, utils, variants, yaml
from .conda_interface import MatchSpec
from .config import Config, get_or_merge_config
from .deprecations import deprecated
from .features import feature_list
from .license_family import ensure_valid_license_family
from .utils import (
Expand All @@ -34,50 +35,19 @@
insert_variant_versions,
on_win,
)
from .yaml import _StringifyNumbersLoader

if TYPE_CHECKING:
from typing import Literal

try:
import yaml
except ImportError:
sys.exit(
"Error: could not import yaml (required to read meta.yaml "
"files of conda recipes)"
)

try:
Loader = yaml.CLoader
except AttributeError:
Loader = yaml.Loader


class StringifyNumbersLoader(Loader):
@classmethod
def remove_implicit_resolver(cls, tag):
if "yaml_implicit_resolvers" not in cls.__dict__:
cls.yaml_implicit_resolvers = {
k: v[:] for k, v in cls.yaml_implicit_resolvers.items()
}
for ch in tuple(cls.yaml_implicit_resolvers):
resolvers = [(t, r) for t, r in cls.yaml_implicit_resolvers[ch] if t != tag]
if resolvers:
cls.yaml_implicit_resolvers[ch] = resolvers
else:
del cls.yaml_implicit_resolvers[ch]

@classmethod
def remove_constructor(cls, tag):
if "yaml_constructors" not in cls.__dict__:
cls.yaml_constructors = cls.yaml_constructors.copy()
if tag in cls.yaml_constructors:
del cls.yaml_constructors[tag]

deprecated.constant(
"24.5",
"24.7",
"StringifyNumbersLoader",
_StringifyNumbersLoader,
addendum="Use `conda_build.yaml._StringifyNumbersLoader` instead.",
)

StringifyNumbersLoader.remove_implicit_resolver("tag:yaml.org,2002:float")
StringifyNumbersLoader.remove_implicit_resolver("tag:yaml.org,2002:int")
StringifyNumbersLoader.remove_constructor("tag:yaml.org,2002:float")
StringifyNumbersLoader.remove_constructor("tag:yaml.org,2002:int")

# arches that don't follow exact names in the subdir need to be mapped here
ARCH_MAP = {"32": "x86", "64": "x86_64"}
Expand Down Expand Up @@ -305,15 +275,8 @@ def select_lines(data, namespace, variants_in_place):

def yamlize(data):
try:
return yaml.load(data, Loader=StringifyNumbersLoader)
except yaml.error.YAMLError as e:
if "{{" in data:
try:
import jinja2

jinja2 # Avoid pyflakes failure: 'jinja2' imported but unused
except ImportError:
raise exceptions.UnableToParseMissingJinja2(original=e)
return yaml.safe_load(data, stringify_numbers=True)
except yaml.YAMLError as e:
print("Problematic recipe:", file=sys.stderr)
print(data, file=sys.stderr)
raise exceptions.UnableToParse(original=e)
Expand Down
66 changes: 28 additions & 38 deletions conda_build/render.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from __future__ import annotations

import json
import os
import random
import re
import string
Expand All @@ -15,7 +14,6 @@
from contextlib import contextmanager
from functools import lru_cache
from os.path import (
dirname,
isabs,
isdir,
isfile,
Expand All @@ -25,13 +23,14 @@
from pathlib import Path
from typing import TYPE_CHECKING

import yaml
import yaml as pyyaml
from conda.base.context import context
from conda.core.package_cache_data import ProgressiveFetchExtract
from conda.exceptions import UnsatisfiableError

from . import environ, exceptions, source, utils
from . import environ, exceptions, source, utils, yaml
from .conda_interface import PackageRecord, TemporaryDirectory, specs_from_url
from .deprecations import deprecated
from .exceptions import DependencyNeedsBuildingError
from .index import get_build_index
from .metadata import MetaData, combine_top_level_metadata_with_output
Expand All @@ -47,20 +46,17 @@
)

if TYPE_CHECKING:
import os
from typing import Iterator

from .config import Config


@deprecated("24.5", "24.7")
def odict_representer(dumper, data):
return dumper.represent_dict(data.items())


yaml.add_representer(set, yaml.representer.SafeRepresenter.represent_list)
yaml.add_representer(tuple, yaml.representer.SafeRepresenter.represent_list)
yaml.add_representer(OrderedDict, odict_representer)


def bldpkg_path(m):
"""
Returns path to built package's tarball given its ``Metadata``.
Expand Down Expand Up @@ -1026,57 +1022,51 @@ def render_recipe(

# Next bit of stuff is to support YAML output in the order we expect.
# http://stackoverflow.com/a/17310199/1170370
@deprecated("24.5", "24.7")
class _MetaYaml(dict):
fields = FIELDS

def to_omap(self):
return [(field, self[field]) for field in _MetaYaml.fields if field in self]


@deprecated("24.5", "24.7")
def _represent_omap(dumper, data):
return dumper.represent_mapping("tag:yaml.org,2002:map", data.to_omap())


@deprecated("24.5", "24.7")
def _unicode_representer(dumper, uni):
node = yaml.ScalarNode(tag="tag:yaml.org,2002:str", value=uni)
node = pyyaml.ScalarNode(tag="tag:yaml.org,2002:str", value=uni)
return node


class _IndentDumper(yaml.Dumper):
@deprecated("24.5", "24.7")
class _IndentDumper(pyyaml.Dumper):
def increase_indent(self, flow=False, indentless=False):
return super().increase_indent(flow, False)

def ignore_aliases(self, data):
return True


yaml.add_representer(_MetaYaml, _represent_omap)
yaml.add_representer(str, _unicode_representer)
unicode = None # silence pyflakes about unicode not existing in py3
def output_yaml(
metadata: MetaData,
filename: str | os.PathLike | Path | None = None,
suppress_outputs: bool = False,
) -> str:
meta = metadata.meta
# create a manually ordered copy of the meta dict
meta = {field: meta[field] for field in FIELDS if field in meta}
if suppress_outputs and metadata.is_output and "outputs" in meta:
del meta["outputs"]

output = yaml.safe_dump(meta)

def output_yaml(metadata, filename=None, suppress_outputs=False):
local_metadata = metadata.copy()
if (
suppress_outputs
and local_metadata.is_output
and "outputs" in local_metadata.meta
):
del local_metadata.meta["outputs"]
output = yaml.dump(
_MetaYaml(local_metadata.meta),
Dumper=_IndentDumper,
default_flow_style=False,
indent=2,
)
if filename:
if any(sep in filename for sep in ("\\", "/")):
try:
os.makedirs(dirname(filename))
except OSError:
pass
with open(filename, "w") as f:
f.write(output)
return "Wrote yaml to %s" % filename
else:
if not filename:
return output

filename = Path(filename)
filename.parent.mkdir(parents=True, exist_ok=True)
filename.write_text(output)
return "Wrote yaml to %s" % filename
12 changes: 2 additions & 10 deletions conda_build/skeletons/cran.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,10 @@
realpath,
relpath,
)
from typing import TYPE_CHECKING

import requests
import yaml

# try to import C dumper
try:
from yaml import CSafeDumper as SafeDumper
except ImportError:
from yaml import SafeDumper

from typing import TYPE_CHECKING

from conda.common.io import dashlist

from .. import source
Expand Down Expand Up @@ -564,7 +556,7 @@ def yaml_quote_string(string):
Note that this function is NOT general.
"""
return (
yaml.dump(string, indent=True, Dumper=SafeDumper)
yaml.safe_dump(string, indent=True)
.replace("\n...\n", "")
.replace("\n", "\n ")
.rstrip("\n ")
Expand Down
4 changes: 2 additions & 2 deletions conda_build/variants.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@
from functools import lru_cache
from itertools import product

import yaml
from conda.base.context import context

from . import yaml
from .conda_interface import cc_conda_build
from .utils import ensure_list, get_logger, islist, on_win, trim_empty_keys
from .version import _parse as parse_version
Expand Down Expand Up @@ -136,7 +136,7 @@ def parse_config_file(path, config):
with open(path) as f:
contents = f.read()
contents = select_lines(contents, get_selectors(config), variants_in_place=False)
content = yaml.load(contents, Loader=yaml.loader.BaseLoader) or {}
content = yaml.safe_load(contents) or {}
trim_empty_keys(content)
return content

Expand Down
Loading
Loading