Skillsbench python-scala-libraries
Guide for mapping common Python libraries and idioms to Scala equivalents. Use when converting Python code that uses standard library modules (json, datetime, os, re, logging) or needs equivalent Scala libraries for HTTP, testing, or async operations.
install
source · Clone the upstream repo
git clone https://github.com/benchflow-ai/skillsbench
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/benchflow-ai/skillsbench "$T" && mkdir -p ~/.claude/skills && cp -r "$T/tasks/python-scala-translation/environment/skills/python-scala-libraries" ~/.claude/skills/benchflow-ai-skillsbench-python-scala-libraries && rm -rf "$T"
manifest:
tasks/python-scala-translation/environment/skills/python-scala-libraries/SKILL.mdsource content
Python to Scala Library Mappings
JSON Handling
# Python import json data = {"name": "Alice", "age": 30} json_str = json.dumps(data) parsed = json.loads(json_str) # With dataclass from dataclasses import dataclass, asdict @dataclass class Person: name: str age: int person = Person("Alice", 30) json.dumps(asdict(person))
// Scala - circe (most popular) import io.circe._ import io.circe.generic.auto._ import io.circe.syntax._ import io.circe.parser._ case class Person(name: String, age: Int) val person = Person("Alice", 30) val jsonStr: String = person.asJson.noSpaces val parsed: Either[Error, Person] = decode[Person](jsonStr) // Scala - play-json import play.api.libs.json._ case class Person(name: String, age: Int) implicit val personFormat: Format[Person] = Json.format[Person] val json = Json.toJson(person) val parsed = json.as[Person]
Date and Time
# Python from datetime import datetime, date, timedelta import pytz now = datetime.now() today = date.today() specific = datetime(2024, 1, 15, 10, 30) formatted = now.strftime("%Y-%m-%d %H:%M:%S") parsed = datetime.strptime("2024-01-15", "%Y-%m-%d") tomorrow = now + timedelta(days=1) # Timezone utc_now = datetime.now(pytz.UTC)
// Scala - java.time (recommended) import java.time._ import java.time.format.DateTimeFormatter val now = LocalDateTime.now() val today = LocalDate.now() val specific = LocalDateTime.of(2024, 1, 15, 10, 30) val formatted = now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) val parsed = LocalDate.parse("2024-01-15") val tomorrow = now.plusDays(1) // Timezone val utcNow = ZonedDateTime.now(ZoneOffset.UTC) val instant = Instant.now()
File Operations
# Python from pathlib import Path import os # Reading with open("file.txt", "r") as f: content = f.read() lines = f.readlines() # Writing with open("file.txt", "w") as f: f.write("Hello") # Path operations path = Path("dir/file.txt") path.exists() path.parent path.name path.suffix list(Path(".").glob("*.txt")) os.makedirs("new/dir", exist_ok=True)
// Scala - java.nio.file (standard) + scala.io import java.nio.file.{Files, Path, Paths} import scala.io.Source import scala.util.Using // Reading val content = Source.fromFile("file.txt").mkString Using(Source.fromFile("file.txt")) { source => source.getLines().toList } // Writing Files.writeString(Paths.get("file.txt"), "Hello") // Path operations val path = Paths.get("dir/file.txt") Files.exists(path) path.getParent path.getFileName // Extension: path.toString.split('.').lastOption Files.createDirectories(Paths.get("new/dir")) // os-lib (better alternative) // import os._ // os.read(os.pwd / "file.txt") // os.write(os.pwd / "file.txt", "Hello")
Regular Expressions
# Python import re pattern = r"\d+" text = "abc123def456" # Search match = re.search(pattern, text) if match: print(match.group()) # Find all matches = re.findall(pattern, text) # Replace result = re.sub(pattern, "X", text) # Split parts = re.split(r"\s+", "a b c")
// Scala val pattern = """\d+""".r val text = "abc123def456" // Search pattern.findFirstIn(text) match { case Some(m) => println(m) case None => () } // Find all val matches = pattern.findAllIn(text).toList // Replace val result = pattern.replaceAllIn(text, "X") // Split val parts = """\s+""".r.split("a b c").toList // Pattern matching with regex val datePattern = """(\d{4})-(\d{2})-(\d{2})""".r "2024-01-15" match { case datePattern(year, month, day) => s"$year/$month/$day" case _ => "no match" }
HTTP Requests
# Python import requests response = requests.get("https://api.example.com/data") data = response.json() response = requests.post( "https://api.example.com/data", json={"key": "value"}, headers={"Authorization": "Bearer token"} )
// Scala - sttp (recommended) import sttp.client3._ import sttp.client3.circe._ import io.circe.generic.auto._ val backend = HttpURLConnectionBackend() val response = basicRequest .get(uri"https://api.example.com/data") .send(backend) val postResponse = basicRequest .post(uri"https://api.example.com/data") .body("""{"key": "value"}""") .header("Authorization", "Bearer token") .send(backend)
Logging
# Python import logging logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) logger.debug("Debug message") logger.info("Info message") logger.warning("Warning message") logger.error("Error message", exc_info=True)
// Scala - scala-logging with logback import com.typesafe.scalalogging.LazyLogging class MyClass extends LazyLogging { logger.debug("Debug message") logger.info("Info message") logger.warn("Warning message") logger.error("Error message", exception) } // Alternative: slf4j directly import org.slf4j.LoggerFactory val logger = LoggerFactory.getLogger(getClass)
Testing
# Python - pytest import pytest def test_addition(): assert 1 + 1 == 2 @pytest.fixture def sample_data(): return {"key": "value"} def test_with_fixture(sample_data): assert sample_data["key"] == "value" @pytest.mark.parametrize("input,expected", [ (1, 2), (2, 4), ]) def test_double(input, expected): assert input * 2 == expected
// Scala - ScalaTest import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers class MySpec extends AnyFlatSpec with Matchers { "Addition" should "work" in { 1 + 1 shouldEqual 2 } it should "handle negative numbers" in { -1 + 1 shouldEqual 0 } } // Table-driven tests import org.scalatest.prop.TableDrivenPropertyChecks._ val examples = Table( ("input", "expected"), (1, 2), (2, 4) ) forAll(examples) { (input, expected) => input * 2 shouldEqual expected } // Scala - munit (simpler alternative) class MySuite extends munit.FunSuite { test("addition") { assertEquals(1 + 1, 2) } }
Async/Concurrent Operations
# Python import asyncio async def fetch_data(url: str) -> str: # async HTTP call await asyncio.sleep(1) return "data" async def main(): results = await asyncio.gather( fetch_data("url1"), fetch_data("url2"), )
// Scala - Future (standard) import scala.concurrent.{Future, ExecutionContext} import scala.concurrent.ExecutionContext.Implicits.global def fetchData(url: String): Future[String] = Future { Thread.sleep(1000) "data" } val results: Future[List[String]] = Future.sequence(List( fetchData("url1"), fetchData("url2") )) // Scala - cats-effect IO (preferred for FP) import cats.effect.IO import cats.syntax.parallel._ def fetchData(url: String): IO[String] = IO.sleep(1.second) *> IO.pure("data") val results: IO[List[String]] = List( fetchData("url1"), fetchData("url2") ).parSequence
Configuration
# Python import os from dataclasses import dataclass @dataclass class Config: db_host: str = os.getenv("DB_HOST", "localhost") db_port: int = int(os.getenv("DB_PORT", "5432"))
// Scala - pureconfig import pureconfig._ import pureconfig.generic.auto._ case class Config(dbHost: String, dbPort: Int) val config = ConfigSource.default.loadOrThrow[Config] // application.conf (HOCON format) // db-host = "localhost" // db-host = ${?DB_HOST} // db-port = 5432
Command Line Arguments
# Python import argparse parser = argparse.ArgumentParser() parser.add_argument("--name", required=True) parser.add_argument("--count", type=int, default=1) args = parser.parse_args()
// Scala - scopt import scopt.OParser case class Config(name: String = "", count: Int = 1) val builder = OParser.builder[Config] val parser = { import builder._ OParser.sequence( opt[String]("name").required().action((x, c) => c.copy(name = x)), opt[Int]("count").action((x, c) => c.copy(count = x)) ) } OParser.parse(parser, args, Config()) match { case Some(config) => // use config case None => // arguments are bad } // Scala - decline (FP style) import com.monovore.decline._ val nameOpt = Opts.option[String]("name", "Name") val countOpt = Opts.option[Int]("count", "Count").withDefault(1) val command = Command("app", "Description") { (nameOpt, countOpt).tupled }
Build Tools Comparison
| Python | Scala |
|---|---|
| pip/poetry | sbt/mill |
| requirements.txt | build.sbt |
| pyproject.toml | build.sbt |
| setup.py | build.sbt |
| virtualenv | Project-local dependencies (automatic) |