[{"data":1,"prerenderedAt":904},["ShallowReactive",2],{"page-\u002Fadvanced-pytest-architecture-configuration\u002Ftest-layout-and-import-modes\u002Ffixing-import-file-mismatch-errors-in-pytest\u002F":3},{"id":4,"title":5,"body":6,"description":865,"extension":866,"meta":867,"navigation":124,"path":900,"seo":901,"stem":902,"__hash__":903},"content\u002Fadvanced-pytest-architecture-configuration\u002Ftest-layout-and-import-modes\u002Ffixing-import-file-mismatch-errors-in-pytest\u002Findex.md","Fixing import file mismatch Errors in pytest",{"type":7,"value":8,"toc":854},"minimark",[9,18,21,26,52,56,59,166,174,196,227,250,394,398,425,433,437,529,533,536,542,548,627,637,641,644,656,664,729,733,736,743,749,753,773,791,809,813,845,850],[10,11,12,13,17],"p",{},"The error reads ",[14,15,16],"code",{},"import file mismatch: imported module 'test_invoice' has this __file__ attribute … which is not the same as the test file we want to collect",", and it usually appears the day someone adds a test file whose name already exists elsewhere in the suite. It is not a bug in pytest and not random: it is pytest noticing that two different files are trying to become the same Python module, and refusing to run the wrong one silently.",[10,19,20],{},"The fix is quick once the mechanism is clear, and the durable version of the fix — switching import mode — also removes the class of problem for good. This guide walks through the cause, the two fixes, and how to confirm the layout is sound afterwards.",[22,23,25],"h2",{"id":24},"prerequisites","Prerequisites",[27,28,29,40,43],"ul",{},[30,31,32,35,36,39],"li",{},[14,33,34],{},"pytest >= 8.0","; ",[14,37,38],{},"--import-mode=importlib"," has existed since 6.0 but is reliable for most layouts from 8.",[30,41,42],{},"Shell access to the repository to list and inspect files.",[30,44,45,46,51],{},"The layout background in ",[47,48,50],"a",{"href":49},"\u002Fadvanced-pytest-architecture-configuration\u002Ftest-layout-and-import-modes\u002F","test layout and import modes",".",[22,53,55],{"id":54},"solution","Solution",[10,57,58],{},"First rule out stale bytecode, then find the collision, then remove it.",[60,61,66],"pre",{"className":62,"code":63,"language":64,"meta":65,"style":65},"language-bash shiki shiki-themes github-light github-dark","# 1. Stale .pyc files from moved or renamed tests can trigger the same error.\nfind . -name \"__pycache__\" -type d -prune -exec rm -rf {} +\n\n# 2. Which basenames occur more than once?\nfind tests -name \"test_*.py\" -printf \"%f\\n\" | sort | uniq -d\n","bash","",[14,67,68,77,119,126,132],{"__ignoreMap":65},[69,70,73],"span",{"class":71,"line":72},"line",1,[69,74,76],{"class":75},"sJ8bj","# 1. Stale .pyc files from moved or renamed tests can trigger the same error.\n",[69,78,80,84,88,92,95,98,101,104,107,110,113,116],{"class":71,"line":79},2,[69,81,83],{"class":82},"sScJk","find",[69,85,87],{"class":86},"sZZnC"," .",[69,89,91],{"class":90},"sj4cs"," -name",[69,93,94],{"class":86}," \"__pycache__\"",[69,96,97],{"class":90}," -type",[69,99,100],{"class":86}," d",[69,102,103],{"class":90}," -prune",[69,105,106],{"class":90}," -exec",[69,108,109],{"class":86}," rm",[69,111,112],{"class":90}," -rf",[69,114,115],{"class":86}," {}",[69,117,118],{"class":86}," +\n",[69,120,122],{"class":71,"line":121},3,[69,123,125],{"emptyLinePlaceholder":124},true,"\n",[69,127,129],{"class":71,"line":128},4,[69,130,131],{"class":75},"# 2. Which basenames occur more than once?\n",[69,133,135,137,140,142,145,148,151,155,158,160,163],{"class":71,"line":134},5,[69,136,83],{"class":82},[69,138,139],{"class":86}," tests",[69,141,91],{"class":90},[69,143,144],{"class":86}," \"test_*.py\"",[69,146,147],{"class":90}," -printf",[69,149,150],{"class":86}," \"%f\\n\"",[69,152,154],{"class":153},"szBVR"," |",[69,156,157],{"class":82}," sort",[69,159,154],{"class":153},[69,161,162],{"class":82}," uniq",[69,164,165],{"class":90}," -d\n",[60,167,172],{"className":168,"code":170,"language":171,"meta":65},[169],"language-text","test_invoice.py\ntest_models.py\n","text",[14,173,170],{"__ignoreMap":65},[60,175,179],{"className":176,"code":177,"language":178,"meta":65,"style":65},"language-toml shiki shiki-themes github-light github-dark","# 3a. The durable fix: import by path, no module-name collisions at all.\n[tool.pytest.ini_options]\naddopts = \"--import-mode=importlib\"\n","toml",[14,180,181,186,191],{"__ignoreMap":65},[69,182,183],{"class":71,"line":72},[69,184,185],{},"# 3a. The durable fix: import by path, no module-name collisions at all.\n",[69,187,188],{"class":71,"line":79},[69,189,190],{},"[tool.pytest.ini_options]\n",[69,192,193],{"class":71,"line":121},[69,194,195],{},"addopts = \"--import-mode=importlib\"\n",[60,197,199],{"className":62,"code":198,"language":64,"meta":65,"style":65},"# 3b. The alternative under prepend mode: make every directory a package.\nfind tests -type d -exec touch {}\u002F__init__.py \\;\n",[14,200,201,206],{"__ignoreMap":65},[69,202,203],{"class":71,"line":72},[69,204,205],{"class":75},"# 3b. The alternative under prepend mode: make every directory a package.\n",[69,207,208,210,212,214,216,218,221,224],{"class":71,"line":79},[69,209,83],{"class":82},[69,211,139],{"class":86},[69,213,97],{"class":90},[69,215,100],{"class":86},[69,217,106],{"class":90},[69,219,220],{"class":86}," touch",[69,222,223],{"class":86}," {}\u002F__init__.py",[69,225,226],{"class":90}," \\;\n",[10,228,229,230,233,234,237,238,241,242,245,246,249],{},"The second command is shown for completeness. Under the default ",[14,231,232],{},"prepend"," mode it works, because it changes the module names from ",[14,235,236],{},"test_invoice"," to ",[14,239,240],{},"tests.billing.test_invoice"," and ",[14,243,244],{},"tests.api.test_invoice",". But it turns the test tree into an importable package called ",[14,247,248],{},"tests",", which can collide with other distributions and has to be excluded from the built wheel. The configuration change avoids all of that and is the one to prefer.",[251,252,255,390],"figure",{"className":253},[254],"diagram",[256,257,264,265,264,269,264,273,264,291,264,299,264,308,264,317,264,323,264,326,264,330,264,338,264,342,264,351,264,357,264,362,264,367,264,371,264,374,264,378,264,385],"svg",{"viewBox":258,"role":259,"ariaLabelledBy":260,"xmlns":263},"0 0 820 262","img",[261,262],"ifm-t","ifm-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","\n  ",[266,267,268],"title",{"id":261},"How two files become one module name",[270,271,272],"desc",{"id":262},"Two test files named test_invoice.py sit in sibling directories with no init files. Under prepend mode each directory is inserted into sys.path and each file is imported as the top-level module test_invoice. The second import finds the name already bound to the first file and pytest raises import file mismatch rather than run the wrong code.",[274,275,276,277,264],"defs",{},"\n    ",[278,279,286],"marker",{"id":280,"viewBox":281,"refX":282,"refY":283,"markerWidth":284,"markerHeight":284,"orient":285},"ifm-a","0 0 10 10","9","5","7","auto-start-reverse",[287,288],"path",{"d":289,"fill":290},"M0 0 L10 5 L0 10 z","#e07a5f",[292,293],"rect",{"x":294,"y":294,"width":295,"height":296,"rx":297,"fill":298},"0","820","262","14","#fffdf8",[171,300,307],{"x":301,"y":302,"textAnchor":303,"fontSize":304,"fontWeight":305,"fill":306},"410","28","middle","16","700","#3d405b","Two paths, one name, one refusal",[292,309],{"x":310,"y":311,"width":312,"height":313,"rx":314,"fill":315,"stroke":306,"strokeWidth":316},"34","60","260","46","10","#f4f1de","1.6",[171,318,322],{"x":319,"y":320,"textAnchor":303,"fontSize":321,"fill":306},"164","88","11.5","tests\u002Fbilling\u002Ftest_invoice.py",[292,324],{"x":310,"y":325,"width":312,"height":313,"rx":314,"fill":315,"stroke":306,"strokeWidth":316},"150",[171,327,329],{"x":319,"y":328,"textAnchor":303,"fontSize":321,"fill":306},"178","tests\u002Fapi\u002Ftest_invoice.py",[71,331],{"x1":332,"y1":333,"x2":334,"y2":335,"stroke":290,"strokeWidth":336,"markerEnd":337},"298","83","400","118","1.8","url(#ifm-a)",[71,339],{"x1":332,"y1":340,"x2":334,"y2":341,"stroke":290,"strokeWidth":336,"markerEnd":337},"173","138",[292,343],{"x":344,"y":345,"width":346,"height":347,"rx":348,"fill":349,"stroke":290,"strokeWidth":350},"406","100","190","56","11","#fbe9e3","2",[171,352,356],{"x":353,"y":354,"textAnchor":303,"fontSize":355,"fontWeight":305,"fill":306},"501","124","12","sys.modules",[171,358,361],{"x":353,"y":359,"textAnchor":303,"fontSize":348,"fill":360},"143","#8f3d22","\"test_invoice\"",[292,363],{"x":364,"y":365,"width":328,"height":366,"rx":348,"fill":298,"stroke":290,"strokeWidth":336},"616","92","72",[171,368,370],{"x":369,"y":335,"textAnchor":303,"fontSize":321,"fontWeight":305,"fill":306},"705","second import:",[171,372,373],{"x":369,"y":341,"textAnchor":303,"fontSize":348,"fill":360},"name taken by a",[171,375,377],{"x":369,"y":376,"textAnchor":303,"fontSize":348,"fill":360},"154","different file",[292,379],{"x":310,"y":380,"width":381,"height":382,"rx":282,"fill":298,"stroke":383,"strokeWidth":384},"214","760","32","rgba(61,64,91,0.35)","1.4",[171,386,389],{"x":387,"y":388,"textAnchor":303,"fontSize":321,"fill":306},"414","235","pytest stops rather than silently running the first file's tests twice.",[391,392,393],"figcaption",{},"The error is protective. Without it, the second file's tests would never run and the first file's would be reported twice.",[22,395,397],{"id":396},"why-this-works","Why this works",[10,399,400,401,403,404,407,408,411,412,415,416,418,419,421,422,424],{},"Under the default ",[14,402,232],{}," import mode, pytest walks up from each test file for as long as it finds ",[14,405,406],{},"__init__.py",", stops at the first directory without one, inserts that directory at the front of ",[14,409,410],{},"sys.path",", and imports the file under the dotted name formed from its path relative to that directory. Two files named ",[14,413,414],{},"test_invoice.py"," in directories with no ",[14,417,406],{}," both resolve to the bare name ",[14,420,236],{},". The first is imported and cached in ",[14,423,356],{},"; the second finds the name already taken by a different file, and pytest raises rather than reuse the wrong module.",[10,426,427,429,430,432],{},[14,428,38],{}," does not consult ",[14,431,410],{}," or derive names from the directory structure. It imports each file directly by location and gives it a unique name, so two files with the same basename are simply two modules. That is why it removes the error class entirely rather than working around one instance of it.",[22,434,436],{"id":435},"edge-cases-and-failure-modes","Edge cases and failure modes",[27,438,439,461,475,500,519],{},[30,440,441,445,446,449,450,453,454,456,457,460],{},[442,443,444],"strong",{},"Tests that import each other by bare name."," Under ",[14,447,448],{},"importlib",", ",[14,451,452],{},"from test_helpers import make_order"," no longer works, because the test directory is not on ",[14,455,410],{},". Move shared helpers into a proper module under a ",[14,458,459],{},"conftest.py"," or an installed test-support package.",[30,462,463,469,470,474],{},[442,464,465,466,468],{},"A partial set of ",[14,467,406],{}," files."," Adding them to some directories but not others changes where the walk stops, and can produce a ",[471,472,473],"em",{},"new"," mismatch between files that previously had different names. It is all or nothing.",[30,476,477,484,485,237,488,491,492,495,496,499],{},[442,478,479,480,483],{},"Stale ",[14,481,482],{},".pyc"," after a move."," A test file renamed from ",[14,486,487],{},"test_a.py",[14,489,490],{},"test_b.py"," can leave a compiled ",[14,493,494],{},"test_a"," behind that claims the old name. Clearing ",[14,497,498],{},"__pycache__"," rules it out in a second.",[30,501,502,510,511,513,514,516,517,51],{},[442,503,504,505,241,508,51],{},"The same basename in ",[14,506,507],{},"src",[14,509,248],{}," A test module named like an application module can shadow it under ",[14,512,232],{},". Another reason to prefer a ",[14,515,507],{}," layout and ",[14,518,448],{},[30,520,521,524,525,528],{},[442,522,523],{},"IDE runners with their own settings."," An IDE that passes its own ",[14,526,527],{},"--import-mode"," produces different behaviour from the command line. Put the mode in configuration so every runner inherits it.",[22,530,532],{"id":531},"reading-the-error-message-precisely","Reading the error message precisely",[10,534,535],{},"The full message carries more information than its first line suggests, and reading all of it usually identifies both files without any searching.",[60,537,540],{"className":538,"code":539,"language":171,"meta":65},[169],"import file mismatch:\nimported module 'test_invoice' has this __file__ attribute:\n  \u002Frepo\u002Ftests\u002Fbilling\u002Ftest_invoice.py\nwhich is not the same as the test file we want to collect:\n  \u002Frepo\u002Ftests\u002Fapi\u002Ftest_invoice.py\nHINT: remove __pycache__ \u002F .pyc files and\u002For use a unique basename for your test file modules\n",[14,541,539],{"__ignoreMap":65},[10,543,544,545,547],{},"The first path is the file that won — the one imported earlier and cached under the bare module name. The second is the one pytest was trying to collect when it noticed. The module name in quotes is the collision itself. If the two paths are the same file at different locations — the old and new home of a moved test — the cause is stale bytecode and clearing ",[14,546,498],{}," is the whole fix. If they are genuinely different files, the cause is layout, and the import mode is the fix.",[251,549,551,624],{"className":550},[254],[256,552,264,557,264,560,264,563,264,567,264,571,264,579,264,585,264,590,264,594,264,598,264,602,264,606,264,610,264,614,264,617,264,621],{"viewBox":553,"role":259,"ariaLabelledBy":554,"xmlns":263},"0 0 800 228",[555,556],"rd2-t","rd2-d",[266,558,559],{"id":555},"Triage from the two paths in the error",[270,561,562],{"id":556},"A decision based on the two paths the error prints. If both paths refer to the same logical test at an old and a new location, stale bytecode is the cause and clearing pycache fixes it. If they are two different files with the same basename, the layout is the cause and switching import mode fixes it.",[292,564],{"x":294,"y":294,"width":565,"height":566,"rx":297,"fill":298},"800","228",[171,568,570],{"x":334,"y":302,"textAnchor":303,"fontSize":569,"fontWeight":305,"fill":306},"15.5","The two paths tell you which fix you need",[292,572],{"x":573,"y":574,"width":575,"height":576,"rx":355,"fill":577,"stroke":578,"strokeWidth":350},"26","50","360","156","#f7f0da","#f2cc8f",[171,580,584],{"x":581,"y":582,"textAnchor":303,"fontSize":583,"fontWeight":305,"fill":306},"206","76","12.5","same test, old and new home",[171,586,589],{"x":587,"y":588,"fontSize":348,"fill":306},"44","106","a file was moved or renamed",[171,591,593],{"x":587,"y":592,"fontSize":348,"fill":306},"128","and its old .pyc survived",[171,595,597],{"x":587,"y":319,"fontSize":348,"fontWeight":305,"fill":596},"#8a5a00","fix: clear __pycache__",[171,599,601],{"x":587,"y":600,"fontSize":348,"fill":306},"186","nothing else changes",[292,603],{"x":387,"y":574,"width":575,"height":576,"rx":355,"fill":604,"stroke":605,"strokeWidth":350},"#e6f0ea","#81b29a",[171,607,609],{"x":608,"y":582,"textAnchor":303,"fontSize":583,"fontWeight":305,"fill":306},"594","two different files",[171,611,613],{"x":612,"y":588,"fontSize":348,"fill":306},"432","same basename in plain",[171,615,616],{"x":612,"y":592,"fontSize":348,"fill":306},"directories, prepend mode",[171,618,620],{"x":612,"y":319,"fontSize":348,"fontWeight":305,"fill":619},"#2a5f49","fix: --import-mode=importlib",[171,622,623],{"x":612,"y":600,"fontSize":348,"fill":306},"removes the error class",[391,625,626],{},"Checking which case applies takes seconds and avoids restructuring a test tree to fix what was only a stale compiled file.",[10,628,629,630,633,634,636],{},"CI runners that restore a cached virtual environment or workspace can carry stale bytecode between builds, which is why this error sometimes appears only in CI after a file move and never locally. Setting ",[14,631,632],{},"PYTHONDONTWRITEBYTECODE=1"," in the CI environment, or excluding ",[14,635,498],{}," from the cache key's restored paths, removes that source permanently.",[22,638,640],{"id":639},"choosing-between-the-two-fixes","Choosing between the two fixes",[10,642,643],{},"Both fixes make the error go away, and they differ in what they leave behind.",[10,645,646,647,649,650,652,653,655],{},"The ",[14,648,406],{}," route keeps the default import mode, which some older plugins and some cross-test imports depend on. Its cost is structural: the test tree becomes a package, every new directory must remember to include the marker file, and the package name ",[14,651,248],{}," is claimed in the import namespace. Forgetting one ",[14,654,406],{}," in a new directory is enough to reintroduce the error months later, typically for whoever adds the next duplicate basename.",[10,657,646,658,660,661,663],{},[14,659,448],{}," route changes one configuration line and removes the marker files. Its cost is behavioural: test modules can no longer import one another by bare name, and a very small number of plugins that assume ",[14,662,232],{}," semantics need updating. For a new or medium-sized suite that cost is nearly zero; for a large legacy suite it is a few hours of moving shared helpers into proper modules, which is an improvement in its own right.",[251,665,667,726],{"className":666},[254],[256,668,264,673,264,676,264,679,264,682,264,685,264,688,264,691,264,694,264,697,264,700,264,704,264,707,264,709,264,711,264,714,264,717,264,720,264,723],{"viewBox":669,"role":259,"ariaLabelledBy":670,"xmlns":263},"0 0 800 244",[671,672],"fx-t","fx-d",[266,674,675],{"id":671},"Comparing the two fixes",[270,677,678],{"id":672},"Two columns. Adding init files keeps prepend mode and cross-test imports but turns the test tree into a package that every new directory must maintain. Switching to importlib mode removes the marker files and the collision class entirely, at the cost of test modules no longer importing each other by bare name.",[292,680],{"x":294,"y":294,"width":565,"height":681,"rx":297,"fill":298},"244",[171,683,684],{"x":334,"y":302,"textAnchor":303,"fontSize":569,"fontWeight":305,"fill":306},"Both fix today's error; only one prevents the next",[292,686],{"x":573,"y":574,"width":575,"height":687,"rx":355,"fill":577,"stroke":578,"strokeWidth":350},"176",[171,689,690],{"x":581,"y":582,"textAnchor":303,"fontSize":583,"fontWeight":305,"fill":306},"add __init__.py everywhere",[171,692,693],{"x":587,"y":588,"fontSize":348,"fill":619},"✓ keeps prepend semantics",[171,695,696],{"x":587,"y":592,"fontSize":348,"fill":619},"✓ cross-test imports still work",[171,698,699],{"x":587,"y":325,"fontSize":348,"fill":596},"✗ tree becomes package \"tests\"",[171,701,703],{"x":587,"y":702,"fontSize":348,"fill":596},"172","✗ every new dir must remember",[171,705,706],{"x":587,"y":581,"fontSize":348,"fill":306},"the error returns when one is missed",[292,708],{"x":387,"y":574,"width":575,"height":687,"rx":355,"fill":604,"stroke":605,"strokeWidth":350},[171,710,38],{"x":608,"y":582,"textAnchor":303,"fontSize":583,"fontWeight":305,"fill":306},[171,712,713],{"x":612,"y":588,"fontSize":348,"fill":619},"✓ no module-name collisions",[171,715,716],{"x":612,"y":592,"fontSize":348,"fill":619},"✓ no marker files to maintain",[171,718,719],{"x":612,"y":325,"fontSize":348,"fill":619},"✓ tests stay out of the namespace",[171,721,722],{"x":612,"y":702,"fontSize":348,"fill":596},"✗ no bare-name cross-test imports",[171,724,725],{"x":612,"y":581,"fontSize":348,"fill":306},"the error class is gone for good",[391,727,728],{},"The right-hand column's one cost is also a small improvement: shared helpers end up in a real module rather than in another test file.",[22,730,732],{"id":731},"confirming-the-layout-is-sound","Confirming the layout is sound",[10,734,735],{},"After either fix, two checks confirm that the suite now collects the same tests however it is invoked.",[10,737,738,739,742],{},"Run collection from the repository root and from inside a test subdirectory, and compare the results. With ",[14,740,741],{},"testpaths"," and the import mode in configuration, they should be identical; if they differ, some part of the behaviour still depends on the current directory, and the next person to run a single file from their editor will see a different suite from CI.",[10,744,745,746,748],{},"Then add a new test file whose basename duplicates an existing one, run the suite, and delete it again. Under ",[14,747,448],{}," it should simply collect. This takes thirty seconds and proves the fix addresses the class of problem rather than the instance that prompted it — which matters because the next duplicate basename will be added by someone who has never heard of this error and would otherwise spend an hour rediscovering it. Recording the chosen import mode and the reason in a comment beside the configuration completes the job, so that nobody later removes it as an unexplained setting.",[22,750,752],{"id":751},"frequently-asked-questions","Frequently Asked Questions",[10,754,755,762,763,765,766,768,769,772],{},[442,756,757,758,761],{},"Why does the error mention ",[442,759,760],{},"pycache","?","\nBecause a compiled file from a previous location can also trigger the check. If a test file was moved or renamed, a stale ",[14,764,482],{}," may still claim the old module name. Deleting ",[14,767,498],{}," directories, or running with ",[14,770,771],{},"-p no:cacheprovider"," once, rules this out before investigating the layout.",[10,774,775,782,783,785,786,788,789,51],{},[442,776,777,778,781],{},"Is adding ",[442,779,780],{},"init",".py everywhere a real fix?","\nIt is a working fix under the default ",[14,784,232],{}," mode, because it makes module names unique by turning the test tree into packages. The cleaner fix is ",[14,787,38],{},", which removes the need for those files entirely and avoids the test tree becoming an importable package called ",[14,790,248],{},[10,792,793,796,797,799,800,802,803,805,806,51],{},[442,794,795],{},"Can two test files ever safely share a basename?","\nYes, under ",[14,798,448],{}," mode, or under ",[14,801,232],{}," mode when both live inside packages so their dotted names differ. What cannot work is two same-named files in plain directories under ",[14,804,232],{}," mode, because both become the top-level module ",[14,807,808],{},"test_name",[22,810,812],{"id":811},"related","Related",[27,814,815,821,831,838],{},[30,816,817,820],{},[47,818,819],{"href":49},"Test Layout & Import Modes"," — the full model behind this error.",[30,822,823,830],{},[47,824,826,827,829],{"href":825},"\u002Fadvanced-pytest-architecture-configuration\u002Ftest-layout-and-import-modes\u002Fusing-importmode-importlib-without-init-files\u002F","Using importmode=importlib Without ",[442,828,780],{},".py Files"," — making the durable fix work across a whole suite.",[30,832,833,837],{},[47,834,836],{"href":835},"\u002Fadvanced-pytest-architecture-configuration\u002Fmanaging-conftest-hierarchies\u002Fcreating-conftestpy-hierarchies-for-monorepos\u002F","Creating conftest.py Hierarchies for Monorepos"," — where duplicate basenames are most common.",[30,839,840,844],{},[47,841,843],{"href":842},"\u002Fadvanced-pytest-architecture-configuration\u002Foptimizing-test-discovery\u002Fcutting-collection-time-with-norecursedirs\u002F","Cutting Collection Time with norecursedirs"," — keeping collection to the directories you mean.",[10,846,847,848],{},"← Back to ",[47,849,819],{"href":49},[851,852,853],"style",{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":65,"searchDepth":79,"depth":79,"links":855},[856,857,858,859,860,861,862,863,864],{"id":24,"depth":79,"text":25},{"id":54,"depth":79,"text":55},{"id":396,"depth":79,"text":397},{"id":435,"depth":79,"text":436},{"id":531,"depth":79,"text":532},{"id":639,"depth":79,"text":640},{"id":731,"depth":79,"text":732},{"id":751,"depth":79,"text":752},{"id":811,"depth":79,"text":812},"Diagnose and fix pytest's 'import file mismatch' error: duplicate basenames, prepend import mode, stale __pycache__, __init__.py placement and switching to importlib.","md",{"slug":868,"type":869,"breadcrumb":870,"datePublished":871,"dateModified":871,"faq":872,"howto":881},"fixing-import-file-mismatch-errors-in-pytest","article","import file mismatch","2026-09-18",[873,876,879],{"q":874,"a":875},"Why does the error mention __pycache__?","Because a compiled file from a previous location can also trigger the check. If a test file was moved or renamed, a stale .pyc may still claim the old module name. Deleting __pycache__ directories, or running with -p no:cacheprovider once, rules this out before investigating the layout.",{"q":877,"a":878},"Is adding __init__.py everywhere a real fix?","It is a working fix under the default prepend mode, because it makes module names unique by turning the test tree into packages. The cleaner fix is --import-mode=importlib, which removes the need for those files entirely and avoids the test tree becoming an importable package called tests.",{"q":795,"a":880},"Yes, under importlib mode, or under prepend mode when both live inside packages so their dotted names differ. What cannot work is two same-named files in plain directories under prepend mode, because both become the top-level module test_name.",{"name":882,"description":883,"steps":884},"How to fix pytest's import file mismatch error","Rule out stale bytecode, find the duplicate basename, and remove the collision by changing the import mode or the packaging.",[885,888,891,894,897],{"name":886,"text":887},"Clear stale bytecode","Delete __pycache__ directories so a moved file's old .pyc cannot claim the module name.",{"name":889,"text":890},"Find the duplicate basenames","List test files whose basenames appear more than once in non-package directories.",{"name":892,"text":893},"Switch to importlib mode","Set --import-mode=importlib in the configuration so files are imported by path with unique names.",{"name":895,"text":896},"Or make the directories packages","Under prepend mode, add __init__.py to every directory from the tests root down so dotted names differ.",{"name":898,"text":899},"Verify collection from two directories","Run the suite from the repository root and from a subdirectory and confirm both collect cleanly.","\u002Fadvanced-pytest-architecture-configuration\u002Ftest-layout-and-import-modes\u002Ffixing-import-file-mismatch-errors-in-pytest",{"title":5,"description":865},"advanced-pytest-architecture-configuration\u002Ftest-layout-and-import-modes\u002Ffixing-import-file-mismatch-errors-in-pytest\u002Findex","krq-iDqbUgeJJck_gZD1ykhC5J1_LJRnQABLA196ZfI",1789718768804]