Skip to content

Commit 8a63c15

Browse files
authored
Merge pull request #257 from crytic/simplify
Update README, clean up code
2 parents bb6e444 + f832e36 commit 8a63c15

File tree

10 files changed

+50
-85
lines changed

10 files changed

+50
-85
lines changed

README.md

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,24 +14,28 @@ The versioned binaries are stored in `~/.solc-select/artifacts/`.
1414

1515
## Installation
1616

17-
### Using pip
17+
### Using uv
1818

1919
```bash
20-
pip3 install solc-select
20+
uv tool install solc-select
2121
```
2222

23-
### Using uv (recommended for development)
23+
### Using pip
2424

2525
```bash
26-
uv tool install solc-select
26+
pip3 install solc-select
2727
```
2828

2929
To automatically install and use a version, run `solc-select use <version> --always-install`.
3030

31-
### Running on ARM (Mac M1/M2)
31+
### Running on macOS ARM (Mac M1 and newer)
3232

3333
`solc-select` provides native ARM64 support for versions 0.8.5-0.8.23, and universal binary support for 0.8.24+. For versions older than 0.8.5, Rosetta is required. See the FAQ on [how to install Rosetta](#oserror-errno-86-bad-cpu-type-in-executable).
3434

35+
### Running on Linux ARM
36+
37+
`solc-select` provides native ARM64 support for versions 0.8.31+. For versions older than 0.8.31, QEMU (`qemu-x86_64`) is required. Additionally, a libc binary (e.g., from package `libc6-amd64-cross`) and adequate `QEMU_LD_PREFIX` environment variable (e.g., `QEMU_LD_PREFIX=/usr/x86_64-linux-gnu`) might be necessary to execute certain solc binaries that are not built statically.
38+
3539
## Usage
3640

3741
### Quick Start
@@ -160,7 +164,7 @@ pip3 install solc-select==0.2.0
160164
solc-select install
161165
```
162166

163-
### `solc-select` version changes, but `solc --version does not match`
167+
### `solc-select` version changes, but `solc --version` does not match
164168

165169
Users seem to be experiencing situations in which the following command is successful:
166170

solc_select/__main__.py

Lines changed: 16 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,7 @@ def solc_select_install(service: SolcService, versions: list[str]) -> None:
2626
"""Handle the install command."""
2727
if not versions:
2828
print("Available versions to install:")
29-
installable = service.get_installable_versions()
30-
for version in installable:
29+
for version in service.get_installable_versions():
3130
print(str(version))
3231
else:
3332
success = service.install_versions(versions)
@@ -42,21 +41,20 @@ def solc_select_use(service: SolcService, version: str, always_install: bool) ->
4241
def solc_select_versions(service: SolcService) -> None:
4342
"""Handle the versions command."""
4443
installed = service.get_installed_versions()
45-
if installed:
46-
try:
47-
current_version, source = service.get_current_version()
48-
except (NoVersionSetError, VersionNotInstalledError):
49-
# No version is currently set or not installed, that's ok for the versions command
50-
current_version = None
51-
52-
installed_strs = [str(v) for v in installed]
53-
for version_str in sort_versions(installed_strs):
54-
if current_version and version_str == str(current_version):
55-
print(f"{version_str} (current, set by {source})")
56-
else:
57-
print(version_str)
58-
else:
44+
if not installed:
5945
print("No solc version installed. Run `solc-select install --help` for more information")
46+
return
47+
48+
try:
49+
current_version, source = service.get_current_version()
50+
except (NoVersionSetError, VersionNotInstalledError):
51+
current_version = None
52+
53+
for version_str in sort_versions([str(v) for v in installed]):
54+
if current_version and version_str == str(current_version):
55+
print(f"{version_str} (current, set by {source})")
56+
else:
57+
print(version_str)
6058

6159

6260
def solc_select_upgrade(service: SolcService) -> None:
@@ -72,7 +70,6 @@ def create_parser() -> argparse.ArgumentParser:
7270
dest="command",
7371
)
7472

75-
# Install command
7673
parser_install = subparsers.add_parser(
7774
INSTALL_COMMAND, help="list and install available solc versions"
7875
)
@@ -83,20 +80,17 @@ def create_parser() -> argparse.ArgumentParser:
8380
default=[],
8481
)
8582

86-
# Use command
8783
parser_use = subparsers.add_parser(
8884
USE_COMMAND, help="change the version of global solc compiler"
8985
)
9086
parser_use.add_argument("version", help="solc version you want to use (eg: 0.4.25)", nargs="?")
9187
parser_use.add_argument("--always-install", action="store_true")
9288

93-
# Versions command
9489
parser_versions = subparsers.add_parser(
9590
VERSIONS_COMMAND, help="prints out all installed solc versions"
9691
)
9792
parser_versions.add_argument("versions", nargs="*", help=argparse.SUPPRESS)
9893

99-
# Upgrade command
10094
parser_upgrade = subparsers.add_parser(UPGRADE_COMMAND, help="upgrades solc-select")
10195
parser_upgrade.add_argument("upgrade", nargs="*", help=argparse.SUPPRESS)
10296

@@ -106,25 +100,19 @@ def create_parser() -> argparse.ArgumentParser:
106100
def solc_select() -> None:
107101
parser = create_parser()
108102
args = parser.parse_args()
109-
110-
# Create service instance
111103
service = SolcService()
112104

113105
try:
114106
if args.command == INSTALL_COMMAND:
115107
solc_select_install(service, args.versions)
116-
117108
elif args.command == USE_COMMAND:
118109
if not args.version:
119110
parser.error("the following arguments are required: version")
120111
solc_select_use(service, args.version, args.always_install)
121-
122112
elif args.command == VERSIONS_COMMAND:
123113
solc_select_versions(service)
124-
125114
elif args.command == UPGRADE_COMMAND:
126115
solc_select_upgrade(service)
127-
128116
else:
129117
parser.parse_args(["--help"])
130118
sys.exit(0)
@@ -154,7 +142,7 @@ def solc_select() -> None:
154142
print(f"Error: {e}", file=sys.stderr)
155143
sys.exit(1)
156144
except KeyboardInterrupt:
157-
print("\nOperation cancelled by user", file=sys.stderr)
145+
print("\nOperation cancelled", file=sys.stderr)
158146
sys.exit(1)
159147
except Exception as e:
160148
print(f"Unexpected error: {e}", file=sys.stderr)
@@ -168,7 +156,7 @@ def solc() -> None:
168156
try:
169157
service.execute_solc(sys.argv[1:])
170158
except KeyboardInterrupt:
171-
print("\nOperation cancelled by user", file=sys.stderr)
159+
print("\nOperation cancelled", file=sys.stderr)
172160
sys.exit(1)
173161
except Exception as e:
174162
print(f"Error executing solc: {e}", file=sys.stderr)

solc_select/exceptions.py

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,9 @@
1-
"""
2-
Custom exception classes for solc-select.
3-
4-
This module provides a structured exception hierarchy for better error handling
5-
and more informative error messages throughout the application.
6-
"""
1+
"""Custom exception classes for solc-select."""
72

83

94
class SolcSelectError(Exception):
105
"""Base exception for all solc-select errors."""
116

12-
pass
13-
147

158
class VersionNotFoundError(SolcSelectError):
169
"""Raised when requested version doesn't exist or isn't available."""
@@ -26,7 +19,6 @@ def __init__(
2619
self.suggestion = suggestion
2720

2821
message = f"Version '{version}' not found"
29-
3022
if available_versions:
3123
if len(available_versions) <= 5:
3224
message += f". Available versions: {', '.join(available_versions)}"
@@ -77,7 +69,6 @@ def __init__(self, version: str, platform: str, min_version: str | None = None):
7769
self.min_version = min_version
7870

7971
message = f"Version '{version}' is not supported on {platform}"
80-
8172
if min_version:
8273
message += f". Minimum supported version is '{min_version}'"
8374

@@ -105,7 +96,6 @@ class InstallationError(SolcSelectError):
10596
def __init__(self, version: str, reason: str):
10697
self.version = version
10798
self.reason = reason
108-
10999
super().__init__(f"Failed to install version '{version}': {reason}")
110100

111101

@@ -120,10 +110,8 @@ def __init__(
120110
self.original_error = original_error
121111

122112
message = f"Network error during {operation}"
123-
124113
if url:
125114
message += f" from {url}"
126-
127115
if original_error:
128116
message += f": {original_error!s}"
129117

@@ -136,7 +124,6 @@ class VersionResolutionError(SolcSelectError):
136124
def __init__(self, requested: str, reason: str):
137125
self.requested = requested
138126
self.reason = reason
139-
140127
super().__init__(f"Could not resolve version '{requested}': {reason}")
141128

142129

solc_select/infrastructure/http_client.py

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,4 @@
1-
"""
2-
HTTP client configuration for solc-select.
3-
4-
This module provides centralized HTTP client configuration with
5-
retry logic and proper timeout handling.
6-
"""
1+
"""HTTP client configuration for solc-select."""
72

83
from collections.abc import Mapping
94
from typing import Any
@@ -38,7 +33,6 @@ def create_http_session() -> requests.Session:
3833
"""Create a new HTTP session with retry logic for rate limits and server errors."""
3934
session = requests.Session()
4035

41-
# Configure retry strategy for 429s and server errors
4236
retry_strategy = Retry(
4337
total=5,
4438
backoff_factor=1,

solc_select/models/platforms.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from dataclasses import dataclass
66

77
from ..constants import LINUX_AMD64, LINUX_ARM64, MACOSX_AMD64, WINDOWS_AMD64
8-
from ..platform_capabilities import PlatformCapability, PlatformIdentifier
8+
from ..platform_capabilities import CAPABILITY_REGISTRY, PlatformCapability, PlatformIdentifier
99

1010

1111
@dataclass(frozen=True)
@@ -25,8 +25,6 @@ def __post_init__(self) -> None:
2525

2626
def get_capability(self) -> PlatformCapability:
2727
"""Get the capability declaration for this platform."""
28-
from ..platform_capabilities import CAPABILITY_REGISTRY
29-
3028
key = f"{self.os_type}-{self.architecture}"
3129
return CAPABILITY_REGISTRY.get(key, self._create_default_capability())
3230

solc_select/models/versions.py

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,25 @@
55
from packaging.version import Version
66

77

8+
class SolcVersion(Version):
9+
"""Represents a Solidity compiler version."""
10+
11+
@classmethod
12+
def parse(cls, version_str: str) -> "SolcVersion":
13+
"""Parse a version string into a SolcVersion instance."""
14+
if version_str == "latest":
15+
raise ValueError("Cannot parse 'latest' - resolve to actual version first")
16+
return cls(version_str)
17+
18+
819
@dataclass(frozen=True)
920
class VersionRange:
1021
"""Inclusive version range [min, max]. None means unbounded."""
1122

12-
min_version: "SolcVersion | None" = None
13-
max_version: "SolcVersion | None" = None
23+
min_version: SolcVersion | None = None
24+
max_version: SolcVersion | None = None
1425

15-
def contains(self, version: "SolcVersion") -> bool:
26+
def contains(self, version: SolcVersion) -> bool:
1627
"""Check if version is within range (inclusive)."""
1728
above_minimum = self.min_version is None or version >= self.min_version
1829
below_maximum = self.max_version is None or version <= self.max_version
@@ -30,14 +41,3 @@ def exact_range(cls, min_ver: str, max_ver: str) -> "VersionRange":
3041
min_version=SolcVersion.parse(min_ver),
3142
max_version=SolcVersion.parse(max_ver),
3243
)
33-
34-
35-
class SolcVersion(Version):
36-
"""Represents a Solidity compiler version."""
37-
38-
@classmethod
39-
def parse(cls, version_str: str) -> "SolcVersion":
40-
"""Parse a version string into a SolcVersion instance."""
41-
if version_str == "latest":
42-
raise ValueError("Cannot parse 'latest' - resolve to actual version first")
43-
return cls(version_str)

solc_select/repositories.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""Repository implementations for fetching Solidity compiler versions."""
22

33
from functools import lru_cache
4-
from typing import TYPE_CHECKING, Any
4+
from typing import Any
55

66
import requests
77

@@ -11,11 +11,9 @@
1111
CRYTIC_SOLC_ARTIFACTS,
1212
CRYTIC_SOLC_JSON,
1313
)
14+
from .models.platforms import Platform
1415
from .models.versions import SolcVersion
1516

16-
if TYPE_CHECKING:
17-
from .models.platforms import Platform
18-
1917

2018
class SolcRepository:
2119
"""Repository for fetching Solidity compiler version information and artifacts."""
@@ -91,7 +89,7 @@ def get_checksums(self, version: SolcVersion) -> tuple[str, str | None]:
9189
return sha256_hash, keccak256_hash
9290

9391

94-
def SoliditylangRepository(platform: "Platform", session: requests.Session) -> SolcRepository:
92+
def SoliditylangRepository(platform: Platform, session: requests.Session) -> SolcRepository:
9593
"""Create a Soliditylang repository for the given platform."""
9694
platform_key = platform.get_soliditylang_key()
9795
return SolcRepository(

solc_select/services/__init__.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1 @@
1-
"""
2-
Service layer for solc-select.
3-
4-
This package contains business logic services that orchestrate between
5-
the domain models and infrastructure layers.
6-
"""
1+
"""Service layer for solc-select."""

solc_select/utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22

33

44
def sort_versions(versions: list[str]) -> list[str]:
5-
"""Sorts a list of versions following the component order (major/minor/patch)"""
5+
"""Sort versions by major/minor/patch order."""
66
return sorted(versions, key=Version)

tests/test_network_isolation.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
import requests
88

9+
from solc_select.services.solc_service import SolcService
10+
911

1012
class TestNetworkIsolation:
1113
"""Test that solc operations don't make unnecessary network requests."""
@@ -23,7 +25,6 @@ def test_solc_version_no_network_calls_after_install(
2325
The isolated_solc_data fixture ensures the binary path resolution works
2426
correctly by redirecting VIRTUAL_ENV to point to the isolated test directory.
2527
"""
26-
from solc_select.services.solc_service import SolcService
2728

2829
# Phase 1: Install version (network calls expected/allowed)
2930
service = SolcService()

0 commit comments

Comments
 (0)