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

merge: basic parsing of GH Action #15

Merged
merged 2 commits into from
Oct 21, 2022
Merged
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
140 changes: 129 additions & 11 deletions conda_envfile/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1005,7 +1005,7 @@ def parse_file(*args: list[str]) -> dict:
Parse one or more files and return the raw result.

:param args: List of filenames to parse.
:return: Raw result.
:return: Raw result: ``{"name": [...], "channels": [...], "dependencies": [...]}``
"""

env = {"name": [], "channels": [], "dependencies": []}
Expand Down Expand Up @@ -1038,11 +1038,105 @@ def parse_file(*args: list[str]) -> dict:
else:
del env["name"]

if len(env["channels"]) == 0:
del env["channels"]

env["dependencies"] = [PackageSpecifier(i) for i in env["dependencies"]]

return env


def _iterate_nested_dict(mydict: dict):
for key, value in mydict.items():
yield key, value
if isinstance(value, dict):
yield from _iterate_nested_dict(value)


def apply_selector(text: str) -> str:
"""
Apply platform selector::

sel(linux): ...
sel(osx): ...
sel(win): ...

based on current platform.

:param text: Package specifier with optional selector.
:return: Package specifier. Returns ``None`` is excluded by selector.
"""

if re.match(r"(\s*)(sel\(\w*\)\:)(.*)", text):
_, _, _, plat, _, package, _ = re.split(r"(\s*)(sel\()(\w*)(\)\:\s*)(.*)", text)
if (sys.platform == "linux" or sys.platform == "linux2") and plat == "linux":
return package
elif sys.platform == "darwin" and plat == "osx":
return package
elif sys.platform == "win32" and plat == "win":
return package
else:
return None

return text


def filter_selectors(deps: list[str]) -> list[str]:
"""
Filter selectors from dependencies.

:param deps: List of dependencies (text).
:return: List of dependencies (text) without selectors, and dependencies excluded by selectors.
"""
return [apply_selector(i) for i in deps if apply_selector(i)]


def parse_github_action(text: str) -> dict:
"""
Parse a GitHub action.
Currently very basic. Looks for the first occurrence of ``mamba-org/provision-with-micromamba``.

:param filename: Filename to parse.
:return: Raw result: ``{"name": [...], "channels": [...], "dependencies": [...]}``
"""

if "mamba-org/provision-with-micromamba" in text:
data = text.split("mamba-org/provision-with-micromamba")[1].split("with:")[1]
data = data.split("\n")
if len(data[0].strip()) == 0:
data = data[1:]
indent = len(re.split(r"(\s*)(.*)", data[0])[1])
select = []
for line in data:
if len(re.split(r"(\s*)(.*)", line)[1]) < indent:
break
if re.match(r"(\s*)(-\s)(.*)", line):
break
select += [line]
data = yaml.load("\n".join(select), Loader=yaml.FullLoader)

for key in data:
if type(data[key]) == str:
data[key] = data[key].split("\n")

if "environment-file" in data:
env = parse_file(*data["environment-file"])
else:
env = {"dependencies": []}
if "environment-name" in data:
env["name"] = data["environment-name"]

if "extra-specs" in data:
for deps in data["extra-specs"]:
deps = apply_selector(deps)
if deps:
env["dependencies"].append(deps)

return env

return {}


def _conda_envfile_parse_parser():
"""
Return parser for :py:func:`conda_envfile_parse`.
Expand Down Expand Up @@ -1123,8 +1217,15 @@ class MyFmt(
parser.add_argument("-o", "--output", type=str, help="Write to output file.")
parser.add_argument("-a", "--append", type=str, action="append", default=[], help="Append deps")
parser.add_argument("-r", "--remove", type=str, action="append", default=[], help="Remove deps")
parser.add_argument("--version", action="version", version=version)
parser.add_argument("files", type=str, nargs="*", help="Input files.")
parser.add_argument("--no-name", action="store_true", help="Remove name from output.")
parser.add_argument(
"--github-action",
type=str,
action="append",
default=[],
help="Interpret file as GitHub action",
)
parser.add_argument("files", type=str, nargs="*", help="Input file(s).")
return parser


Expand All @@ -1138,13 +1239,33 @@ def conda_envfile_merge(args: list[str]):
parser = _conda_envfile_merge_parser()
args = parser.parse_args(args)
env = parse_file(*args.files)
env["dependencies"] = unique(*(env["dependencies"] + args.append))

if args.github_action:
for filename in args.github_action:
with open(filename) as file:
extra = parse_github_action(file.read())
for key in extra:
if key not in env:
env[key] = extra[key]
else:
env[key] += extra[key]

env["dependencies"] = unique(*(env["dependencies"] + filter_selectors(args.append)))

if args.remove:
env["dependencies"] = remove(env["dependencies"], *args.remove)
env["dependencies"] = remove(env["dependencies"], *filter_selectors(args.remove))

env["dependencies"] = list(map(str, env["dependencies"]))

for key in env:
if key == "dependencies":
continue
if type(env[key]) == list:
env[key] = list(set(env[key]))

if args.no_name:
env.pop("name", None)

if not args.output:
print(yaml.dump(env, default_flow_style=False, default_style="").strip())
return 0
Expand Down Expand Up @@ -1204,7 +1325,7 @@ class MyFmt(
action="append",
help="Interpret the next file (``source`` or ``comparison``) as conda-forge feedstock.",
)
parser.add_argument("-a", "--append", type=str, action="append", help="Append dependencies.")
parser.add_argument("-a", "--append", type=str, action="append", default=[], help="Append deps")
parser.add_argument("--version", action="version", version=version)
parser.add_argument("source", type=str, nargs="?", help="Input file.")
parser.add_argument("comparison", type=str, nargs="*", help="Comparison file(s).")
Expand All @@ -1221,9 +1342,6 @@ def conda_envfile_restrict(args: list[str]):
parser = _conda_envfile_restrict_parser()
args = parser.parse_args(args)

if args.append is None:
args.append = []

if len(args.conda_forge) > 0:
other = []
with open(args.conda_forge[0]) as file:
Expand All @@ -1235,7 +1353,7 @@ def conda_envfile_restrict(args: list[str]):
for filename in args.conda_forge[1:]:
with open(filename) as file:
other += condaforge_dependencies(file.read())
ret = restrict(source, unique(*(other + args.append)))
ret = restrict(source, unique(*(other + filter_selectors(args.append))))
if ret != source:
print("Difference found")
print("\n".join([i for i in ret if i not in source]))
Expand All @@ -1249,7 +1367,7 @@ def conda_envfile_restrict(args: list[str]):
for filename in args.comparison:
other += parse_file(filename)["dependencies"]

env["dependencies"] = restrict(source, unique(*(other + args.append)))
env["dependencies"] = restrict(source, unique(*(other + filter_selectors(args.append))))
env["dependencies"] = list(map(str, *env["dependencies"]))

if not args.output:
Expand Down