Chapter 12Lesson 04220–300 min

Resource Files, Variable Files, Library Imports, and Project Layout: Diagnostics, Failure Modes, and Production Practices

Diagnose import and layout failures systematically: preserve first evidence, identify the exact resolver and source, then repair cycles, collisions, cwd dependence, module shadowing, variable-file side effects, and output contamination.

DiagnosticsImport graphCollisionsModule shadowingEvidence

Learning objectives

  • Apply a layered diagnostic sequence before changing import paths.
  • Differentiate missing-resource, missing-library, collision, variable-file, and working-directory failures.
  • Diagnose Python module shadowing using import provenance rather than PYTHONPATH guesswork.
  • Preserve first-failure results and avoid import-time secret leakage.
  • Repair the smallest ownership boundary and rerun a controlled slice.

Current compatibility baseline. Verified 2026-08-31: Robot Framework 7.4.2 is the current stable release and requires Python 3.8+; 7.5b1 is a pre-release and is not required in this chapter. In 7.4.2, .resource is the recommended resource-file extension. Resource and Settings-level variable-file paths are resolved relative to the importing data file first and then via Python's module search path. ${CURDIR} is the absolute directory of the current data file; ${EXECDIR} is the absolute directory where execution started. JSON variable files require no optional package; YAML variable files require PyYAML.

1. Diagnostic sequence: preserve first evidence, then identify the resolver

  1. Preserve console output and the current results/ directory; do not overwrite it.
  2. Record Python/Robot versions, executable path, cwd, and sys.path.
  3. Confirm the exact suite path and command-line --pythonpath/--variablefile options.
  4. Run --dryrun into a new result directory.
  5. Trace each failed Resource/Variables/Library import from the importing file outward.
  6. Inspect keyword qualification and variable-source precedence only after imports succeed.
  7. Inspect external-system/parallel/CI/container state only if the import graph is clean.
  8. Apply the least destructive correction and rerun the smallest suite/test with a fresh output directory.

2. Failure map

Symptom Likely layer Evidence
Resource file not found Source-relative path or module search path Importer path, resource setting, --pythonpath
Library module not found Python environment/module search path sys.executable, sys.path, module.__file__
Keyword is ambiguous Resource/library resolution Imported resources/libraries + full keyword names
Variable changes by runner Variable precedence or import-time behavior Variable-file order, CLI values, environment snapshot
Works only from repo root cwd/EXECDIR-dependent path Compare commands and logged EXECDIR/CURDIR
YAML import fails only in clean env Missing optional PyYAML Pinned dependencies + import error
Source tree becomes dirty after tests Output placement --outputdir and git status

3. Intentionally broken path: cwd dependency disguised as convenience

*** Settings ***
# INTENTIONALLY BRITTLE — resolved by the process environment, not the owning source file.
Variables    variables/demo.py

If this file lives under tests/, the path does not mean “project-root/variables” simply because you normally launch from the project root. Settings-level variable paths are source-relative first. Repair it explicitly as ../variables/demo.py or establish a documented module-search/package contract.

4. Duplicate resource keywords: make the ambiguity observable

*** Settings ***
Resource    ../resources/orders.resource
Resource    ../resources/users.resource

*** Test Cases ***
Ambiguous example
    Normalize ID    abc-123

If both resources define Normalize ID, Robot cannot choose by folder intent. Preserve the failure, then qualify the call:

orders.Normalize ID    order-123
users.Normalize ID     user-123

The production follow-up is to ask whether the names should be more domain-specific. Qualification is transparent; accidental import-order dependence is not.

5. Circular resource imports: stop following the loop and redraw ownership

# resources/a.resource
*** Settings ***
Resource    b.resource

# resources/b.resource
*** Settings ***
Resource    a.resource

A cycle means neither resource is a lower-level dependency. Do not add dynamic Import Resource calls or search-path tricks to hide it. Extract the shared keyword/value into shared.resource and have a.resource and b.resource import that lower layer. The repaired graph should be acyclic and reviewable.

6. Python module shadowing: prove which module was imported

