10
3
Fork
You've already forked sciop-cli
4

Add "pack"/"unpack" #21

Merged
sneakers-the-rat merged 17 commits from tripleshrimp/sciop-cli:pack-datasets into main 2025年08月20日 07:06:13 +02:00
Contributor
Copy link

Added "pack" command, enabling users to run a full packing pipeline:

  • Create checksum manifest (currently has SHA512 and SHA256 but is extensible), in semi-bagit format
  • Find compression targets based on mode (currently have "leaf", "depth" and "heuristic", but is extensible)
  • Compress targets into .tar.zst files
  • Create a {base dir name}.packmap.json file
  • Delete original targets

Also has the "unpack" command for restoring the original state of the directory.

It's highly likely I made some sort of faux pas or just idiotic mistake; please don't hold back with criticism/suggestions/edits you'd like to make. There are other obvious things we could add (compression from a provided json of specified compression targets, compression level selection (like a --fast mode or something), etc), happy to do so if y'all would like me to.

I also included a small test script, and did my best to update the pyproject.toml/pmd.lock/changelog to fit this update, again please let me know if I did this wrong.

Added "pack" command, enabling users to run a full packing pipeline: - Create checksum manifest (currently has SHA512 and SHA256 but is extensible), in semi-bagit format - Find compression targets based on mode (currently have "leaf", "depth" and "heuristic", but is extensible) - Compress targets into .tar.zst files - Create a {base dir name}.packmap.json file - Delete original targets Also has the "unpack" command for restoring the original state of the directory. It's highly likely I made some sort of faux pas or just idiotic mistake; please don't hold back with criticism/suggestions/edits you'd like to make. There are other obvious things we could add (compression from a provided json of specified compression targets, compression level selection (like a --fast mode or something), etc), happy to do so if y'all would like me to. I also included a small test script, and did my best to update the pyproject.toml/pmd.lock/changelog to fit this update, again please let me know if I did this wrong.
tripleshrimp changed title from (削除) WIP: Add "pack"/"unpack" (削除ここまで) to Add "pack"/"unpack" 2025年06月05日 06:39:25 +02:00
sneakers-the-rat left a comment
Copy link

this is a huge chunk of work! thank you!!!!! some comments to polish it up but it looks like the basic functionality is solid. I'll show you some examples of how to make pydantic data models, i think that you'll like them! we basically want to avoid ever having anonymous/unstructured dictionaries anywhere, so i'll add some models for those too

this is a huge chunk of work! thank you!!!!! some comments to polish it up but it looks like the basic functionality is solid. I'll show you some examples of how to make pydantic data models, i think that you'll like them! we basically want to avoid ever having anonymous/unstructured dictionaries anywhere, so i'll add some models for those too
@ -230,1 +219,4 @@
print(panel)
@torrent.command()

looking at this again i'm thinking maybe we want to move this to its own top-level command, sciop-cli pack

looking at this again i'm thinking maybe we want to move this to its own top-level command, `sciop-cli pack`
sneakers-the-rat marked this conversation as resolved
@ -231,0 +221,4 @@
@torrent.command()
@click.option(
"--base-dir",

for consistency, --path ?

for consistency, `--path` ?
sneakers-the-rat marked this conversation as resolved
@ -231,0 +249,4 @@
"--skip-threshold",
type=float,
default=None,
help="Leaf mode: fraction of present compressed files in a leaf that causes skipping.",

wording is a little unclear, maybe like "float from 0-1, skip directories that have more than this proportion of their files already compressed as archives (list of extensions detected)"

wording is a *little* unclear, maybe like "float from 0-1, skip directories that have more than this proportion of their files already compressed as archives (list of extensions detected)"
sneakers-the-rat marked this conversation as resolved
@ -231,0 +268,4 @@
show_default=True,
help="Which checksum algorithm to write into manifest-<algo>.txt",
)
def pack(

i think we probably want a --dry-run option here that just prints the directories it would compress with the given settings?

i think we probably want a `--dry-run` option here that just prints the directories it would compress with the given settings?

reading the impl, this should be easy to do with the Compressor.select_targets method :) nicely done

reading the impl, this should be easy to do with the `Compressor.select_targets` method :) nicely done

lets open as a secondary issue

lets open as a secondary issue
sneakers-the-rat marked this conversation as resolved
@ -231,0 +279,4 @@
top_n: int | None,
hash_algo: str,
) -> None:
try:

we can just use pydantic models for this :)

we can just use pydantic models for this :)
sneakers-the-rat marked this conversation as resolved
@ -0,0 +18,4 @@
from tqdm import tqdm
CHECKSUM_CHUNK = 4 << 20 # 4 MiB
HashCtor = Callable[[], Any]

i think the return type is _hashlib.HASH

i think the return type is `_hashlib.HASH`
sneakers-the-rat marked this conversation as resolved
@ -0,0 +27,4 @@
COMPRESSED_EXTS = (".zip", ".tar", ".gz", ".tgz", ".bz2", ".rar", ".xz", ".7z", ".tar.zst")
SELECTED_COMP_FILETYPE = ".tar.zst"
PACKED_BY = "sciop-cli"

let's use const.DEFAULT_TORRENT_CREATOR for this

let's use `const.DEFAULT_TORRENT_CREATOR` for this
sneakers-the-rat marked this conversation as resolved
@ -0,0 +32,4 @@
@dataclass
class PackArgs:
base_dir: str

base_dir: Path

`base_dir: Path`
sneakers-the-rat marked this conversation as resolved
@ -0,0 +33,4 @@
@dataclass
class PackArgs:
base_dir: str
mode: str = "heuristic" # "heuristic" | "leaf" | "depth"

mode: typing.Literal["heuristic", "leaf", "depth"] = "heuristic"

`mode: typing.Literal["heuristic", "leaf", "depth"] = "heuristic"`
sneakers-the-rat marked this conversation as resolved
@ -0,0 +57,4 @@
top_n: int | None = None
# -------------------------------------------------------------------------
def __post_init__(self):

i'll show ya how pydantic models work for this :)

i'll show ya how pydantic models work for this :)
sneakers-the-rat marked this conversation as resolved
@ -0,0 +112,4 @@
return os.walk(root, topdown=False)
def _write_manifest(base: Union[str, Path], algo: str, ctor: HashCtor) -> Path:

let's make the manifest a model too!

let's make the manifest a model too!
sneakers-the-rat marked this conversation as resolved
@ -0,0 +122,4 @@
# discover files
files, total = [], 0
for p in base.rglob("*"):
if p.is_file() and not (p.name.startswith("manifest-") and p.name.endswith(".txt")):

only hashing .txt files?

only hashing .txt files?
sneakers-the-rat marked this conversation as resolved
@ -0,0 +127,4 @@
total += p.stat().st_size
bar = tqdm(
total=total, unit="B", unit_scale=True, desc=f"Hashing {algo.upper()}", file=sys.stdout

love a good progress bar

love a good progress bar
sneakers-the-rat marked this conversation as resolved
@ -0,0 +152,4 @@
return _write_manifest(base_dir, algorithm, ctor)
def compress_folder(folder_path: str) -> str | None:

add an option for removing the compressed files?

add an option for removing the compressed files?

opening as secondary issue

opening as secondary issue
sneakers-the-rat marked this conversation as resolved
@ -0,0 +169,4 @@
try:
with temp_path.open("wb") as fout:
cctx = zstd.ZstdCompressor(level=3)

make level a param and then put in manifest?

make `level` a param and then put in manifest?

opening as secondary issue

opening as secondary issue
sneakers-the-rat marked this conversation as resolved
@ -0,0 +173,4 @@
with (
cctx.stream_writer(fout, closefd=False) as zfh,
tarfile.open(fileobj=zfh, mode="w|") as tar,
):

pbar on this would probably be good

pbar on this would probably be good
sneakers-the-rat marked this conversation as resolved
@ -0,0 +174,4 @@
cctx.stream_writer(fout, closefd=False) as zfh,
tarfile.open(fileobj=zfh, mode="w|") as tar,
):
for root, _dirs, files in os.walk(folder_path):

use const.EXCLUDE_FILES to exclude system files

use `const.EXCLUDE_FILES` to exclude system files
sneakers-the-rat marked this conversation as resolved
@ -0,0 +188,4 @@
temp_path.replace(archive_path)
return str(archive_path)
except Exception:

