Skip to content

Multi-language documentation snippets (Java, Kotlin, Groovy, Python) - #2107

Draft
graemerocher wants to merge 2 commits into
7.2.xfrom
python-docs
Draft

graemerocher wants to merge 2 commits into
7.2.xfrom
python-docs

Conversation

@graemerocher

@graemerocher graemerocher commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

Every application-code sample of the user guide is now a snippet:: macro backed by compiled and tested sources in Java, Kotlin, Groovy and Python (the Python compiler), and every dependency uses the dependency: macro so it renders Gradle, Maven and the Python compiler tabs.

Part of the multi-language documentation effort built on micronaut-projects/micronaut-build#939.

Important

Requires micronaut-build 8.1.2 (io.micronaut.build.internal.python plugin, Python dependency: rendering, micronautBuild.python.compilerArgs) and micronaut-core 5.2.3 (the Python compiler and runtime fixes the Hibernate samples rely on); both are released and pinned by this PR.

What was ported

New projects: test-suite (JUnit 5), test-suite-groovy (Spock), test-suite-kotlin (KSP, JUnit 5), test-suite-python (the Python compiler, JUnit 5), sharing the io.micronaut.build.internal.sql-test-suite convention (hibernate-jpa + jdbc-hikari + data-tx-hibernate + H2, no Docker), plus tests/mybatis-python next to the existing tests/mybatis-{java,groovy,kotlin}.

Guide section Former inline block(s) Snippet target(s)
Injecting an EntityManager or Hibernate Session [source,java] @PersistenceContext + stale [source,groovy] annotationProcessor "jakarta.persistence:jakarta.persistence-api:2.2" io.micronaut.docs.hibernate.session.BookRepository
Entity Scan Configuration @Introspected(packages="foo.bar") io.micronaut.docs.hibernate.entityscan.Application
Using compile-time Hibernate proxies Pet / Owner io.micronaut.docs.hibernate.proxies.Pet, Owner
Hibernate GraalVM native image @Introspected(packages), Owner, Pet, OrderId, Order the two above + io.micronaut.docs.hibernate.graalvm.OrderId, Order
Configuring Multiple Data Sources @Inject DataSource / @Named("warehouse") io.micronaut.docs.jdbc.multiple.InventoryService
Transaction Management @Transactional void saveBook(Book) io.micronaut.docs.jdbc.transactions.BookRepository
Data Source Runtime Password Change two DbPasswordRefresher variants io.micronaut.docs.jdbc.refresh.DbPasswordRefresher, io.micronaut.docs.jdbc.refreshdiff.DbPasswordRefresher
Configuring MyBatis customizers existing snippet (project-base="tests/mybatis") Python sibling tests/mybatis-python added
Configuring Jdbi hand-written Gradle implementation("org.jdbi:jdbi3-sqlobject") dependency:jdbi3-sqlobject[groupId="org.jdbi"]

13 inline Java blocks + 1 Groovy build block converted; 14 snippet:: macros × 4 languages; 28 dependency: macros render a the Python compiler tab.

Documentation fixes found while making the samples compile and pass:

  • @Introspected(packages = "...") alone no longer compiles ("When specifying 'packages' you must also specify 'includedAnnotations'"); the sample now uses includedAnnotations = Entity.class.
  • jakarta.transaction.Transactional only works with micronaut-data-processor on the annotation processor path; the transactions section now says so (dependency: macro). A package-private void saveBook(Book) is not intercepted; the sample method is public.
  • The JPA annotations do not need to be on the annotationProcessor path (removed the stale jakarta.persistence-api:2.2 instruction).
  • The injected DataSource is transaction aware: connections must be obtained inside a transactional method (the multiple data sources sample shows @Transactional("warehouse")).
  • CustomConfigurationCustomizer (MyBatis) also tunes a setting (mapUnderscoreToCamelCase) so the Python suite, which cannot declare MyBatis mapper interfaces, has something to assert.