A local file named json.py, robot.py, or the same name as a third-party library can shadow the intended module depending on sys.path. Diagnose with Python import provenance:

python -c "import os,sys; sys.path.insert(0, os.environ['RF12_ROOT']); import libraries.text_tools as m; print(m.__file__)"
python -c "import sys; print('\n'.join(sys.path))"

Do not fix shadowing by prepending arbitrary directories until the desired file happens to win. Rename the conflicting module or package the code so import identity is explicit.

7. Variable-file side effects: imports should not become hidden setup

# INTENTIONALLY UNSAFE DESIGN — do not use for production configuration.
import os
print("token=", os.environ.get("REAL_TOKEN"))
# network_call_to_discover_environment()  # hidden external side effect

This is dangerous because import happens before the scenario has a visible setup/teardown boundary. It can leak secrets to console/logs and make dry-run/environment behavior surprising. Replace it with static/synthetic values or an explicit runtime keyword that can redact evidence and own cleanup.

8. Missing YAML dependency: optional format means optional package

Robot Framework core has no mandatory dependency beyond Python, but YAML variable files require PyYAML. A clean CI environment that installs only robotframework==7.4.2 can therefore fail to load variables.yaml. Either pin PyYAML in the project dependencies or use JSON for dependency-free structured values. Do not “fix CI” by installing unpinned packages interactively on the runner.

9. Results in source directories: diagnose contamination with git status

git status --short
# Expected after a clean run: only intended source edits.
# output.xml/log.html/report.html should be under results/, not tests/.

If generated evidence appears beside source files, correct the runner with an explicit --outputdir. Do not add broad ignore rules that hide arbitrary generated files everywhere; keep the output root deliberate.

10. Brittle ../../ chains: count ownership boundaries, not dots

A path such as ../../../../shared/common.resource signals that the importer depends on repository topology rather than a nearby owner. Before replacing it with --pythonpath, ask whether the resource belongs in a package-like shared root, whether the suite is too deeply nested, or whether the dependency belongs behind a domain resource. Fix architecture first; configure the search path second.

11. Troubleshooting shortcuts to reject

  • Appending arbitrary directories to PYTHONPATH until an import succeeds.
  • Using global Robot variables to “share” configuration around a broken import graph.
  • Deleting the first failing result directory before comparing the repair.
  • Disabling TLS/SSH verification or contacting production systems to “test the real path.”
  • Using broad EXCEPT/retries around import-dependent runtime keywords.
  • Renaming output files into source directories instead of defining an output root.
  • Logging real environment secrets to prove a variable file loaded.

12. Diagnostic exercise: two failures, two layers

Start with the clean Chapter 12 lab. Then introduce these changes separately:

  1. Change Resource ../resources/labels.resource to Resource labels.resource. Run dry-run from an unrelated cwd and preserve the “not found” evidence. Repair the source-relative path.
  2. Add a second resource defining Label Should Normalize. Preserve the ambiguity, qualify the call, then rename/refactor to restore one domain owner.

Do not change both failures at once. One controlled variable per diagnostic run keeps causality visible.

13. Knowledge check

A suite works from the IDE but not CI. What should you inspect before editing imports?

Why is “add the repo root to PYTHONPATH globally” a weak emergency fix?

How do you prove Python imported the expected custom library?

Why should a YAML-only failure in clean CI not be treated as a Robot parser bug?

14. Summary and next step

Import incidents become tractable when you preserve evidence and identify the resolver before changing paths. Source-relative resource/variable paths, Python module discovery, keyword resolution, variable-file behavior, and output placement are distinct layers. Lesson 5 combines them in a checkpoint: run from two directories, inject one path failure and one keyword collision, repair both, and deliver a dependency diagram plus evidence packet.

Next lesson

Checkpoint Lab — Resource Files, Variable Files, Library Imports, and Project Layout

Continue with Checkpoint Lab — Resource Files, Variable Files, Library Imports, and Project Layout. It builds directly on the state, evidence, and operating assumptions established here, so carry those constraints forward rather than treating the next page as an isolated topic.

Further reading

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.