Skip to content

feat: add global tinctures support via tavern-global-cfg (fixes #969)#1033

Open
devimano2011 wants to merge 13 commits intotaverntesting:masterfrom
devimano2011:feat/issue-969-global-tinctures
Open

feat: add global tinctures support via tavern-global-cfg (fixes #969)#1033
devimano2011 wants to merge 13 commits intotaverntesting:masterfrom
devimano2011:feat/issue-969-global-tinctures

Conversation

@devimano2011
Copy link
Copy Markdown
Contributor

@devimano2011 devimano2011 commented May 1, 2026

Fixes #969

Problem

Tinctures could only be applied per-test/stage. Users could not apply a tincture globally to all tests via tavern-global-cfg, despite variables and stages already supporting this.

Solution

This PR adds global tinctures support across 3 files:

  1. tavern/_core/pytest/config.py - Added tinctures: list field to TestConfig dataclass
    1. tavern/_core/pytest/util.py - Loads tinctures from global config files (same as stages and variables)
    1. tavern/_core/run.py - Merges global tinctures into stage tinctures in run_stage()

Usage

Users can now add global tinctures in their tavern-global-cfg file:

# global_config.yaml
tinctures:
  - function: mypackage.helpers:time_request
  - ```
```ini
# pytest.ini
[pytest]
tavern-global-cfg = global_config.yaml

Summary by CodeRabbit

  • New Features
    • Test configuration now supports global tinctures settings that automatically merge with stage-specific tinctures, enabling centralised configuration management.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 1, 2026

📝 Walkthrough

Walkthrough

The changes extend the test configuration structure to support globally configured tinctures. A new tinctures field is added to TestConfig, the global configuration loader reads tinctures from the configuration file, and the stage execution logic merges these global tinctures with per-stage tinctures during test execution.

Changes

Cohort / File(s) Summary
Test Configuration Structure
tavern/_core/pytest/config.py
Added tinctures list attribute to TestConfig dataclass, initialised as an empty list by default.
Global Configuration Loading
tavern/_core/pytest/util.py
Modified global configuration loader to read optional tinctures section from configuration dictionary and pass it to TestConfig constructor.
Stage Execution Runtime
tavern/_core/run.py
Augmented run_stage to merge globally configured tinctures into per-stage tinctures, wrapping each with tavern._core.extfunctions.get_wrapped_response_function before appending.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding global tinctures support via tavern-global-cfg configuration, directly addressing issue #969.
Linked Issues check ✅ Passed The PR successfully implements the requested feature: loading tinctures from global config and merging them into stage tinctures, allowing users to apply tinctures globally across all tests.
Out of Scope Changes check ✅ Passed All changes are directly scoped to implementing global tinctures support: config field addition, loading from global config, and merging into stages. No extraneous modifications detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tavern/_core/pytest/config.py`:
- Around line 36-38: The dataclass defines the field tinctures with a default
before the non-default tavern_internal, causing a dataclass field ordering
TypeError; fix by reordering or giving tavern_internal a default. Specifically,
in the dataclass that declares tinctures and tavern_internal, move the
tavern_internal field above tinctures (so tavern_internal is declared before
tinctures) or assign a default/default_factory to tavern_internal so all fields
with defaults follow non-default fields.

In `@tavern/_core/pytest/util.py`:
- Line 203: At config-load time, normalize global_cfg_dict["tinctures"] before
assigning to tinctures: if the value is a dict wrap it into a single-element
list, if it is None use an empty list, if it is a list validate that every
element is a mapping/dict and raise a TypeError (or ValueError) for any other
type; update the assignment that currently reads
tinctures=global_cfg_dict.get("tinctures", []) to perform these checks and
normalization so downstream code that iterates tinctures always sees a list of
dicts.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5b4fc8db-5c6f-4633-83ad-16bee77d2bbe

📥 Commits

Reviewing files that changed from the base of the PR and between 55e5925 and 424f36b.

📒 Files selected for processing (3)
  • tavern/_core/pytest/config.py
  • tavern/_core/pytest/util.py
  • tavern/_core/run.py

Comment thread tavern/_core/pytest/config.py Outdated
Comment on lines 36 to 38
tinctures: list = dataclasses.field(default_factory=list)

tavern_internal: TavernInternalConfig
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

cat -n tavern/_core/pytest/config.py | head -60

Repository: taverntesting/tavern

Length of output: 2442


🏁 Script executed:

cd tavern && python3 -c "from _core.pytest.config import TestConfig; print('Import successful')" 2>&1

Repository: taverntesting/tavern

Length of output: 462


🏁 Script executed:

python3 << 'EOF'
import dataclasses

`@dataclasses.dataclass`(frozen=True)
class TavernInternalConfig:
    pytest_hook_caller: object
    backends: dict

`@dataclasses.dataclass`(frozen=True)
class TestConfig:
    variables: dict
    strict: object
    follow_redirects: bool
    stages: list
    tinctures: list = dataclasses.field(default_factory=list)
    tavern_internal: TavernInternalConfig

print("No error - dataclass defined successfully")
EOF

Repository: taverntesting/tavern

Length of output: 637


Dataclass field ordering is invalid and prevents module import.

Line 36 introduces a field with a default value before the non-defaulted field at line 38, which violates Python dataclass field ordering rules. This raises a TypeError at class definition time.

Proposed fix
    variables: dict
    strict: StrictLevel
    follow_redirects: bool
    stages: list
-    tinctures: list = dataclasses.field(default_factory=list)
-
    tavern_internal: TavernInternalConfig
+    tinctures: list = dataclasses.field(default_factory=list)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tavern/_core/pytest/config.py` around lines 36 - 38, The dataclass defines
the field tinctures with a default before the non-default tavern_internal,
causing a dataclass field ordering TypeError; fix by reordering or giving
tavern_internal a default. Specifically, in the dataclass that declares
tinctures and tavern_internal, move the tavern_internal field above tinctures
(so tavern_internal is declared before tinctures) or assign a
default/default_factory to tavern_internal so all fields with defaults follow
non-default fields.

Comment thread tavern/_core/pytest/util.py Outdated
backends=_load_global_backends(pytest_config),
),
stages=global_cfg_dict.get("stages", []),
tinctures=global_cfg_dict.get("tinctures", []),
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Normalise global tinctures shape at config load time.

Line 203 assumes a list shape; a single mapping-form tincture will later be iterated as keys and fail at runtime. Please coerce dict -> [dict] (and reject unsupported types) here.

💡 Proposed fix
-    global_cfg = TestConfig(
+    raw_tinctures = global_cfg_dict.get("tinctures", [])
+    if raw_tinctures is None:
+        tinctures = []
+    elif isinstance(raw_tinctures, list):
+        tinctures = raw_tinctures
+    elif isinstance(raw_tinctures, dict):
+        tinctures = [raw_tinctures]
+    else:
+        raise exceptions.BadSchemaError(
+            f"Badly formatted 'tinctures' block in global config: {type(raw_tinctures)}"
+        )
+
+    global_cfg = TestConfig(
         variables=variables,
         strict=_load_global_strictness(pytest_config),
         follow_redirects=_load_global_follow_redirects(pytest_config),
         tavern_internal=TavernInternalConfig(
             pytest_hook_caller=pytest_config.hook,
             backends=_load_global_backends(pytest_config),
         ),
         stages=global_cfg_dict.get("stages", []),
-    tinctures=global_cfg_dict.get("tinctures", []),
+        tinctures=tinctures,
     )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tavern/_core/pytest/util.py` at line 203, At config-load time, normalize
global_cfg_dict["tinctures"] before assigning to tinctures: if the value is a
dict wrap it into a single-element list, if it is None use an empty list, if it
is a list validate that every element is a mapping/dict and raise a TypeError
(or ValueError) for any other type; update the assignment that currently reads
tinctures=global_cfg_dict.get("tinctures", []) to perform these checks and
normalization so downstream code that iterates tinctures always sees a list of
dicts.

fix: move tinctures field before tavern_internal to fix dataclass ordering TypeError
…es helper with type validation and normalization

Add function to normalize tinctures configuration value.
fix: move tinctures field after tavern_internal to fix dataclass TypeError
Copy link
Copy Markdown
Member

@michaelboulton michaelboulton left a comment

Choose a reason for hiding this comment

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

Please also:

  • Add some unit tests (eg, that call run_test and ensure that the 'global' tinctures are called)
  • Add integration tests if possible (though this might be a bit difficult to do?)
  • Update the docs in external_code.md

Comment thread tavern/_core/run.py
Comment on lines +343 to +344
# Merge global tinctures from test config (fixes #969)
from tavern._core.extfunctions import get_wrapped_response_function
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Make this import global and remove the comment

This file contains unit tests for global tinctures support using Tavern. It includes tests for tinctures normalization, merging global tinctures with stage tinctures, and handlingtest: add unit tests for global tinctures normalization and run_stage merging of invalid types.
Correct encoding issues and improve formatting for clarity.
Merge global tinctures from test config to stage tinctures.
Refactor patch statements to use context manager for better readability.
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.

How to apply tinctures to all tests?

2 participants