Not ported (and why)

  • jasync-client.adoc, vertxmysqlclient.adoc, vertxpgclient.adoc: the [source,groovy] blocks include:: fragments of the modules' own Testcontainers specs (JasyncSpec, MySQLClientSpec, PgClientSpec) which need a live PostgreSQL/MySQL (Docker). They stay as Groovy includes of tested code.
  • jooq-graalvm.adoc [source,groovy]: jOOQ Gradle plugin configuration (build file, not application code).
  • Python, marked @Disabled with TODO(python) and listed in test-suite-python/src/test/python/micronaut/docs/DISABLED_TESTS.md: hibernate.session.BookRepositoryTest and hibernate.proxies.CompileTimeProxiesTest. The Python entities are plain @Entity classes and Hibernate maps them (the suite passes -Amicronaut.introspection.allowReflection=micronaut.docs.hibernate.* to the Python compiler so the generated classes carry the JPA annotations), but the id Hibernate assigns on entity_manager.persist(book) is set on the Java wrapper of the Python object and not written back to it, so book.id stays None in the sample's save. hibernate.entityscan.EntityScanTest and hibernate.graalvm.EmbeddedIdTest pass; the JDBC and MyBatis Python tests pass.
  • Groovy, marked @Ignore: hibernate.proxies.CompileTimeProxiesSpec - @GenerateProxy on a Groovy entity produces a $Owner$Intercepted proxy without IntroducedHibernateProxy.$registerInterceptor (inject-groovy's GroovyClassElement skips $-prefixed methods), so loading the lazy association throws AbstractMethodError (micronaut-core issue; Java and Kotlin work).

Verification

./gradlew :test-suite:test                       # 8 tests, 0 failures
./gradlew :test-suite-kotlin:test                # 8 tests, 0 failures
./gradlew :test-suite-groovy:test                # 8 tests, 0 failures (1 ignored, see above)
./gradlew pythonCheck -Ppython-ci                # test-suite-python: 8 tests (6 pass, 2 disabled), tests/mybatis-python: 1 test
./gradlew :micronaut-tests:micronaut-mybatis-java:test :micronaut-tests:micronaut-mybatis-kotlin:test :micronaut-tests:micronaut-mybatis-groovy:test
./gradlew docs                                   # build/docs/guide/index.html: data-lang="java" 14, "kotlin" 14, "groovy" 18 (14 + 4 Groovy-only blocks), "python" 14, "python" 28 (= all dependency: macros)

Without -Ppython-ci the Python sources are compiled but their Test tasks are skipped (regular CI); .github/workflows/python.yml is the project-template workflow that runs them on GraalVM.

Every application-code sample of the user guide is now a `snippet::` macro backed
by compiled and tested sources in Java, Kotlin, Groovy and Python:
- new `test-suite`, `test-suite-groovy`, `test-suite-kotlin` and `test-suite-python`
  projects (convention plugin `io.micronaut.build.internal.sql-test-suite`) hosting the
  hibernate (@PersistenceContext injection, entity scan, compile-time proxies, @EmbeddedId)
  and jdbc (multiple data sources, @transactional, runtime password rotation) examples
  against an in-memory H2 database
- new `tests/mybatis-python` project for the existing `project-base="tests/mybatis"` snippet
- the 13 inline Java blocks and the stale Groovy `annotationProcessor` block are replaced by
  snippets; hand-written dependency blocks use the `dependency:` macro
- Python CI workflow (`./gradlew pythonCheck -Ppython-ci`) synced from the project template
TODO(python-docs): requires micronaut-build 8.1.1 (micronaut-projects/micronaut-build#940);
Bump micronaut-core to 5.2.3 and micronaut-build to 8.1.2, pass the
allowReflection option to the Python compiler so that Hibernate maps the
Python entities, and re-enable the entity scan and embedded id tests. The
two tests that read the id Hibernate assigns on persist stay disabled.
@graemerocher
graemerocher marked this pull request as ready for review September 21, 2026 16:05
@graemerocher
graemerocher marked this pull request as draft September 21, 2026 16:05

@dstepanov dstepanov left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: multi-language docs port (pyronaut#139)

Verdict: ⚠️ minor issues

Summary: the PR bumps micronaut-build to 8.1.2 and core to 5.2.3. There are no production (src/main) changes. Deleted docs lines are only inline blocks replaced by snippets, and their prose moved into callouts; the long DbPasswordRefresher comments were kept. The two @Disabled Python tests match tracked item (d) in the "re-verifying against 5.2.3" section of the issue (Java-side id write-back on persist), so they are not stale. The -Amicronaut.introspection.allowReflection compiler arg matches the core#13252 follow-up. CI is skipped because the PR is a draft.

Findings:

  • [medium] src/main/docs/guide/hibernate/hibernate-proxies.adoc:24, hibernate-graalvm.adoc:37 — the Groovy tab of snippet::…proxies.Owner shows a @GenerateProxy Groovy entity. That sample is known not to work: test-suite-groovy/…/proxies/CompileTimeProxiesSpec.groovy:12 is @Ignored because inject-groovy skips $-prefixed methods, and loading the lazy association throws AbstractMethodError. Groovy users get a sample that fails at runtime with no warning. Either add a [.lang-groovy] NOTE or render Owner/Pet with languages="java,kotlin,python" until the core fix lands.
  • [low] test-suite/src/test/java/io/micronaut/docs/jdbc/refresh/DbPasswordRefresher.java:21-30 (and the Kotlin/Groovy/Python equivalents) — the published sample now injects the test-only DbSecretStore and delegates getSecretDbPassword() to it. The guide never introduces DbSecretStore, so readers see an unknown type in the constructor. Keep the store out of the clazz tag (for example, leave getSecretDbPassword() as a documented stub) or add a sentence to the guide.
  • [low] tests/mybatis-{java,groovy,kotlin}/…/CustomConfigurationCustomizer.*:17 — setMapUnderscoreToCamelCase(true) was added to the rendered MyBatis customizer sample in all languages only so the Python test has something to assert. The PR body explains it, but it changes what the documented example does. Acceptable; just flagging the behaviour change.
  • [low] .github/workflows/python.yml — missing the python/**, python-* stacked-branch targets of the current template (#794).

@dstepanov dstepanov left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving — the findings in my previous review are minor.

@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants