Skip to content
This repository was archived by the owner on Mar 10, 2024. It is now read-only.

Commit 9d30382

Browse files
authored
Merge pull request #75 from regro/dot-conda
ENH support dot conda
2 parents e03f5ec + 95a6990 commit 9d30382

File tree

4 files changed

+85
-47
lines changed

4 files changed

+85
-47
lines changed

libcflib/harvester.py

Lines changed: 66 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -43,64 +43,87 @@ def yaml_construct_fallback(loader, node):
4343
)
4444

4545

46+
def harvest_dot_conda(io_like, filename):
47+
from conda_package_streaming import package_streaming
48+
stream = package_streaming.stream_conda_component(
49+
filename, io_like, component=package_streaming.CondaComponent.info
50+
)
51+
data = harvest_tarfile(stream)
52+
stream.close()
53+
54+
return data
55+
56+
4657
def harvest(io_like):
4758
tf = tarfile.open(fileobj=io_like, mode="r:bz2")
48-
49-
# info/files
50-
file_listing = tf.extractfile("info/files").readlines()
51-
file_listing = (fn.decode("utf8").strip() for fn in file_listing if fn)
52-
file_listing = [fn for fn in file_listing if filter_file(fn)]
53-
54-
# info/recipe/meta.yaml
55-
try:
56-
rendered_recipe = ruamel_yaml.safe_load(tf.extractfile("info/recipe/meta.yaml"))
57-
except ScannerError:
58-
# Non parseable
59-
rendered_recipe = {}
60-
except KeyError:
61-
# older artifacts have a meta.yaml in another location
62-
try:
63-
rendered_recipe = ruamel_yaml.safe_load(tf.extractfile("info/meta.yaml"))
64-
except ScannerError:
65-
# Non parseable
66-
rendered_recipe = {}
67-
68-
try:
69-
raw_recipe = (
70-
tf.extractfile("info/recipe/meta.yaml.template").read().decode("utf8")
71-
)
72-
except KeyError:
73-
raw_recipe = tf.extractfile("info/recipe/meta.yaml").read().decode("utf8")
74-
75-
try:
76-
conda_build_config = ruamel_yaml.safe_load(
77-
tf.extractfile("info/recipe/conda_build_config.yaml")
78-
)
79-
except KeyError:
80-
conda_build_config = {}
81-
82-
try:
83-
about = json.load(tf.extractfile("info/about.json"))
84-
except KeyError:
85-
about = {}
86-
index = json.load(tf.extractfile("info/index.json"))
59+
return harvest_tarfile(tf)
60+
61+
62+
def harvest_tarfile(tf_or_stream):
63+
rendered_recipe = {}
64+
index = {}
65+
about = {}
66+
raw_recipe = ""
67+
conda_build_config = {}
68+
raw_recipe_backup = ""
69+
70+
for _data in tf_or_stream:
71+
if isinstance(_data, tarfile.TarInfo):
72+
mem = _data
73+
tf = tf_or_stream
74+
else:
75+
tf, mem = _data
76+
77+
if mem.name == "info/files":
78+
# info/files
79+
file_listing = tf.extractfile(mem).readlines()
80+
file_listing = [fn.decode("utf8").strip() for fn in file_listing if fn]
81+
file_listing = [fn for fn in file_listing if filter_file(fn)]
82+
elif mem.name == "info/recipe/meta.yaml":
83+
raw_recipe_backup = tf.extractfile(mem).read(mem.size).decode("utf8")
84+
# info/recipe/meta.yaml
85+
try:
86+
rendered_recipe = ruamel_yaml.safe_load(raw_recipe_backup)
87+
except ScannerError:
88+
# Non parseable
89+
rendered_recipe = {}
90+
elif mem.name == "info/meta.yaml":
91+
# older artifacts have a meta.yaml in another location
92+
try:
93+
rendered_recipe = ruamel_yaml.safe_load(tf.extractfile(mem).read(mem.size))
94+
except ScannerError:
95+
# Non parseable
96+
rendered_recipe = {}
97+
elif mem.name == "info/about.json":
98+
about = json.loads(tf.extractfile(mem).read(mem.size))
99+
elif mem.name == "info/index.json":
100+
index = json.loads(tf.extractfile(mem).read(mem.size))
101+
elif mem.name == "info/recipe/meta.yaml.template":
102+
raw_recipe = tf.extractfile(mem).read(mem.size).decode("utf8")
103+
elif mem.name == "info/recipe/conda_build_config.yaml":
104+
conda_build_config = ruamel_yaml.safe_load(tf.extractfile(mem).read(mem.size))
87105

