Skip to content
Merged
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
6 changes: 5 additions & 1 deletion docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,12 @@ Test fixtures for use by clients are available for each release on the [Github r

#### `fill`

- 🐞 Fix `DeprecationWarning: Pickle, copy, and deepcopy support will be removed from itertools in Python 3.14.` by avoiding use `itertools` object in the spec `BaseTest` pydantic model ([#1414](https://github.com/ethereum/execution-spec-tests/pull/1414)).
- ✨ The `static_filler` plug-in now has support for static state tests (from [GeneralStateTests](https://github.com/ethereum/tests/tree/develop/src/GeneralStateTestsFiller)) ([#1362](https://github.com/ethereum/execution-spec-tests/pull/1362)).
- 🐞 Fix `DeprecationWarning: Pickle, copy, and deepcopy support will be removed from itertools in Python 3.14.` by avoiding use `itertools` object in the spec `BaseTest` pydantic model ([#1414](https://github.com/ethereum/execution-spec-tests/pull/1414)).

#### `consume`

- 🐞 Fix fixture tarball downloading with regular, non-Github release URLS and with numerical versions in regular release specs, e.g., `[email protected]` ([#1437](https://github.com/ethereum/execution-spec-tests/pull/1437)).

### 📋 Misc

Expand Down
42 changes: 29 additions & 13 deletions src/pytest_plugins/consume/consume.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from ethereum_test_forks import get_forks, get_relative_fork_markers, get_transition_forks
from ethereum_test_tools.utility.versioning import get_current_commit_hash_or_tag

from .releases import ReleaseTag, get_release_page_url, get_release_url
from .releases import ReleaseTag, get_release_page_url, get_release_url, is_release_url, is_url

CACHED_DOWNLOADS_DIRECTORY = (
Path(platformdirs.user_cache_dir("ethereum-execution-spec-tests")) / "cached_downloads"
Expand Down Expand Up @@ -50,10 +50,16 @@ def __init__(self, url: str, base_directory: Path): # noqa: D107
self.url = url
self.base_directory = base_directory
self.parsed_url = urlparse(url)
self.org_repo = self.extract_github_repo()
self.version = Path(self.parsed_url.path).parts[-2]
self.archive_name = self.strip_archive_extension(Path(self.parsed_url.path).name)
self.extract_to = base_directory / self.org_repo / self.version / self.archive_name

@property
def extract_to(self) -> Path:
"""Path to the directory where the archive will be extracted."""
if is_release_url(self.url):
version = Path(self.parsed_url.path).parts[-2]
self.org_repo = self.extract_github_repo()
return self.base_directory / self.org_repo / version / self.archive_name
return self.base_directory / "other" / self.archive_name

def download_and_extract(self) -> Tuple[bool, Path]:
"""Download the URL and extract it locally if it hasn't already been downloaded."""
Expand Down Expand Up @@ -91,7 +97,7 @@ def detect_extracted_directory(self) -> Path:
"""
Detect a single top-level dir within the extracted archive, otherwise return extract_to.
""" # noqa: D200
extracted_dirs = [d for d in self.extract_to.iterdir() if d.is_dir()]
extracted_dirs = [d for d in self.extract_to.iterdir() if d.is_dir() and d.name != ".meta"]
return extracted_dirs[0] if len(extracted_dirs) == 1 else self.extract_to


Expand All @@ -112,15 +118,17 @@ def from_input(cls, input_source: str) -> "FixturesSource":
"""Determine the fixture source type and return an instance."""
if input_source == "stdin":
return cls(input_option=input_source, path=Path(), is_local=False, is_stdin=True)
if is_release_url(input_source):
return cls.from_release_url(input_source)
if is_url(input_source):
return cls.from_url(input_source)
if ReleaseTag.is_release_string(input_source):
return cls.from_release_spec(input_source)
return cls.validate_local_path(Path(input_source))

@classmethod
def from_url(cls, url: str) -> "FixturesSource":
"""Create a fixture source from a direct URL."""
def from_release_url(cls, url: str) -> "FixturesSource":
"""Create a fixture source from a supported github repo release URL."""
release_page = get_release_page_url(url)
downloader = FixtureDownloader(url, CACHED_DOWNLOADS_DIRECTORY)
was_cached, path = downloader.download_and_extract()
Expand All @@ -133,6 +141,20 @@ def from_url(cls, url: str) -> "FixturesSource":
was_cached=was_cached,
)

@classmethod
def from_url(cls, url: str) -> "FixturesSource":
"""Create a fixture source from a direct URL."""
downloader = FixtureDownloader(url, CACHED_DOWNLOADS_DIRECTORY)
was_cached, path = downloader.download_and_extract()
return cls(
input_option=url,
path=path,
url=url,
release_page="",
is_local=False,
was_cached=was_cached,
)

@classmethod
def from_release_spec(cls, spec: str) -> "FixturesSource":
"""Create a fixture source from a release spec (e.g., develop@latest)."""
Expand All @@ -159,12 +181,6 @@ def validate_local_path(path: Path) -> "FixturesSource":
return FixturesSource(input_option=str(path), path=path)


def is_url(string: str) -> bool:
"""Check if a string is a remote URL."""
result = urlparse(string)
return all([result.scheme, result.netloc])


class SimLimitBehavior:
"""Represents options derived from the `--sim.limit` argument."""

Expand Down
22 changes: 21 additions & 1 deletion src/pytest_plugins/consume/releases.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from datetime import datetime
from pathlib import Path
from typing import List
from urllib.parse import urlparse

import platformdirs
import requests
Expand Down Expand Up @@ -72,7 +73,11 @@ def __eq__(self, value) -> bool:
"""
assert isinstance(value, str), f"Expected a string, but got: {value}"
if self.version is not None:
return value == f"{self.tag_name}@{self.version}"
# normal release, e.g., [email protected]
normal_release_match = value == self.version
# pre release, e.g., [email protected]
pre_release_match = value == f"{self.tag_name}@{self.version}"
return normal_release_match or pre_release_match
return value.startswith(self.tag_name)

@property
Expand Down Expand Up @@ -140,6 +145,21 @@ def is_docker_or_ci() -> bool:
return "GITHUB_ACTIONS" in os.environ or Path("/.dockerenv").exists()


def is_url(string: str) -> bool:
"""Check if a string is a remote URL."""
result = urlparse(string)
return all([result.scheme, result.netloc])


def is_release_url(input_str: str) -> bool:
"""Check if the release string is a URL."""
if not is_url(input_str):
return False
repo_pattern = "|".join(re.escape(repo) for repo in SUPPORTED_REPOS)
regex_pattern = rf"https://github\.com/({repo_pattern})/releases/download/"
return re.match(regex_pattern, input_str) is not None


def parse_release_information(release_information: List) -> List[ReleaseInformation]:
"""Parse the release information from the Github API."""
return Releases.model_validate(release_information).root # type: ignore
Expand Down
Loading