Skip to content

Commit 2efe2c0

Browse files
balhoffclaude
andauthored
Migrate to ZIO 2 (#529)
* Migrate to ZIO 2.x and zio-logging 2.x Update ZIO 1.0.18 to 2.1.26, zio-logging 0.5.16 to 2.5.3, and switch to zio-logging-slf4j2-bridge to match the slf4j 2.x API on the classpath. Drop the unused zio-streams dependency. - Replace the Logging environment service with ZIO 2 core logging; log context is applied via Main.withLogContext over ZIO.logAnnotate, and the console logger plus slf4j bridge are wired as a layer in Main. - Port the case-app entry point from zio.App to ZIOAppDefault. - Apply core API renames (effect/effectTotal, effectBlocking, foreach_, bracketAuto) and convert the Query reasoner from ZManaged to a scoped resource. - Request a non-zero process exit explicitly on DOSDPError, since ZIOAppDefault no longer derives the exit code from a returned ExitCode. - Update the test suite to zio-test 2.x (ZIOSpecDefault, test, assertZIO). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Preserve CLI failure exit codes * Use ZIO type aliases * Fix logging typo. --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 5f83db0 commit 2efe2c0

47 files changed

Lines changed: 417 additions & 465 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

build.sbt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,12 @@ buildInfoKeys := Seq[BuildInfoKey](name, version, scalaVersion, sbtVersion, gitC
2727

2828
buildInfoPackage := "org.monarchinitiative.dosdp.cli"
2929

30-
val zioVersion = "1.0.18"
30+
val zioVersion = "2.1.26"
31+
val zioLoggingVersion = "2.5.3"
3132

3233
libraryDependencies ++= {
3334
Seq(
3435
"dev.zio" %% "zio" % zioVersion,
35-
"dev.zio" %% "zio-streams" % zioVersion,
3636
"com.github.alexarchambault" %% "case-app" % "2.0.6",
3737
"net.sourceforge.owlapi" % "owlapi-distribution" % "4.5.29",
3838
"org.phenoscape" %% "scowl" % "1.4.1",
@@ -55,8 +55,8 @@ libraryDependencies ++= {
5555
"org.apache.jena" % "apache-jena-libs" % "4.10.0" exclude("org.slf4j", "slf4j-log4j12"),
5656
"com.github.tototoshi" %% "scala-csv" % "2.0.0",
5757
"commons-codec" % "commons-codec" % "1.17.2",
58-
"dev.zio" %% "zio-logging" % "0.5.16",
59-
"dev.zio" %% "zio-logging-slf4j-bridge" % "0.5.16",
58+
"dev.zio" %% "zio-logging" % zioLoggingVersion,
59+
"dev.zio" %% "zio-logging-slf4j2-bridge" % zioLoggingVersion,
6060
"dev.zio" %% "zio-test" % zioVersion % Test,
6161
"dev.zio" %% "zio-test-sbt" % zioVersion % Test
6262
)

src/main/scala/org/monarchinitiative/dosdp/AnnotationCompiler.scala

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@ package org.monarchinitiative.dosdp
33
import org.monarchinitiative.dosdp.cli.DOSDPError
44
import org.monarchinitiative.dosdp.cli.DOSDPError.logErrorFail
55
import org.semanticweb.owlapi.model.OWLAnnotationProperty
6-
import zio._
7-
import zio.logging.Logging
6+
import zio.{Config => _, _}
87

98
/**
109
* Pattern-time normalization of annotations and permutations: resolves
@@ -18,7 +17,7 @@ import zio.logging.Logging
1817
*/
1918
private[dosdp] object AnnotationCompiler {
2019

21-
def normalizeAnnotation(annotation: Annotations, checker: SafeOWLEntityChecker): ZIO[Logging, DOSDPError, NormalizedAnnotation] = annotation match {
20+
def normalizeAnnotation(annotation: Annotations, checker: SafeOWLEntityChecker): IO[DOSDPError, NormalizedAnnotation] = annotation match {
2221
case PrintfAnnotation(anns, ap, text, vars, overrideColumn, multiClause, perms) =>
2322
for {
2423
prop <- checker.getOWLAnnotationProperty(ap).orElse(logErrorFail(s"No annotation property binding: $ap"))
@@ -38,7 +37,7 @@ private[dosdp] object AnnotationCompiler {
3837
} yield NormalizedIRIValueAnnotation(prop, varr, annotations.to(Set))
3938
}
4039

41-
def normalizeOBOAnnotation(annotation: OBOAnnotations, property: OWLAnnotationProperty, overrideColumn: Option[String], checker: SafeOWLEntityChecker): ZIO[Logging, DOSDPError, NormalizedAnnotation] =
40+
def normalizeOBOAnnotation(annotation: OBOAnnotations, property: OWLAnnotationProperty, overrideColumn: Option[String], checker: SafeOWLEntityChecker): IO[DOSDPError, NormalizedAnnotation] =
4241
annotation match {
4342
case PrintfAnnotationOBO(anns, xrefs, text, vars, multiClause, perms) =>
4443
for {
@@ -53,14 +52,14 @@ private[dosdp] object AnnotationCompiler {
5352
xrefs.map(NormalizedListAnnotation(PrintfAnnotationOBO.Xref, _, Set.empty)).to(Set)))
5453
}
5554

56-
def normalizePermutation(permutation: Permutation, checker: SafeOWLEntityChecker): ZIO[Logging, DOSDPError, NormalizedPermutation] =
55+
def normalizePermutation(permutation: Permutation, checker: SafeOWLEntityChecker): IO[DOSDPError, NormalizedPermutation] =
5756
for {
5857
props <- ZIO.foreach(permutation.annotationProperties)(apName =>
5958
checker.getOWLAnnotationProperty(apName).orElse(logErrorFail(s"No annotation property binding for permutation: $apName")))
6059
} yield NormalizedPermutation(permutation.`var`, props)
6160

6261
/** Every variable named in a permutation spec must appear in the enclosing annotation's `vars` list. */
63-
def validatePermutationVars(permutations: List[Permutation], vars: List[String]): ZIO[Logging, DOSDPError, Unit] = {
62+
def validatePermutationVars(permutations: List[Permutation], vars: List[String]): IO[DOSDPError, Unit] = {
6463
val varSet = vars.toSet
6564
val invalidVars = permutations.map(_.`var`).filterNot(varSet.contains)
6665
if (invalidVars.isEmpty) ZIO.unit
@@ -72,7 +71,7 @@ private[dosdp] object AnnotationCompiler {
7271
* Names that fail to resolve are silently dropped here; `normalizePermutation` is the canonical
7372
* place where an unresolved reference becomes a validation error.
7473
*/
75-
def permutationAnnotationProperties(dosdp: DOSDP, checker: SafeOWLEntityChecker): URIO[Logging, Set[OWLAnnotationProperty]] = {
74+
def permutationAnnotationProperties(dosdp: DOSDP, checker: SafeOWLEntityChecker): UIO[Set[OWLAnnotationProperty]] = {
7675
val names = collectPermutationPropertyNames(dosdp)
7776
ZIO.foreach(names.toList)(name => checker.getOWLAnnotationProperty(name).option)
7877
.map(_.flatten.toSet)

src/main/scala/org/monarchinitiative/dosdp/CompiledPattern.scala

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@ import org.monarchinitiative.dosdp.AxiomType
66
import org.semanticweb.owlapi.apibinding.OWLManager
77
import org.semanticweb.owlapi.manchestersyntax.parser.{ManchesterOWLSyntaxClassExpressionParser, ManchesterOWLSyntaxInlineAxiomParser}
88
import org.semanticweb.owlapi.model.{AxiomType => _, _}
9-
import zio._
10-
import zio.logging.Logging
9+
import zio.{Config => _, _}
1110

1211
import scala.jdk.CollectionConverters._
1312

@@ -197,7 +196,7 @@ private[dosdp] object PatternCompiler {
197196
private val PlaceholderLiteralPattern = """^\$(.+)$""".r
198197
private val VariableNamePattern = "^[A-Za-z0-9_]+$".r
199198

200-
def compile(dosdp: DOSDP, prefixes: PartialFunction[String, String]): ZIO[Logging, DOSDPError, CompiledPattern] = {
199+
def compile(dosdp: DOSDP, prefixes: PartialFunction[String, String]): IO[DOSDPError, CompiledPattern] = {
201200
val checker = new DOSDPEntityChecker(dosdp, prefixes)
202201
val safeChecker = new SafeOWLEntityChecker(checker)
203202
val expressionParser = new ManchesterOWLSyntaxClassExpressionParser(factory, checker)
@@ -232,7 +231,7 @@ private[dosdp] object PatternCompiler {
232231
dataVarNames = dataVarNames)
233232
}
234233

235-
private def validateVariableNames(dosdp: DOSDP): ZIO[Logging, DOSDPError, Unit] = {
234+
private def validateVariableNames(dosdp: DOSDP): IO[DOSDPError, Unit] = {
236235
val invalidNames = variableNames(dosdp)
237236
.filterNot(name => VariableNamePattern.pattern.matcher(name).matches)
238237
.toList.sorted
@@ -312,7 +311,7 @@ private[dosdp] object PatternCompiler {
312311
dataVarNames: Set[String],
313312
checker: SafeOWLEntityChecker,
314313
placeholderBindings: Map[String, Binding]
315-
): ZIO[Logging, DOSDPError, Option[CompiledClassExpression]] =
314+
): IO[DOSDPError, Option[CompiledClassExpression]] =
316315
ZIO.foreach(templateOpt) { template =>
317316
for {
318317
annotations <- ZIO.foreach(template.annotations.toList.flatten)(AnnotationCompiler.normalizeAnnotation(_, checker)).map(_.toSet)
@@ -326,7 +325,7 @@ private[dosdp] object PatternCompiler {
326325
dataVarNames: Set[String],
327326
annotations: Set[NormalizedAnnotation],
328327
placeholderBindings: Map[String, Binding]
329-
): ZIO[Logging, DOSDPError, Option[CompiledClassExpression]] =
328+
): IO[DOSDPError, Option[CompiledClassExpression]] =
330329
(template.text, template.multi_clause) match {
331330
case (Some(_), _) =>
332331
parseClauseText(template.text, template.vars, template.multi_clause, dataVarNames, parser, template.shouldQuote, placeholderBindings)
@@ -347,7 +346,7 @@ private[dosdp] object PatternCompiler {
347346
quote: Boolean,
348347
annotations: Set[NormalizedAnnotation],
349348
placeholderBindings: Map[String, Binding]
350-
): ZIO[Logging, DOSDPError, CompiledMultiClassExpression] =
349+
): IO[DOSDPError, CompiledMultiClassExpression] =
351350
for {
352351
clauses <- ZIO.foreach(mc.clauses.toList.flatten)(compilePrintfClause(_, dataVarNames, parser, quote, placeholderBindings))
353352
op <- operatorFor(mc.sep, clauses.size)
@@ -359,7 +358,7 @@ private[dosdp] object PatternCompiler {
359358
parser: ManchesterOWLSyntaxClassExpressionParser,
360359
quote: Boolean,
361360
placeholderBindings: Map[String, Binding]
362-
): ZIO[Logging, DOSDPError, CompiledPrintfClause] =
361+
): IO[DOSDPError, CompiledPrintfClause] =
363362
for {
364363
main <- parseClauseText(Some(clause.text), clause.vars, None, dataVarNames, parser, quote, placeholderBindings)
365364
subs <- ZIO.foreach(clause.sub_clauses.toList.flatten)(compileSubExpression(_, dataVarNames, parser, quote, placeholderBindings))
@@ -371,7 +370,7 @@ private[dosdp] object PatternCompiler {
371370
parser: ManchesterOWLSyntaxClassExpressionParser,
372371
quote: Boolean,
373372
placeholderBindings: Map[String, Binding]
374-
): ZIO[Logging, DOSDPError, CompiledSubExpression] =
373+
): IO[DOSDPError, CompiledSubExpression] =
375374
for {
376375
clauses <- ZIO.foreach(mc.clauses.toList.flatten)(compilePrintfClause(_, dataVarNames, parser, quote, placeholderBindings))
377376
op <- operatorFor(mc.sep, clauses.size)
@@ -387,7 +386,7 @@ private[dosdp] object PatternCompiler {
387386
* lone expression through). Omitting `sep` with two or more clauses is an
388387
* error.
389388
*/
390-
private def operatorFor(sep: Option[String], clauseCount: Int): ZIO[Logging, DOSDPError, LogicalOperator] =
389+
private def operatorFor(sep: Option[String], clauseCount: Int): IO[DOSDPError, LogicalOperator] =
391390
sep match {
392391
case Some(" and ") => ZIO.succeed(LogicalOperator.And)
393392
case Some(" or ") => ZIO.succeed(LogicalOperator.Or)
@@ -403,7 +402,7 @@ private[dosdp] object PatternCompiler {
403402
dataVarNames: Set[String],
404403
checker: SafeOWLEntityChecker,
405404
placeholderBindings: Map[String, Binding]
406-
): ZIO[Logging, DOSDPError, Option[CompiledAxiom]] =
405+
): IO[DOSDPError, Option[CompiledAxiom]] =
407406
ZIO.foreach(templateOpt.filter(_.text.isDefined)) { template =>
408407
for {
409408
_ <- ZIO.when(template.multi_clause.isDefined)(
@@ -420,7 +419,7 @@ private[dosdp] object PatternCompiler {
420419
dataVarNames: Set[String],
421420
checker: SafeOWLEntityChecker,
422421
placeholderBindings: Map[String, Binding]
423-
): ZIO[Logging, DOSDPError, Option[CompiledLogicalAxiom]] = {
422+
): IO[DOSDPError, Option[CompiledLogicalAxiom]] = {
424423
for {
425424
annotations <- ZIO.foreach(template.annotations.toList.flatten)(AnnotationCompiler.normalizeAnnotation(_, checker)).map(_.toSet)
426425
compiled <- template.axiom_type match {
@@ -450,12 +449,12 @@ private[dosdp] object PatternCompiler {
450449
parser: ManchesterOWLSyntaxClassExpressionParser,
451450
quote: Boolean,
452451
placeholderBindings: Map[String, Binding]
453-
): ZIO[Logging, DOSDPError, ParsedPiece[OWLClassExpression]] = {
452+
): IO[DOSDPError, ParsedPiece[OWLClassExpression]] = {
454453
val effective = withFallbackPlaceholders(referencedVars(vars, multi), placeholderBindings)
455454
val resolved = PrintfText.replaced(text, vars, multi, Some(effective), quote, dataVarNames)
456455
ZIO.fromOption(resolved).orElse(logErrorFail(s"Could not assemble Manchester template text for parsing"))
457456
.flatMap { rendered =>
458-
ZIO.effect(parser.parse(rendered))
457+
ZIO.attempt(parser.parse(rendered))
459458
.flatMapError(e => DOSDPError.logError(s"Failed to parse class expression: $rendered", e))
460459
.map(ce => describePiece(ce, vars))
461460
}
@@ -468,12 +467,12 @@ private[dosdp] object PatternCompiler {
468467
parser: ManchesterOWLSyntaxInlineAxiomParser,
469468
quote: Boolean,
470469
placeholderBindings: Map[String, Binding]
471-
): ZIO[Logging, DOSDPError, ParsedPiece[OWLAxiom]] = {
470+
): IO[DOSDPError, ParsedPiece[OWLAxiom]] = {
472471
val effective = withFallbackPlaceholders(vars.getOrElse(Nil), placeholderBindings)
473472
val resolved = PrintfText.replaced(text, vars, multi_clause = None, Some(effective), quote, dataVarNames)
474473
ZIO.fromOption(resolved).orElse(logErrorFail("Could not assemble Manchester axiom text for parsing"))
475474
.flatMap { rendered =>
476-
ZIO.effect(parser.parse(rendered))
475+
ZIO.attempt(parser.parse(rendered))
477476
.flatMapError(e => DOSDPError.logError(s"Failed to parse axiom: $rendered", e))
478477
.map(ax => describePiece(ax, vars))
479478
}
@@ -605,7 +604,7 @@ private[dosdp] object PatternCompiler {
605604
case _ => ()
606605
}
607606

608-
private def compileOBOAnnotations(dosdp: DOSDP, checker: SafeOWLEntityChecker): ZIO[Logging, DOSDPError, Set[NormalizedAnnotation]] = {
607+
private def compileOBOAnnotations(dosdp: DOSDP, checker: SafeOWLEntityChecker): IO[DOSDPError, Set[NormalizedAnnotation]] = {
609608
val fields: List[(Iterable[OBOAnnotations], OWLAnnotationProperty, Option[String])] = List(
610609
(dosdp.name, Name, Some(overrides(Name))),
611610
(dosdp.comment, Comment, Some(overrides(Comment))),
@@ -625,7 +624,7 @@ private[dosdp] object PatternCompiler {
625624
}.map(_.flatten.toSet)
626625
}
627626

628-
private def compileReadableIdentifierProperties(dosdp: DOSDP, checker: SafeOWLEntityChecker): ZIO[Logging, DOSDPError, List[OWLAnnotationProperty]] =
627+
private def compileReadableIdentifierProperties(dosdp: DOSDP, checker: SafeOWLEntityChecker): IO[DOSDPError, List[OWLAnnotationProperty]] =
629628
ZIO.foreach(dosdp.readable_identifiers) { identifiers =>
630629
ZIO.foreach(identifiers)(name =>
631630
checker.getOWLAnnotationProperty(name)

src/main/scala/org/monarchinitiative/dosdp/DocsMarkdown.scala

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@ import org.monarchinitiative.dosdp.cli.Docs.DocData
55
import org.phenoscape.scowl._
66
import org.semanticweb.owlapi.io.OWLObjectRenderer
77
import org.semanticweb.owlapi.model.{IRI, OWLObject}
8-
import zio.ZIO
9-
import zio.logging.Logging
8+
import zio.{IO, ZIO}
109

1110
import scala.jdk.CollectionConverters._
1211

@@ -20,7 +19,7 @@ import scala.jdk.CollectionConverters._
2019
*/
2120
object DocsMarkdown {
2221

23-
def markdown(compiled: CompiledPattern, docData: DocData, renderer: OWLObjectRenderer, data: List[List[String]]): ZIO[Logging, DOSDPError, String] = {
22+
def markdown(compiled: CompiledPattern, docData: DocData, renderer: OWLObjectRenderer, data: List[List[String]]): IO[DOSDPError, String] = {
2423
def r(obj: OWLObject): String = renderer.render(obj).replace("\n", " ")
2524

2625
val dosdp = compiled.source

src/main/scala/org/monarchinitiative/dosdp/SPARQL.scala

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,15 @@ import org.phenoscape.owlet.OwletManchesterSyntaxDataType.SerializableClassExpre
99
import org.semanticweb.owlapi.apibinding.OWLManager
1010
import org.semanticweb.owlapi.model._
1111
import org.phenoscape.scowl._
12-
import zio._
13-
import zio.logging._
12+
import zio.{Config => _, _}
1413

1514
import scala.jdk.CollectionConverters._
1615

1716
object SPARQL {
1817

1918
private val factory = OWLManager.getOWLDataFactory()
2019

21-
def queryFor(compiled: CompiledPattern, axioms: AxiomKind): ZIO[Logging, DOSDPError, String] = {
20+
def queryFor(compiled: CompiledPattern, axioms: AxiomKind): IO[DOSDPError, String] = {
2221
// Logical axioms always materialize even on an annotation-only query: the
2322
// `OPTIONAL { ?var rdfs:label ... }` clauses are derived from their
2423
// variable set. Compute once and share with `selectFor` / `triplesFor` so
@@ -63,10 +62,10 @@ ORDER BY ?defined_class_label
6362

6463
private val Thing = OWLManager.getOWLDataFactory.getOWLThing
6564

66-
def triplesFor(compiled: CompiledPattern, axioms: AxiomKind): ZIO[Logging, DOSDPError, Seq[String]] =
65+
def triplesFor(compiled: CompiledPattern, axioms: AxiomKind): IO[DOSDPError, Seq[String]] =
6766
triplesFor(compiled, axioms, Expansion.placeholderAxioms(compiled, LogicalAxioms))
6867

69-
private def triplesFor(compiled: CompiledPattern, axioms: AxiomKind, logicalAxioms: Set[OWLAxiom]): ZIO[Logging, DOSDPError, Seq[String]] = {
68+
private def triplesFor(compiled: CompiledPattern, axioms: AxiomKind, logicalAxioms: Set[OWLAxiom]): IO[DOSDPError, Seq[String]] = {
7069
val props = compiled.readableIdentifierProperties.to(Set)
7170
val (queryLogical, queryAnnotations) = Generate.axiomsOutputChoice(axioms)
7271
val annotationAxioms = if (queryAnnotations) Expansion.placeholderAxioms(compiled, AnnotationAxioms) else Set.empty[OWLAxiom]
@@ -94,13 +93,13 @@ ORDER BY ?defined_class_label
9493
} yield annotationTriples ++ axiomTriples ++ variableTriples ++ labelTriples
9594
}
9695

97-
def triplesForAxiom(axiom: OWLAxiom, readableIdentifierProperties: Set[OWLAnnotationProperty]): URIO[Logging, Seq[String]] = axiom match {
96+
def triplesForAxiom(axiom: OWLAxiom, readableIdentifierProperties: Set[OWLAnnotationProperty]): UIO[Seq[String]] = axiom match {
9897
case subClassOf: OWLSubClassOfAxiom =>
9998
val (subClass, subClassTriples) = triplesForClassExpression(subClassOf.getSubClass)
10099
val (superClass, superClassTriples) = triplesForClassExpression(subClassOf.getSuperClass)
101100
ZIO.succeed(Seq(s"$subClass rdfs:subClassOf $superClass .") ++ subClassTriples ++ superClassTriples)
102101
case equivalentTo: OWLEquivalentClassesAxiom =>
103-
log.warn("More than two operands or missing named class in equivalent class axiom unexpected")
102+
ZIO.logWarning("More than two operands or missing named class in equivalent class axiom unexpected")
104103
.when(!equivalentTo.containsNamedEquivalentClass || (equivalentTo.getClassExpressions.size > 2)) *>
105104
ZIO.succeed {
106105
(for {
@@ -113,7 +112,7 @@ ORDER BY ?defined_class_label
113112
}).toSeq.flatten
114113
}
115114
case disjointWith: OWLDisjointClassesAxiom =>
116-
log.warn("More than two operands or missing named class in disjointness axiom unexpected")
115+
ZIO.logWarning("More than two operands or missing named class in disjointness axiom unexpected")
117116
.when(!disjointWith.getClassExpressions.asScala.forall(_.isAnonymous) || (disjointWith.getClassExpressions.size > 2)) *>
118117
ZIO.succeed {
119118
(for {

src/main/scala/org/monarchinitiative/dosdp/SafeOWLEntityChecker.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package org.monarchinitiative.dosdp
22

33
import org.semanticweb.owlapi.expression.OWLEntityChecker
44
import org.semanticweb.owlapi.model._
5-
import zio._
5+
import zio.{Config => _, _}
66

77
class SafeOWLEntityChecker(checker: OWLEntityChecker) {
88

0 commit comments

Comments
 (0)