88106
return {
89107
"metadata_version": METADATA_VERSION,
90-
"name": index["name"],
91-
"version": index["version"],
108+
"name": index.get("name", ""),
109+
"version": index.get("version", ""),
92110
"index": index,
93111
"about": about,
94112
"rendered_recipe": rendered_recipe,
95-
"raw_recipe": raw_recipe,
113+
"raw_recipe": raw_recipe if len(raw_recipe) > 0 else raw_recipe_backup,
96114
"conda_build_config": conda_build_config,
97115
"files": file_listing,
98116
}
99117

100118

101119
def harvest_from_filename(filename):
102120
with open(filename, "rb") as fo:
103-
return harvest(fo)
121+
if filename.endswith(".tar.bz2"):
122+
return harvest(fo)
123+
elif filename.endswith(".conda"):
124+
return harvest_dot_conda(fo, filename)
125+
else:
126+
raise RuntimeError(f"File '{filename}' is not a recognized conda format!")
104127

105128

106129
if __name__ == "__main__":

libcflib/models.xsh

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ class Artifact(Model):
113113
arch : str or None, optional
114114
Architeture name
115115
name : str or None, optional
116-
Fully resolved name, without *.tar.bz2 or *.json
116+
Fully resolved name, without *.conda, *.tar.bz2 or *.json
117117
path : str or None, optional
118118
Path to artifact file. This is relative to the libcfgraph repo dir.
119119
"""
@@ -129,6 +129,8 @@ class Artifact(Model):
129129
name = name[:-8]
130130
elif name.endswith(".json"):
131131
name = name[:-5]
132+
elif name.endswith(".conda"):
133+
name - name[:-6]
132134
self._path = os.path.join(pkg, channel, arch, name + ".json")
133135
else:
134136
msg = "Artifact path or pkg, channel, arch and name must be given. "

libcflib/preloader.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
import requests
1818
import tqdm
1919

20-
from .harvester import harvest
20+
from .harvester import harvest, harvest_dot_conda
2121
from .tools import expand_file_and_mkdirs
2222

2323

@@ -43,6 +43,12 @@ def fetch_arch(arch):
4343
".tar.bz2", ".json"
4444
)
4545
yield v["name"], file_name, package_url
46+
for p, v in repodata["packages.conda"].items():
47+
package_url = f"{arch}/{p}"
48+
file_name = package_url.replace("https://conda.anaconda.org/", "").replace(
49+
".conda", ".json"
50+
)
51+
yield v["name"], file_name, package_url
4652

4753

4854
def fetch_upstream() -> Dict[str, Dict[str, str]]:
@@ -110,8 +116,14 @@ def reap_package(root_path, package, dst_path, src_url, progress_callback=None):
110116
shell=True,
111117
check=True,
112118
)
113-
with open(os.path.join(tmpdir, os.path.basename(src_url)), "rb") as filelike:
114-
harvested_data = harvest(filelike)
119+
pkg_pth = os.path.join(tmpdir, os.path.basename(src_url))
120+
with open(pkg_pth, "rb") as filelike:
121+
if pkg_pth.endswith(".tar.bz2"):
122+
harvested_data = harvest(filelike)
123+
elif pkg_pth.endswith(".conda"):
124+
harvested_data = harvest_dot_conda(filelike, pkg_pth)
125+
else:
126+
raise RuntimeError(f"File '{pkg_pth}' is not a recognized conda format!")
115127
with open(
116128
expand_file_and_mkdirs(os.path.join(root_path, package, dst_path)), "w"
117129
) as fo:

requirements/run.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@ networkx
1313
requests
1414
ruamel_yaml
1515
wget
16+
conda-package-streaming

0 commit comments

Comments
 (0)