{"id":"d199c507493fb2c0","repo":"pytest-dev/pytest","slug":"pytester-makefile-expects-a-file-extension-try","errorCode":null,"errorMessage":"pytester.makefile expects a file extension, try .{ext} instead of {ext}","messagePattern":"pytester\\.makefile expects a file extension, try \\.(.+?) instead of (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/_pytest/pytester.py","lineNumber":772,"sourceCode":"\n        This is done automatically upon instantiation.\n        \"\"\"\n        self._monkeypatch.chdir(self.path)\n\n    def _makefile(\n        self,\n        ext: str,\n        lines: Sequence[Any | bytes],\n        files: Mapping[str, _FileContent],\n        encoding: str = \"utf-8\",\n    ) -> Path:\n        items = list(files.items())\n\n        if ext is None:\n            raise TypeError(\"ext must not be None\")\n\n        if ext and not ext.startswith(\".\"):\n            raise ValueError(\n                f\"pytester.makefile expects a file extension, try .{ext} instead of {ext}\"\n            )\n\n        def to_text(s: Any | bytes) -> str:\n            return s.decode(encoding) if isinstance(s, bytes) else str(s)\n\n        if lines:\n            source = \"\\n\".join(to_text(x) for x in lines)\n            basename = self._name\n            items.insert(0, (basename, source))\n\n        ret = None\n        for basename, value in items:\n            p = self.path.joinpath(basename).with_suffix(ext)\n            p.parent.mkdir(parents=True, exist_ok=True)\n            source_ = Source(value)\n            source = \"\\n\".join(to_text(line) for line in source_.lines)\n            p.write_text(source.strip(), encoding=encoding)","sourceCodeStart":754,"sourceCodeEnd":790,"githubUrl":"https://github.com/pytest-dev/pytest/blob/98b357f69e380da908740a212288d73b2ee06687/src/_pytest/pytester.py#L754-L790","documentation":"Raised by Pytester._makefile when ext is a non-empty string that does not begin with a dot. pytester.makefile expects the leading dot (e.g. '.py', '.ini') because it is passed verbatim to Path.with_suffix. The message suggests the corrected form '.{ext}' to guide the caller. An empty string ext is allowed (it means 'use the basename as-is').","triggerScenarios":"Calling pytester.makefile('py', ...) or makefile('ini', ...) without the leading dot; constructing ext dynamically and stripping the dot; misreading the docstring.","commonSituations":"Copy-pasting code that uses os.path.splitext semantics (which returns the extension without the dot); passing a value from config that omits the dot; new users unfamiliar with the leading-dot convention.","solutions":["Add the leading dot: pass '.py' instead of 'py'.","Normalize ext before calling: ext = ext if ext.startswith('.') else '.' + ext.","Use the dedicated helpers (makepyfile, makeini, maketoml) which handle the extension for you."],"exampleFix":"// before\npytester.makefile(\"txt\", data=\"hello\")\n// after\npytester.makefile(\".txt\", data=\"hello\")","handlingStrategy":"validation","validationCode":"def norm_ext(ext: str) -> str:\n    if ext and not ext.startswith(\".\"):\n        ext = \".\" + ext\n    return ext\n\npytester.makefile(norm_ext(ext), data=source)","typeGuard":"def is_dotted_ext(ext: object) -> bool:\n    return isinstance(ext, str) and (ext == \"\" or ext.startswith(\".\"))","tryCatchPattern":null,"preventionTips":["Always include the leading dot in extensions.","Use makepyfile/makeini/maketoml helpers to avoid manual extension handling."],"tags":["pytester","file-creation","validation"],"analyzedSha":"98b357f69e380da908740a212288d73b2ee06687","analyzedAt":"2026-08-04T20:26:34.442Z","schemaVersion":2}