why not raise? seems like we'd want to do that.

why not raise? seems like we'd want to do that.
sneakers-the-rat marked this conversation as resolved
@ -0,0 +363,4 @@
return targets
def compressor_factory(args: PackArgs) -> Compressor:

for consistency, let's make this a classmethod, Compressor.from_args

for consistency, let's make this a classmethod, `Compressor.from_args`
sneakers-the-rat marked this conversation as resolved
@ -0,0 +414,4 @@
size_after = None
archive_rel = None
recs.append(

make this a model :)

make this a model :)
sneakers-the-rat marked this conversation as resolved
@ -0,0 +424,4 @@
}
)
data = {

and this!

and this!
sneakers-the-rat marked this conversation as resolved
@ -0,0 +443,4 @@
def write_sums(self) -> Path:
return generate_checksums(self.base, algorithm=self.args.hash_algo)
def delete_originals(self) -> List[str]:

the ordering of this is a little odd to me - I would expect it to work like

  • determine which directories to compress
  • stats/hashes are computed for the files that are to be compressed
  • it attempts to compress them, erroring if it fails for some reason and cleaning up temporary files
  • it optionally removes the compressed files

so that the directories that are being removed are tied to the act of compression, eliminating the chance for e.g. the packmap to be out of date and making resume state unambiguous in the case of errors. would probably simplify the method here too, because we check if there is an archive present, but we don't know if it's correct here

the ordering of this is a *little* odd to me - I would expect it to work like - determine which directories to compress - stats/hashes are computed for the files that are to be compressed - it attempts to compress them, erroring if it fails for some reason and cleaning up temporary files - it optionally removes the compressed files so that the directories that are being removed are tied to the act of compression, eliminating the chance for e.g. the packmap to be out of date and making resume state unambiguous in the case of errors. would probably simplify the method here too, because we check if there is an archive present, but we don't know if it's correct here
sneakers-the-rat marked this conversation as resolved
@ -0,0 +522,4 @@
"""
def _fmt(size: int) -> str:

see types.Size - i think we should have one way of stringifying sizes :)

see `types.Size` - i think we should have one way of stringifying sizes :)
sneakers-the-rat marked this conversation as resolved
@ -0,0 +15,4 @@
)
def _make_fixture(root: Path) -> dict[str, Path]:

i think we'll want to parameterize this, add more files programmatically with different sizes and quantities so that we can test the correctness and behavior of the compressors. I think for the sake of testing that we can just have this function create a single directory with n files of average_size and std_size with the filesize taking some normal distribution centered at the average with standard deviation? we use gamma distributions for this in test_torrent to check auto piece size

i think we'll want to parameterize this, add more files programmatically with different sizes and quantities so that we can test the correctness and behavior of the compressors. I *think* for the sake of testing that we can just have this function create a single directory with `n` files of `average_size` and `std_size` with the filesize taking some normal distribution centered at the average with standard deviation? we use gamma distributions for this in `test_torrent` to check auto piece size
sneakers-the-rat marked this conversation as resolved
@ -0,0 +42,4 @@
("heuristic", {"min_filecount": 1}), # any dir w/ ≥1 file
],
)
def test_pack_pipeline(tmp_path: Path, algo: str, mode: str, extra: dict) -> None:

great start here! yes! I think we'll want to add some unit tests too,

  • test roundtrip of the directory compression/decomp functions
  • positive and negative controls for the different compressors - whether they correctly identify a directory that shoudl be compressed and reject a directory that shouldn't.
  • test manifest creation
  • test rejection of system files

and one end-to-end test that is basically this except for calling it with the CLI params so we make sure we don't break the cli part while leaving the library part working

great start here! yes! I think we'll want to add some unit tests too, - test roundtrip of the directory compression/decomp functions - positive and negative controls for the different compressors - whether they correctly identify a directory that shoudl be compressed and reject a directory that shouldn't. - test manifest creation - test rejection of system files and one end-to-end test that is basically this except for calling it with the CLI params so we make sure we don't break the cli part while leaving the library part working
Author
Contributor
Copy link

Working on the test updates, when you say "system files", do you just mean the EXCLUDE_FILES?

Working on the test updates, when you say "system files", do you just mean the EXCLUDE_FILES?

Ya ya, I don't have an exhaustive list there, but just the ones ive seen. We can expand that as we go. Just want to exclude common files that wind up in datasets as a byproduct of the OS

Ya ya, I don't have an exhaustive list there, but just the ones ive seen. We can expand that as we go. Just want to exclude common files that wind up in datasets as a byproduct of the OS
sneakers-the-rat marked this conversation as resolved

i'll mark all comments i have already resolved with a thumbs emoji but not resolve them, because unresolving takes forever in case you wanted to see what they say

i'll mark all comments i have already resolved with a thumbs emoji but not resolve them, because unresolving takes forever in case you wanted to see what they say
Author
Contributor
Copy link

Just checking on this, as I'm new and unsure of how this usually goes: it appears that all the comments have their associated code edits completed, are there any tasks I'm missing related to this current iteration of pack() I should be working on?

Just checking on this, as I'm new and unsure of how this usually goes: it appears that all the comments have their associated code edits completed, are there any tasks I'm missing related to this current iteration of pack() I should be working on?

Oh no I was basically waiting for a comment to ping me back responding to comments/saying you were done. I'll take another look :)

Oh no I was basically waiting for a comment to ping me back responding to comments/saying you were done. I'll take another look :)
Author
Contributor
Copy link

Ok, just to be clear I haven't made any edits of my own, just me poorly communicating that I don't know what is still on the docket for this after your edits

Ok, just to be clear I haven't made any edits of my own, just me poorly communicating that I don't know what is still on the docket for this after your edits
sneakers-the-rat force-pushed pack-datasets from 23c43dfe9f
All checks were successful
lint / black (pull_request) Successful in 41s
lint / ruff (pull_request) Successful in 40s
lint / mypy (pull_request) Successful in 1m3s
tests / tests (pull_request) Successful in 1m51s
to 4a9de1bff6
Some checks failed
lint / ruff (pull_request) Failing after 46s
lint / black (pull_request) Successful in 1m2s
lint / mypy (pull_request) Successful in 1m4s
tests / tests (pull_request) Successful in 2m33s
2025年06月26日 10:13:49 +02:00
Compare
move pack to top-level cli command
All checks were successful
lint / ruff (pull_request) Successful in 45s
lint / black (pull_request) Successful in 1m0s
lint / mypy (pull_request) Successful in 1m4s
tests / tests (pull_request) Successful in 2m31s
b87dafee14
exclude system files
All checks were successful
lint / black (pull_request) Successful in 1m0s
lint / ruff (pull_request) Successful in 45s
lint / mypy (pull_request) Successful in 1m3s
tests / tests (pull_request) Successful in 2m27s
41885a801c
use humanize for consistent display, use logging rather than printing
All checks were successful
lint / black (pull_request) Successful in 39s
lint / ruff (pull_request) Successful in 39s
lint / mypy (pull_request) Successful in 1m3s
tests / tests (pull_request) Successful in 2m32s
b123dcbb88

ok, usually the other person/people resolve the comments - if you are fine with stuff, resolve, if you are not, then feel free to discuss and comment adn we can not do things or revert them. so i just went ahead and resolved everything that wasn't pressing for me and did those changes if by the above you mean you were fine with them

ok, usually the other person/people resolve the comments - if you are fine with stuff, resolve, if you are not, then feel free to discuss and comment adn we can not do things or revert them. so i just went ahead and resolved everything that wasn't pressing for me and did those changes if by the above you mean you were fine with them
Author
Contributor
Copy link

Thank you for your patience, and for putting in so much extra work, and for showing me the ropes on this, it truly means alot to me.

Thank you for your patience, and for putting in so much extra work, and for showing me the ropes on this, it truly means alot to me.
updates to test_pack, also slight change to pack for system file skipping
Some checks failed
lint / ruff (pull_request) Failing after 44s
lint / black (pull_request) Failing after 1m0s
lint / mypy (pull_request) Successful in 1m4s
tests / tests (pull_request) Successful in 2m20s
6d93f90781
Author
Contributor
Copy link

OK I think these test should cover our desired cases, let me know if there's anything I need to change/resolve

OK I think these test should cover our desired cases, let me know if there's anything I need to change/resolve
sneakers-the-rat left a comment
Copy link

ok cool, still a few test cases to cover, but you have the pieces in place to add them pretty easily :)

ok cool, still a few test cases to cover, but you have the pieces in place to add them pretty easily :)
@ -0,0 +138,4 @@
total_bytes, _ = _dir_stats(folder)
usage = shutil.disk_usage(folder.parent)
if usage.free < total_bytes:
return None # not enough space

let's raise here - if we have run out of disk space, we shouldn't try to continue

let's raise here - if we have run out of disk space, we shouldn't try to continue

actually looking at the docs, this has platform availability notes so can sometimes fail. so we should probably do this like

try:
 usage = shutil.disk_usage(folder.parent)
except Exception:
 pass
else:
 if usage.free < total_bytes:
 raise OSError(f"Out of disk space, directory is {humanize.naturalsize(total_bytes)}, {humanize.naturalsize(usage.free)} available.")
actually looking at the docs, this has platform availability notes so can sometimes fail. so we should probably do this like ```python try: usage = shutil.disk_usage(folder.parent) except Exception: pass else: if usage.free < total_bytes: raise OSError(f"Out of disk space, directory is {humanize.naturalsize(total_bytes)}, {humanize.naturalsize(usage.free)} available.") ```
sneakers-the-rat marked this conversation as resolved
@ -0,0 +63,4 @@
manifest = base / f"manifest-{args.hash_algo}.txt"
assert manifest.is_file()
text = manifest.read_text()
assert {"a/alpha.txt", "b/beta.txt"} <= set(text.split())

hopefully == right?

hopefully `==` right?
sneakers-the-rat marked this conversation as resolved
@ -0,0 +113,4 @@
def _depth(path: str) -> int:
"""Component count in a relative POSIX path."""
return path.count(os.sep) + 1 if path else 0

len(Path(path).parents)

>>> len(Path("sup/hey/hello.txt").parents)
3
`len(Path(path).parents)` ```python >>> len(Path("sup/hey/hello.txt").parents) 3 ```
sneakers-the-rat marked this conversation as resolved
@ -0,0 +129,4 @@
SELECTOR_CASES = [
# ── HEURISTIC ──
# min_filecount 2 → allow *anything* but keep_me isn't guaranteed just make sure test doesn't demand it
(HeuristicArgs, "heuristic", {"min_filecount": 2}, set(), None),

yeah for this one similar feelings, don't mean to bog you down with too much testing for this but i think we do want to cover the over and under for each of the heuristic rules - like if we set a mean filesize, does it correctly select a directory that is beneath it and reject a directory that above it and etc. for each of the rules in the heuristic. you already have the basic setup in place here so just need to add cases for each of these. you may want to split this off to its own testing function that works similarly to this one so you don't need to mess too much with the sample directory used in the rest of the non-heuristic cases, which only care about depth for the most part.

yeah for this one similar feelings, don't mean to bog you down with too much testing for this but i think we do want to cover the over and under for each of the heuristic rules - like if we set a mean filesize, does it correctly select a directory that is beneath it and reject a directory that above it and etc. for each of the rules in the heuristic. you already have the basic setup in place here so just need to add cases for each of these. you may want to split this off to its own testing function that works similarly to this one so you don't need to mess too much with the sample directory used in the rest of the non-heuristic cases, which only care about depth for the most part.

asking for this mostly for regression protection - want to make sure we don't accidentally break this, so want to at least cover the expected basic functionality even if we're not covering all the possible edge cases

asking for this mostly for regression protection - want to make sure we don't accidentally break this, so want to at least cover the expected basic functionality even if we're not covering all the possible edge cases
sneakers-the-rat marked this conversation as resolved
@ -0,0 +134,4 @@
(HeuristicArgs, "heuristic", {"min_filecount": 1, "top_n": 1}, None, None),
# ── DEPTH ──
(DepthArgs, "depth", {"depth": 1}, {"keep_me", "skip_me", "tiny", "parent"}, {"parent/child"}),

this is a little odd behavior to me, i would expect that the depth parameter would behave like a "max depth" parameter, where we would compress everything below a certain depth. it seems odd to me to have an exact depth here, like e.g. if you had

a/z.gif
a/b/y.gif
a/b/c/x.gif

it would be surprising to me if i set depth: 2 (meaning a/b) and that caused me to compress a/b/y.gif into a/b.zip but then also still have a/b/c/x.gif since that's also in a/b. am i reading this test case right?

this is a *little* odd behavior to me, i would expect that the depth parameter would behave like a "max depth" parameter, where we would compress everything below a certain depth. it seems odd to me to have an *exact* depth here, like e.g. if you had ``` a/z.gif a/b/y.gif a/b/c/x.gif ``` it would be surprising to me if i set `depth: 2` (meaning `a/b`) and that caused me to compress `a/b/y.gif` into `a/b.zip` but then also still have `a/b/c/x.gif` since that's also in `a/b`. am i reading this test case right?
Author
Contributor
Copy link

In our current implementation, we select the exact level to compress, then compress every folder at that exact level. Therefore, everything below that level will be included in the archives created at that level. At least, this is my intention. Isn't this basically max_depth? If not I'm happy to change it.

I'm adding new dummies to the fixture so I can expand the positive/negative cases anyway, but I think the selectors currently work with that in mind.

In our current implementation, we select the exact level to compress, then compress every folder at that exact level. Therefore, everything below that level will be included in the archives created at that level. At least, this is my intention. Isn't this basically max_depth? If not I'm happy to change it. I'm adding new dummies to the fixture so I can expand the positive/negative cases anyway, but I think the selectors currently work with that in mind.

ok i see this now in the tests ya

ok i see this now in the tests ya
sneakers-the-rat marked this conversation as resolved
@ -0,0 +135,4 @@
# ── DEPTH ──
(DepthArgs, "depth", {"depth": 1}, {"keep_me", "skip_me", "tiny", "parent"}, {"parent/child"}),
(DepthArgs, "depth", {"depth": 2}, {"parent/child"}, {"keep_me"}),

another thing, i would usually think of "leaf" and "depth" as being the same thing, and one would think of "compress the leaf directories" as being depth: -1. not asking you to rewrite because i can see why they would be separated here, but we might want to add a specific note in the cli docs like "for negative depth (e.g. compress the bottommost directories that contain only files) see --leaf"

another thing, i would usually think of "leaf" and "depth" as being the same thing, and one would think of "compress the leaf directories" as being `depth: -1`. not asking you to rewrite because i can see why they would be separated here, but we might want to add a specific note in the cli docs like "for negative depth (e.g. compress the bottommost directories that contain only files) see --leaf"
Author
Contributor
Copy link

.... oh my god. Duh. Not sure how that didn't click for me. In that case we could just allow for a -1, and then throw in the skip_threshold check for the depth mode, to consolidate. Leaf mode is kinda my go-to for pack so it seemed natural to spin it off as it's own thing either way, and we could keep it around if we wanted but maybe also enable -1 depth, to be more intuitive?

.... oh my god. Duh. Not sure how that didn't click for me. In that case we could just allow for a -1, and then throw in the skip_threshold check for the depth mode, to consolidate. Leaf mode is kinda my go-to for pack so it seemed natural to spin it off as it's own thing either way, and we could keep it around if we wanted but maybe also enable -1 depth, to be more intuitive?

i'm good with whatever as long as there is only one actual implementation :)

i'm good with whatever as long as there is only one actual implementation :)
@ -0,0 +138,4 @@
(DepthArgs, "depth", {"depth": 2}, {"parent/child"}, {"keep_me"}),
# ── LEAF ──
(LeafArgs, "leaf", {"skip_threshold": 1.0}, None, {"skip_me"}),

not really testing the behavior of interest here - we want to test at least four things:

  • only files in leaf directories are compressed, even if they are below the threshold
  • files in leaf directories are compressed if they are below the threshold
  • files in leaf directories are not compressed if they are above the threshold
  • zipped files are not counted against the threshold (i think? or however zip files are handled)

(or whichever direction the threshold goes).

so, here with the high skip threshold, we should test that we do choose to zip any directories that should be zipped, which loks like keep_me and parent/child and also tiny??, and then also add another file within parent that should not be zipped.

not really testing the behavior of interest here - we want to test at least four things: - *only* files in leaf directories are compressed, even if they are below the threshold - files in leaf directories are compressed if they are below the threshold - files in leaf directories are *not* compressed if they are above the threshold - zipped files are not counted against the threshold (i think? or however zip files are handled) (or whichever direction the threshold goes). so, here with the high skip threshold, we should test that we do choose to zip any directories that *should* be zipped, which loks like `keep_me` and `parent/child` and also `tiny`??, and then also add another file within `parent` that should not be zipped.
sneakers-the-rat marked this conversation as resolved
expanded cases for depth and leaf, new fixture for heuristic
Some checks failed
lint / black (pull_request) Failing after 38s
lint / ruff (pull_request) Failing after 37s
lint / mypy (pull_request) Successful in 1m13s
tests / tests (pull_request) Failing after 2m18s
b80a447fdb
Author
Contributor
Copy link

OK I made a few changes just based on the main proposed fixes

OK I made a few changes just based on the main proposed fixes
Author
Contributor
Copy link

oops hold on got distracted and forgot a couple test runs

oops hold on got distracted and forgot a couple test runs
actual fix
Some checks failed
lint / black (pull_request) Failing after 39s
lint / ruff (pull_request) Failing after 37s
lint / mypy (pull_request) Successful in 1m4s
tests / tests (pull_request) Failing after 2m13s
1abdda54c9
Author
Contributor
Copy link

ok this actually runs now lol

ok this actually runs now lol
Merge branch 'main' into pack-datasets
Some checks failed
lint / black (pull_request) Failing after 40s
lint / ruff (pull_request) Failing after 38s
lint / mypy (pull_request) Successful in 1m6s
tests / tests (pull_request) Successful in 2m41s
4b2907e185
@ -0,0 +158,4 @@
if mode == "leaf":
assert all(_is_leaf(base, p) for p in selected)
def _heuristic_fixture(tmp_path: Path) -> Path:

nice. splitting this out was the right call.

nice. splitting this out was the right call.
lint
All checks were successful
lint / black (pull_request) Successful in 40s
lint / ruff (pull_request) Successful in 39s
lint / mypy (pull_request) Successful in 1m6s
tests / tests (pull_request) Successful in 2m15s
85ca1735e9
raise on failure, not print
All checks were successful
lint / black (pull_request) Successful in 41s
lint / ruff (pull_request) Successful in 40s
lint / mypy (pull_request) Successful in 1m7s
tests / tests (pull_request) Successful in 2m26s
df4d71c693

i don't remember why this wasn't merged yet, sorry, will do

i don't remember why this wasn't merged yet, sorry, will do
Merge branch 'main' into pack-datasets
Some checks failed
lint / black (pull_request) Successful in 28s
lint / ruff (pull_request) Successful in 58s
lint / mypy (pull_request) Successful in 1m46s
tests / tests (pull_request) Failing after 3m1s
tests / tests-1 (pull_request) Successful in 3m5s
tests / tests-2 (pull_request) Successful in 2m49s
dfd94e98eb
Sign in to join this conversation.
No reviewers
Labels
Clear labels
api
Compat/Breaking
Breaking change that won't be backward compatible
Kind/Bug
Something is not working
Kind/Documentation
Documentation changes
Kind/Enhancement
Improve existing functionality
Kind/Feature
New functionality
Kind/Security
This is security issue
Kind/Tech Debt
Kind/Testing
Issue or pull request related to testing
Priority
Critical
The priority is critical
Priority
High
The priority is high
Priority
Low
The priority is low
Priority
Medium
The priority is medium
Reviewed
Confirmed
Issue has been confirmed
Reviewed
Duplicate
This issue or pull request already exists
Reviewed
Invalid
Invalid issue
Reviewed
Won't Fix
This issue won't be fixed
Status
Abandoned
Somebody has started to work on this but abandoned work
Status
Blocked
Something is blocking this issue or pull request
Status
Need More Info
Feedback is required to reproduce issue or to continue work
Milestone
Clear milestone
No items
No milestone
Projects
Clear projects
No items
No project
Assignees
Clear assignees
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Safeguarding/sciop-cli!21
Reference in a new issue
Safeguarding/sciop-cli
No description provided.
Delete branch "tripleshrimp/sciop-cli:pack-datasets"

Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?