From 453b2fd6a0cec26bc1447fe3b01669d762857ab5 Mon Sep 17 00:00:00 2001 From: ZaharChernenko Date: Sun, 22 Jun 2025 19:47:09 +0300 Subject: [PATCH 1/2] changed: file_type deduction --- python_coderunner/pyproject.toml | 1 + .../vim_coderunner_builder.py | 12 +- .../src/file_info_extractor/__init__.py | 2 +- .../src/file_info_extractor/basic.py | 963 ------------------ .../fetch_linguist_extensions.py | 32 - .../src/file_info_extractor/interface.py | 46 +- .../vim_file_info_extractor.py | 11 + .../unit/command_builder/test_interpolator.py | 42 - .../test_interpolator_command_builder.py | 37 + ...st_file_ext_command_builders_dispatcher.py | 6 +- ...t_file_type_command_builders_dispatcher.py | 14 +- ...ic_command_dispatcher_strategy_selector.py | 2 +- python_coderunner/tests/unit/conftest.py | 131 ++- .../test_interface_file_info_extractor.py | 182 ++-- .../test_interface_project_info_extractor.py | 100 +- python_coderunner/uv.lock | 14 + 16 files changed, 299 insertions(+), 1296 deletions(-) delete mode 100644 python_coderunner/src/file_info_extractor/basic.py delete mode 100644 python_coderunner/src/file_info_extractor/fetch_linguist_extensions.py create mode 100644 python_coderunner/src/file_info_extractor/vim_file_info_extractor.py delete mode 100644 python_coderunner/tests/unit/command_builder/test_interpolator.py create mode 100644 python_coderunner/tests/unit/command_builder/test_interpolator_command_builder.py diff --git a/python_coderunner/pyproject.toml b/python_coderunner/pyproject.toml index c0b3a29..5f96eff 100644 --- a/python_coderunner/pyproject.toml +++ b/python_coderunner/pyproject.toml @@ -8,6 +8,7 @@ dependencies = [ "pre-commit>=1.21.0", "pylint>=2.6.2", "pytest>=6.1.2", + "pytest-lazy-fixture>=0.6.3", "pytest-mock>=3.5.1", "pyyaml>=5.4.1", "requests>=2.27.1", diff --git a/python_coderunner/src/coderunner_builder/vim_coderunner_builder.py b/python_coderunner/src/coderunner_builder/vim_coderunner_builder.py index f7631bf..5ac08a7 100644 --- a/python_coderunner/src/coderunner_builder/vim_coderunner_builder.py +++ b/python_coderunner/src/coderunner_builder/vim_coderunner_builder.py @@ -22,7 +22,7 @@ ) from src.editor import TVimEditor from src.editor_service_for_coderunner import TBasicEditorServiceForCodeRunner -from src.file_info_extractor import TBasicFileInfoExtractor +from src.file_info_extractor import TVimFileInfoExtractor from src.message_printer import TVimMessagePrinter from src.project_info_extractor import TVimProjectInfoExtractor @@ -33,7 +33,7 @@ def build(self) -> Optional[TCodeRunner]: message_printer: TVimMessagePrinter = TVimMessagePrinter() try: - file_info_extractor: TBasicFileInfoExtractor = TBasicFileInfoExtractor() + file_info_extractor: TVimFileInfoExtractor = TVimFileInfoExtractor() project_info_extractor: TVimProjectInfoExtractor = TVimProjectInfoExtractor(file_info_extractor) editor: TVimEditor = TVimEditor() @@ -64,7 +64,7 @@ def build(self) -> Optional[TCodeRunner]: def _build_command_dispatcher_strategy_selector( self, config_manager: TVimConfigManager, - file_info_extractor: TBasicFileInfoExtractor, + file_info_extractor: TVimFileInfoExtractor, project_info_extractor: TVimProjectInfoExtractor, ) -> TBasicCommandDispatcherStrategySelector: shebang_command_builders_dispatcher: TShebangCommandBuildersDispatcher = TShebangCommandBuildersDispatcher( @@ -95,7 +95,7 @@ def _build_command_dispatcher_strategy_selector( def _build_file_ext_command_builders_dispatcher( self, config_manager: TVimConfigManager, - file_info_extractor: TBasicFileInfoExtractor, + file_info_extractor: TVimFileInfoExtractor, project_info_extractor: TVimProjectInfoExtractor, ) -> TFileExtCommandBuildersDispatcher: return TFileExtCommandBuildersDispatcher( @@ -109,7 +109,7 @@ def _build_file_ext_command_builders_dispatcher( def _build_file_type_command_builders_dispatcher( self, config_manager: TVimConfigManager, - file_info_extractor: TBasicFileInfoExtractor, + file_info_extractor: TVimFileInfoExtractor, project_info_extractor: TVimProjectInfoExtractor, ) -> TFileTypeCommandBuildersDispatcher: return TFileTypeCommandBuildersDispatcher( @@ -123,7 +123,7 @@ def _build_file_type_command_builders_dispatcher( def _build_glob_command_builders_dispatcher( self, config_manager: TVimConfigManager, - file_info_extractor: TBasicFileInfoExtractor, + file_info_extractor: TVimFileInfoExtractor, project_info_extractor: TVimProjectInfoExtractor, ) -> TGlobCommandBuildersDispatcher: dict_with_commands: Dict[str, str] = config_manager.get_by_glob() diff --git a/python_coderunner/src/file_info_extractor/__init__.py b/python_coderunner/src/file_info_extractor/__init__.py index e5e0918..7327f81 100644 --- a/python_coderunner/src/file_info_extractor/__init__.py +++ b/python_coderunner/src/file_info_extractor/__init__.py @@ -1,2 +1,2 @@ -from src.file_info_extractor.basic import TBasicFileInfoExtractor from src.file_info_extractor.interface import IFileInfoExtractor +from src.file_info_extractor.vim_file_info_extractor import TVimFileInfoExtractor diff --git a/python_coderunner/src/file_info_extractor/basic.py b/python_coderunner/src/file_info_extractor/basic.py deleted file mode 100644 index fd2252a..0000000 --- a/python_coderunner/src/file_info_extractor/basic.py +++ /dev/null @@ -1,963 +0,0 @@ -import os -from typing import Dict, Final, Optional - -from src.file_info_extractor.interface import IFileInfoExtractor - - -class TBasicFileInfoExtractor(IFileInfoExtractor): - def get_dir(self, file_path_abs: str) -> str: - dir_path: str = os.path.dirname(file_path_abs) - if dir_path and not dir_path.endswith(os.sep): - dir_path += os.sep - return dir_path - - def get_dir_without_trailing_slash(self, file_path_abs: str) -> str: - return os.path.dirname(file_path_abs).rstrip(os.sep) - - def get_file_name(self, file_path_abs: str) -> str: - return os.path.basename(file_path_abs) - - def get_file_name_without_ext(self, file_path_abs: str) -> str: - base: str = self.get_file_name(file_path_abs) - dot_pos: int = base.find(".") - return base[:dot_pos] if dot_pos != -1 else base - - def get_file_ext(self, file_path_abs: str) -> str: - base: str = self.get_file_name(file_path_abs) - dot_pos: int = base.find(".") - return (base[dot_pos : len(base)] if dot_pos != -1 else "").lower() - - def get_file_type(self, file_path_abs: str) -> Optional[str]: - return self.EXT_TO_LANG.get(self.get_file_ext(file_path_abs)) - - def get_drive_letter(self, file_path_abs: str) -> str: - drive, _ = os.path.splitdrive(file_path_abs) - return drive - - def get_shebang(self, file_path_abs: str) -> Optional[str]: - if not os.path.exists(file_path_abs): - return None - - try: - with open(file_path_abs, "r", encoding="utf-8") as fin: - first_line: str = fin.readline().strip() - return first_line[2:] if first_line.startswith("#!") and not first_line.startswith("#![") else None - except (IOError, UnicodeDecodeError): - return None - - EXT_TO_LANG: Final[Dict[str, str]] = { - ".4dm": "4D", - ".4gl": "Genero 4gl", - ".4th": "Forth", - ".6pl": "Raku", - ".6pm": "Raku", - ".8xp": "TI Program", - ".8xp.txt": "TI Program", - "._coffee": "CoffeeScript", - "._js": "JavaScript", - "._ls": "LiveScript", - ".a51": "Assembly", - ".abap": "ABAP", - ".ada": "Ada", - ".adb": "Ada", - ".ado": "Stata", - ".adp": "Tcl", - ".ads": "Ada", - ".agc": "Apollo Guidance Computer", - ".agda": "Agda", - ".ahk": "AutoHotkey", - ".ahkl": "AutoHotkey", - ".aidl": "AIDL", - ".aj": "AspectJ", - ".al": "Perl", - ".als": "Alloy", - ".ampl": "AMPL", - ".angelscript": "AngelScript", - ".apex": "Apex", - ".apl": "APL", - ".app": "Erlang", - ".app.src": "Erlang", - ".applescript": "AppleScript", - ".arc": "Arc", - ".arr": "Pyret", - ".as": "AngelScript", - ".asax": "ASP.NET", - ".asc": "AGS Script", - ".ascx": "ASP.NET", - ".asd": "Common Lisp", - ".asddls": "ABAP CDS", - ".ash": "AGS Script", - ".ashx": "ASP.NET", - ".asl": "ASL", - ".asm": "Motorola 68K Assembly", - ".asmx": "ASP.NET", - ".asp": "Classic ASP", - ".aspx": "ASP.NET", - ".asy": "Asymptote", - ".au3": "AutoIt", - ".aug": "Augeas", - ".auk": "Awk", - ".aw": "PHP", - ".awk": "Awk", - ".axd": "ASP.NET", - ".axi": "NetLinx", - ".axi.erb": "NetLinx+ERB", - ".axs": "NetLinx", - ".axs.erb": "NetLinx+ERB", - ".b": "Limbo", - ".bal": "Ballerina", - ".bas": "Visual Basic 6.0", - ".bash": "Shell", - ".bat": "Batchfile", - ".bats": "Shell", - ".bb": "Clojure", - ".bbappend": "BitBake", - ".bbclass": "BitBake", - ".bdy": "PLSQL", - ".be": "Berry", - ".befunge": "Befunge", - ".bf": "HyPhy", - ".bi": "FreeBASIC", - ".bicep": "Bicep", - ".bicepparam": "Bicep", - ".bison": "Bison", - ".bmx": "BlitzMax", - ".bones": "JavaScript", - ".boo": "Boo", - ".boot": "Clojure", - ".bpl": "Boogie", - ".bqn": "BQN", - ".bro": "Zeek", - ".brs": "Brightscript", - ".bs": "BrighterScript", - ".bsl": "1C Enterprise", - ".bst": "BibTeX Style", - ".bsv": "Bluespec", - ".builder": "Ruby", - ".bzl": "Starlark", - ".c": "C", - ".c++": "C++", - ".cairo": "Cairo Zero", - ".cake": "CoffeeScript", - ".capnp": "Cap'n Proto", - ".carbon": "Carbon", - ".cats": "C", - ".cbl": "COBOL", - ".cc": "C++", - ".ccp": "COBOL", - ".cdc": "Cadence", - ".cdf": "Mathematica", - ".cds": "CAP CDS", - ".ceylon": "Ceylon", - ".cfc": "ColdFusion CFC", - ".cfm": "ColdFusion", - ".cfml": "ColdFusion", - ".cgi": "Shell", - ".cginc": "HLSL", - ".ch": "xBase", - ".chpl": "Chapel", - ".chs": "C2hs Haskell", - ".circom": "Circom", - ".cirru": "Cirru", - ".cjs": "JavaScript", - ".cjsx": "CoffeeScript", - ".ck": "ChucK", - ".cl": "OpenCL", - ".cl2": "Clojure", - ".clar": "Clarity", - ".click": "Click", - ".clj": "Clojure", - ".cljc": "Clojure", - ".cljs": "Clojure", - ".cljs.hl": "Clojure", - ".cljscm": "Clojure", - ".cljx": "Clojure", - ".clp": "CLIPS", - ".cls": "Visual Basic 6.0", - ".clw": "Clarion", - ".cmake": "CMake", - ".cmake.in": "CMake", - ".cmd": "Batchfile", - ".cnc": "G-code", - ".cob": "COBOL", - ".cobol": "COBOL", - ".cocci": "SmPL", - ".coffee": "CoffeeScript", - ".coffee.md": "Literate CoffeeScript", - ".com": "DIGITAL Command Language", - ".command": "Shell", - ".containerfile": "Dockerfile", - ".coq": "Rocq Prover", - ".cp": "Component Pascal", - ".cpp": "C++", - ".cppm": "C++", - ".cps": "Component Pascal", - ".cpy": "COBOL", - ".cr": "Crystal", - ".cs": "Smalltalk", - ".cs.pp": "C#", - ".csc": "GSC", - ".csd": "Csound Document", - ".csh": "Tcsh", - ".csx": "C#", - ".ctl": "Visual Basic 6.0", - ".ctp": "PHP", - ".cts": "TypeScript", - ".cu": "Cuda", - ".cue": "CUE", - ".cuh": "Cuda", - ".curry": "Curry", - ".cw": "Redcode", - ".cwl": "Common Workflow Language", - ".cxx": "C++", - ".cy": "Cycript", - ".cyp": "Cypher", - ".cypher": "Cypher", - ".d": "Makefile", - ".dart": "Dart", - ".dats": "ATS", - ".db2": "SQLPL", - ".dcl": "Clean", - ".ddl": "PLSQL", - ".decls": "BlitzBasic", - ".dfm": "Pascal", - ".dfy": "Dafny", - ".dhall": "Dhall", - ".di": "D", - ".djs": "Dogescript", - ".dlm": "IDL", - ".dm": "DM", - ".do": "Stata", - ".dockerfile": "Dockerfile", - ".doh": "Stata", - ".dpr": "Pascal", - ".druby": "Mirah", - ".dsc": "DenizenScript", - ".dsl": "ASL", - ".dsp": "Faust", - ".dsr": "Visual Basic 6.0", - ".duby": "Mirah", - ".dwl": "DataWeave", - ".dyalog": "APL", - ".dyl": "Dylan", - ".dylan": "Dylan", - ".e": "Euphoria", - ".ebuild": "Gentoo Ebuild", - ".ec": "eC", - ".ecl": "ECLiPSe", - ".eclass": "Gentoo Eclass", - ".eclxml": "ECL", - ".edgeql": "EdgeQL", - ".eh": "eC", - ".el": "Emacs Lisp", - ".eliom": "OCaml", - ".eliomi": "OCaml", - ".elm": "Elm", - ".elv": "Elvish", - ".em": "EmberScript", - ".emacs": "Emacs Lisp", - ".emacs.desktop": "Emacs Lisp", - ".emberscript": "EmberScript", - ".eq": "EQ", - ".erl": "Erlang", - ".es": "JavaScript", - ".es6": "JavaScript", - ".escript": "Erlang", - ".esdl": "EdgeQL", - ".ex": "Euphoria", - ".exs": "Elixir", - ".eye": "Ruby", - ".f": "Fortran", - ".f03": "Fortran Free Form", - ".f08": "Fortran Free Form", - ".f77": "Fortran", - ".f90": "Fortran Free Form", - ".f95": "Fortran Free Form", - ".factor": "Factor", - ".fan": "Fantom", - ".fancypack": "Fancy", - ".fcgi": "Shell", - ".feature": "Gherkin", - ".fir": "FIRRTL", - ".fish": "fish", - ".flex": "JFlex", - ".flux": "FLUX", - ".fnc": "PLSQL", - ".fnl": "Fennel", - ".for": "Fortran", - ".forth": "Forth", - ".fp": "GLSL", - ".fpp": "Fortran", - ".fr": "Frege", - ".frag": "JavaScript", - ".frg": "GLSL", - ".frm": "Visual Basic 6.0", - ".frt": "Forth", - ".fs": "GLSL", - ".fsh": "GLSL", - ".fshader": "GLSL", - ".fsi": "F#", - ".fst": "F*", - ".fsti": "F*", - ".fsx": "F#", - ".fth": "Forth", - ".ftl": "FreeMarker", - ".fun": "Standard ML", - ".fut": "Futhark", - ".fx": "HLSL", - ".fxh": "HLSL", - ".fy": "Fancy", - ".g": "GAP", - ".g4": "ANTLR", - ".gaml": "GAML", - ".gap": "GAP", - ".gawk": "Awk", - ".gco": "G-code", - ".gcode": "G-code", - ".gd": "GDScript", - ".gdb": "GDB", - ".gdbinit": "GDB", - ".gemspec": "Ruby", - ".geo": "GLSL", - ".geom": "GLSL", - ".gf": "Grammatical Framework", - ".gi": "GAP", - ".gjs": "Glimmer JS", - ".gleam": "Gleam", - ".glf": "Glyph", - ".glsl": "GLSL", - ".glslf": "GLSL", - ".glslv": "GLSL", - ".gml": "Game Maker Language", - ".gms": "GAMS", - ".gnu": "Gnuplot", - ".gnuplot": "Gnuplot", - ".go": "Go", - ".god": "Ruby", - ".golo": "Golo", - ".gp": "Gnuplot", - ".grace": "Grace", - ".groovy": "Groovy", - ".grt": "Groovy", - ".gs": "JavaScript", - ".gsc": "GSC", - ".gsh": "GSC", - ".gshader": "GLSL", - ".gsp": "Groovy Server Pages", - ".gst": "Gosu", - ".gsx": "Gosu", - ".gtpl": "Groovy", - ".gts": "Glimmer TS", - ".gvy": "Groovy", - ".gyp": "Python", - ".gypi": "Python", - ".h": "Objective-C", - ".h++": "C++", - ".h.in": "C", - ".ha": "Hare", - ".hack": "Hack", - ".hats": "ATS", - ".hb": "Harbour", - ".hc": "HolyC", - ".hcl": "HCL", - ".hh": "Hack", - ".hhi": "Hack", - ".hic": "Clojure", - ".hip": "HIP", - ".hlean": "Lean", - ".hlsl": "HLSL", - ".hlsli": "HLSL", - ".hoon": "hoon", - ".hpp": "C++", - ".hqf": "SQF", - ".hql": "HiveQL", - ".hrl": "Erlang", - ".hs": "Haskell", - ".hs-boot": "Haskell", - ".hsc": "Haskell", - ".hx": "Haxe", - ".hxsl": "Haxe", - ".hxx": "C++", - ".hy": "Hy", - ".i": "SWIG", - ".i3": "Modula-3", - ".i7x": "Inform 7", - ".ice": "Slice", - ".iced": "CoffeeScript", - ".icl": "Clean", - ".idc": "C", - ".idr": "Idris", - ".ig": "Modula-3", - ".ihlp": "Stata", - ".ijm": "ImageJ Macro", - ".ijs": "J", - ".ik": "Ioke", - ".ily": "LilyPond", - ".imba": "Imba", - ".inc": "SourcePawn", - ".ink": "Ink", - ".inl": "C++", - ".ino": "C++", - ".intr": "Dylan", - ".io": "Io", - ".iol": "Jolie", - ".ipf": "IGOR Pro", - ".ipp": "C++", - ".isl": "Inno Setup", - ".ispc": "ISPC", - ".iss": "Inno Setup", - ".ixx": "C++", - ".j": "Objective-J", - ".jai": "Jai", - ".jake": "JavaScript", - ".janet": "Janet", - ".jav": "Java", - ".java": "Java", - ".javascript": "JavaScript", - ".jbuilder": "Ruby", - ".jcl": "JCL", - ".jflex": "JFlex", - ".jison": "Jison", - ".jisonlex": "Jison Lex", - ".jl": "Julia", - ".jq": "jq", - ".js": "JavaScript", - ".js.erb": "JavaScript+ERB", - ".jsb": "JavaScript", - ".jscad": "JavaScript", - ".jsfl": "JavaScript", - ".jsh": "Java", - ".jslib": "JavaScript", - ".jsm": "JavaScript", - ".jsonnet": "Jsonnet", - ".jsp": "Java Server Pages", - ".jspre": "JavaScript", - ".jss": "JavaScript", - ".jsx": "JavaScript", - ".jte": "Java Template Engine", - ".just": "Just", - ".kak": "KakouneScript", - ".kid": "Genshi", - ".kk": "Koka", - ".kojo": "Scala", - ".krl": "KRL", - ".ks": "KerboScript", - ".ksh": "Shell", - ".ksy": "Kaitai Struct", - ".kt": "Kotlin", - ".ktm": "Kotlin", - ".kts": "Kotlin", - ".l": "PicoLisp", - ".lagda": "Literate Agda", - ".las": "Lasso", - ".lasso": "Lasso", - ".lasso8": "Lasso", - ".lasso9": "Lasso", - ".ld": "Linker Script", - ".lds": "Linker Script", - ".lean": "Lean 4", - ".lex": "Lex", - ".lfe": "LFE", - ".lgt": "Logtalk", - ".lhs": "Literate Haskell", - ".libsonnet": "Jsonnet", - ".lid": "Dylan", - ".lidr": "Idris", - ".ligo": "LigoLANG", - ".linq": "C#", - ".lisp": "NewLisp", - ".litcoffee": "Literate CoffeeScript", - ".livecodescript": "LiveCode Script", - ".lkml": "LookML", - ".ll": "LLVM", - ".lmi": "Python", - ".logtalk": "Logtalk", - ".lol": "LOLCODE", - ".lookml": "LookML", - ".lp": "Linear Programming", - ".lpr": "Pascal", - ".ls": "LoomScript", - ".lsl": "LSL", - ".lslp": "LSL", - ".lsp": "NewLisp", - ".lua": "Lua", - ".luau": "Luau", - ".lvclass": "LabVIEW", - ".lvlib": "LabVIEW", - ".lvproj": "LabVIEW", - ".ly": "LilyPond", - ".m": "Objective-C", - ".m2": "Macaulay2", - ".m3": "Modula-3", - ".m4": "M4Sugar", - ".ma": "Mathematica", - ".mak": "Makefile", - ".make": "Makefile", - ".makefile": "Makefile", - ".mako": "Mako", - ".mao": "Mako", - ".mata": "Stata", - ".matah": "Stata", - ".mathematica": "Mathematica", - ".matlab": "MATLAB", - ".mawk": "Awk", - ".maxhelp": "Max", - ".maxpat": "Max", - ".maxproj": "Max", - ".mbt": "MoonBit", - ".mc": "Monkey C", - ".mcfunction": "mcfunction", - ".mcr": "MAXScript", - ".md": "GCC Machine Description", - ".metal": "Metal", - ".mg": "Modula-3", - ".minid": "MiniD", - ".mint": "Mint", - ".mirah": "Mirah", - ".mjs": "JavaScript", - ".mk": "Makefile", - ".mkfile": "Makefile", - ".ml": "Standard ML", - ".ml4": "OCaml", - ".mli": "OCaml", - ".mligo": "CameLIGO", - ".mlir": "MLIR", - ".mll": "OCaml", - ".mly": "OCaml", - ".mm": "Objective-C++", - ".mmk": "Module Management System", - ".mms": "Module Management System", - ".mo": "Motoko", - ".mod": "NMODL", - ".mojo": "Mojo", - ".monkey": "Monkey", - ".monkey2": "Monkey", - ".moo": "Moocode", - ".moon": "MoonScript", - ".move": "Move", - ".mpl": "JetBrains MPS", - ".mps": "JetBrains MPS", - ".mq4": "MQL4", - ".mq5": "MQL5", - ".mqh": "MQL5", - ".mrc": "mIRC Script", - ".ms": "Unix Assembly", - ".msd": "JetBrains MPS", - ".msg": "OMNeT++ MSG", - ".mspec": "Ruby", - ".mss": "CartoCSS", - ".mt": "Mathematica", - ".mts": "TypeScript", - ".mu": "mupad", - ".mud": "ZIL", - ".muf": "MUF", - ".mumps": "M", - ".mxt": "Max", - ".myt": "Myghty", - ".mzn": "MiniZinc", - ".n": "Nemerle", - ".nas": "Nasal", - ".nasl": "NASL", - ".nasm": "Assembly", - ".nawk": "Awk", - ".nb": "Mathematica", - ".nbp": "Mathematica", - ".nc": "nesC", - ".ncl": "NCL", - ".ne": "Nearley", - ".nearley": "Nearley", - ".ned": "OMNeT++ NED", - ".nf": "Nextflow", - ".ni": "Inform 7", - ".nim": "Nim", - ".nim.cfg": "Nim", - ".nimble": "Nim", - ".nimrod": "Nim", - ".nims": "Nim", - ".nit": "Nit", - ".nix": "Nix", - ".njs": "JavaScript", - ".nl": "NewLisp", - ".nlogo": "NetLogo", - ".nomad": "HCL", - ".nqp": "Raku", - ".nr": "Noir", - ".nse": "Lua", - ".nsh": "NSIS", - ".nsi": "NSIS", - ".nss": "NWScript", - ".nu": "Nushell", - ".numpy": "NumPy", - ".numpyw": "NumPy", - ".numsc": "NumPy", - ".nut": "Squirrel", - ".ny": "Common Lisp", - ".ob2": "Oberon", - ".odin": "Odin", - ".ol": "Jolie", - ".omgrofl": "Omgrofl", - ".ooc": "ooc", - ".opa": "Opa", - ".opal": "Opal", - ".opencl": "OpenCL", - ".orc": "Csound", - ".os": "1C Enterprise", - ".overpassql": "OverpassQL", - ".ox": "Ox", - ".oxh": "Ox", - ".oxo": "Ox", - ".oxygene": "Oxygene", - ".oz": "Oz", - ".p": "OpenEdge ABL", - ".p4": "P4", - ".p6": "Raku", - ".p6l": "Raku", - ".p6m": "Raku", - ".p8": "Lua", - ".pac": "JavaScript", - ".pact": "Pact", - ".pan": "Pan", - ".parrot": "Parrot", - ".pas": "Pascal", - ".pascal": "Pascal", - ".pasm": "Parrot Assembly", - ".pat": "Max", - ".pb": "PureBasic", - ".pbi": "PureBasic", - ".pbt": "PowerBuilder", - ".pck": "PLSQL", - ".pd_lua": "Lua", - ".pddl": "PDDL", - ".pde": "Processing", - ".peggy": "PEG.js", - ".pegjs": "PEG.js", - ".pep": "Pep8", - ".perl": "Perl", - ".pgsql": "PLpgSQL", - ".ph": "Perl", - ".php": "PHP", - ".php3": "PHP", - ".php4": "PHP", - ".php5": "PHP", - ".phps": "PHP", - ".phpt": "PHP", - ".pig": "PigLatin", - ".pike": "Pike", - ".pir": "Parrot Internal Representation", - ".pkb": "PLSQL", - ".pkl": "Pkl", - ".pks": "PLSQL", - ".pl": "Raku", - ".pl6": "Raku", - ".plb": "PLSQL", - ".plot": "Gnuplot", - ".pls": "PLSQL", - ".plsql": "PLSQL", - ".plt": "Prolog", - ".pluginspec": "Ruby", - ".plx": "Perl", - ".pm": "Raku", - ".pm6": "Raku", - ".pml": "Promela", - ".pmod": "Pike", - ".podsl": "Common Lisp", - ".podspec": "Ruby", - ".pogo": "PogoScript", - ".polar": "Polar", - ".pony": "Pony", - ".por": "Portugol", - ".pov": "POV-Ray SDL", - ".pp": "Puppet", - ".pprx": "REXX", - ".praat": "Praat", - ".prawn": "Ruby", - ".prc": "PLSQL", - ".prg": "xBase", - ".pri": "QMake", - ".pro": "QMake", - ".prolog": "Prolog", - ".prw": "xBase", - ".ps1": "PowerShell", - ".psc": "Papyrus", - ".psd1": "PowerShell", - ".psgi": "Perl", - ".psm1": "PowerShell", - ".purs": "PureScript", - ".pwn": "Pawn", - ".pxd": "Cython", - ".pxi": "Cython", - ".py": "Python", - ".py3": "Python", - ".pyde": "Python", - ".pyi": "Python", - ".pyp": "Python", - ".pyt": "Python", - ".pyw": "Python", - ".pyx": "Cython", - ".q": "q", - ".qasm": "OpenQASM", - ".qbs": "QML", - ".ql": "CodeQL", - ".qll": "CodeQL", - ".qml": "QML", - ".qs": "Qt Script", - ".r": "Rez", - ".r2": "Rebol", - ".r3": "Rebol", - ".rabl": "Ruby", - ".rake": "Ruby", - ".raku": "Raku", - ".rakumod": "Raku", - ".rb": "Ruby", - ".rbbas": "REALbasic", - ".rbfrm": "REALbasic", - ".rbi": "Ruby", - ".rbmnu": "REALbasic", - ".rbres": "REALbasic", - ".rbtbar": "REALbasic", - ".rbuild": "Ruby", - ".rbuistate": "REALbasic", - ".rbw": "Ruby", - ".rbx": "Ruby", - ".rbxs": "Lua", - ".rchit": "GLSL", - ".rd": "R", - ".re": "Reason", - ".reb": "Rebol", - ".rebol": "Rebol", - ".red": "Red", - ".reds": "Red", - ".rego": "Open Policy Agent", - ".rei": "Reason", - ".religo": "ReasonLIGO", - ".res": "ReScript", - ".resource": "RobotFramework", - ".rex": "REXX", - ".rexx": "REXX", - ".rg": "Rouge", - ".ring": "Ring", - ".rkt": "Racket", - ".rktd": "Racket", - ".rktl": "Racket", - ".rl": "Ragel", - ".rmiss": "GLSL", - ".robot": "RobotFramework", - ".roc": "Roc", - ".rockspec": "Lua", - ".rpgle": "RPGLE", - ".rpy": "Ren'Py", - ".rs": "Rust", - ".rs.in": "Rust", - ".rsc": "RouterOS Script", - ".rsh": "RenderScript", - ".rsx": "R", - ".ru": "Ruby", - ".ruby": "Ruby", - ".s": "Unix Assembly", - ".sage": "Sage", - ".sagews": "Sage", - ".sail": "Sail", - ".sas": "SAS", - ".sats": "ATS", - ".sbt": "Scala", - ".sc": "SuperCollider", - ".scad": "OpenSCAD", - ".scala": "Scala", - ".scd": "SuperCollider", - ".sce": "Scilab", - ".scenic": "Scenic", - ".sch": "Scheme", - ".sci": "Scilab", - ".scm": "Tree-sitter Query", - ".sco": "Csound Score", - ".scpt": "AppleScript", - ".scrbl": "Racket", - ".sdc": "Tcl", - ".sed": "sed", - ".self": "Self", - ".sexp": "Common Lisp", - ".sh": "Shell", - ".sh-session": "ShellSession", - ".sh.in": "Shell", - ".shader": "ShaderLab", - ".shen": "Shen", - ".sieve": "Sieve", - ".sig": "Standard ML", - ".sj": "Objective-J", - ".sjs": "JavaScript", - ".sl": "Slash", - ".slang": "Slang", - ".sld": "Scheme", - ".sls": "Scheme", - ".sma": "Pawn", - ".smali": "Smali", - ".smithy": "Smithy", - ".smk": "Snakemake", - ".sml": "Standard ML", - ".smt": "SMT", - ".smt2": "SMT", - ".snakefile": "Snakemake", - ".sol": "Solidity", - ".sp": "SourcePawn", - ".spc": "PLSQL", - ".spec": "Ruby", - ".spin": "Propeller Spin", - ".sps": "Scheme", - ".sqf": "SQF", - ".sql": "TSQL", - ".sqlrpgle": "RPGLE", - ".sra": "PowerBuilder", - ".sru": "PowerBuilder", - ".srw": "PowerBuilder", - ".ss": "Scheme", - ".ssjs": "JavaScript", - ".st": "Smalltalk", - ".stan": "Stan", - ".star": "Starlark", - ".sthlp": "Stata", - ".story": "Gherkin", - ".sv": "SystemVerilog", - ".svh": "SystemVerilog", - ".sw": "Sway", - ".swift": "Swift", - ".t": "Turing", - ".tac": "Python", - ".tact": "Tact", - ".tag": "Java Server Pages", - ".talon": "Talon", - ".tcc": "C++", - ".tcl": "Tcl", - ".tcl.in": "Tcl", - ".tcsh": "Tcsh", - ".tesc": "GLSL", - ".tese": "GLSL", - ".tf": "HCL", - ".tfvars": "HCL", - ".thor": "Ruby", - ".thrift": "Thrift", - ".thy": "Isabelle", - ".tla": "TLA", - ".tlv": "TL-Verilog", - ".tm": "Tcl", - ".tmux": "Shell", - ".toit": "Toit", - ".tool": "Shell", - ".tpb": "PLSQL", - ".tpl": "Smarty", - ".tpp": "C++", - ".tps": "PLSQL", - ".trg": "PLSQL", - ".trigger": "Shell", - ".ts": "TypeScript", - ".tsp": "TypeSpec", - ".tst": "Scilab", - ".tsx": "TSX", - ".tu": "Turing", - ".txl": "TXL", - ".txx": "C++", - ".typ": "Typst", - ".uc": "UnrealScript", - ".udo": "Csound", - ".uno": "Uno", - ".upc": "Unified Parallel C", - ".ur": "UrWeb", - ".urs": "UrWeb", - ".v": "Verilog", - ".vala": "Vala", - ".vapi": "Vala", - ".vark": "Gosu", - ".vb": "Visual Basic .NET", - ".vba": "Vim Script", - ".vbhtml": "Visual Basic .NET", - ".vbs": "VBScript", - ".vcl": "VCL", - ".veo": "Verilog", - ".vert": "GLSL", - ".vh": "SystemVerilog", - ".vhd": "VHDL", - ".vhdl": "VHDL", - ".vhf": "VHDL", - ".vhi": "VHDL", - ".vho": "VHDL", - ".vhs": "VHDL", - ".vht": "VHDL", - ".vhw": "VHDL", - ".vim": "Vim Script", - ".vimrc": "Vim Script", - ".vmb": "Vim Script", - ".volt": "Volt", - ".vrx": "GLSL", - ".vs": "GLSL", - ".vsh": "GLSL", - ".vshader": "GLSL", - ".vw": "PLSQL", - ".vy": "Vyper", - ".w": "OpenEdge ABL", - ".wast": "WebAssembly", - ".wat": "WebAssembly", - ".watchr": "Ruby", - ".wdl": "WDL", - ".webidl": "WebIDL", - ".wgsl": "WGSL", - ".whiley": "Whiley", - ".wisp": "wisp", - ".wl": "Mathematica", - ".wlk": "Wollok", - ".wlt": "Mathematica", - ".wlua": "Lua", - ".workflow": "HCL", - ".wren": "Wren", - ".ws": "Witcher Script", - ".wsgi": "Python", - ".x": "RPC", - ".x10": "X10", - ".x68": "Motorola 68K Assembly", - ".xc": "XC", - ".xdc": "Tcl", - ".xi": "Logos", - ".xm": "Logos", - ".xojo_code": "Xojo", - ".xojo_menu": "Xojo", - ".xojo_report": "Xojo", - ".xojo_script": "Xojo", - ".xojo_toolbar": "Xojo", - ".xojo_window": "Xojo", - ".xpl": "XProc", - ".xproc": "XProc", - ".xpy": "Python", - ".xq": "XQuery", - ".xql": "XQuery", - ".xqm": "XQuery", - ".xquery": "XQuery", - ".xqy": "XQuery", - ".xrl": "Erlang", - ".xs": "XS", - ".xsh": "Xonsh", - ".xsjs": "JavaScript", - ".xsjslib": "JavaScript", - ".xsl": "XSLT", - ".xslt": "XSLT", - ".xtend": "Xtend", - ".xzap": "ZAP", - ".y": "Yacc", - ".yacc": "Yacc", - ".yap": "Prolog", - ".yar": "YARA", - ".yara": "YARA", - ".yrl": "Erlang", - ".yul": "Yul", - ".yy": "Yacc", - ".z3": "SMT", - ".zap": "ZAP", - ".zeek": "Zeek", - ".zep": "Zephir", - ".zig": "Zig", - ".zig.zon": "Zig", - ".zil": "ZIL", - ".zimpl": "Zimpl", - ".zmpl": "Zimpl", - ".zpl": "Zimpl", - ".zs": "ZenScript", - ".zsh": "Shell", - ".zsh-theme": "Shell", - } diff --git a/python_coderunner/src/file_info_extractor/fetch_linguist_extensions.py b/python_coderunner/src/file_info_extractor/fetch_linguist_extensions.py deleted file mode 100644 index bcd30c3..0000000 --- a/python_coderunner/src/file_info_extractor/fetch_linguist_extensions.py +++ /dev/null @@ -1,32 +0,0 @@ -""" -This module fetches the latest programming language extension mappings from the GitHub Linguist project. -It downloads the 'languages.yml' file, extracts all programming language extensions, and creates a mapping -from file extensions to language names. The resulting mapping is saved as a JSON file named 'ext_to_lang.json'. - -Dependencies: - - requests - - PyYAML - -Usage: - Run this script to update the 'BasicFileInfoExtractor.EXT_TO_LANG' with the latest extension-to-language mapping. -""" - -import json -import typing - -import requests -import yaml - -if __name__ == "__main__": - URL = "https://raw.githubusercontent.com/github/linguist/master/lib/linguist/languages.yml" - response = requests.get(URL, timeout=10) - languages_data = yaml.safe_load(response.text) - - ext_to_lang: typing.Dict[str, str] = {} - for lang, data in languages_data.items(): - if data.get("type") == "programming": - for ext in data.get("extensions", []): - ext_to_lang[ext.lower()] = lang - - with open("ext_to_lang.json", encoding="utf-8", mode="w") as fout: - json.dump(ext_to_lang, fout, indent=4, sort_keys=True) diff --git a/python_coderunner/src/file_info_extractor/interface.py b/python_coderunner/src/file_info_extractor/interface.py index afe479e..6b380bd 100644 --- a/python_coderunner/src/file_info_extractor/interface.py +++ b/python_coderunner/src/file_info_extractor/interface.py @@ -1,3 +1,4 @@ +import os from abc import ABC, abstractmethod from typing import Optional @@ -8,34 +9,43 @@ class IFileInfoExtractor(ABC): Accepts only file absolute path. """ - @abstractmethod def get_dir(self, file_path_abs: str) -> str: - raise NotImplementedError + dir_path: str = os.path.dirname(file_path_abs) + if dir_path and not dir_path.endswith(os.sep): + dir_path += os.sep + return dir_path - @abstractmethod def get_dir_without_trailing_slash(self, file_path_abs: str) -> str: - raise NotImplementedError + return os.path.dirname(file_path_abs).rstrip(os.sep) + + def get_drive_letter(self, file_path_abs: str) -> str: + drive, _ = os.path.splitdrive(file_path_abs) + return drive + + def get_file_ext(self, file_path_abs: str) -> str: + base: str = self.get_file_name(file_path_abs) + dot_pos: int = base.rfind(".") + return base[dot_pos : len(base)].lower() if dot_pos != -1 else "" - @abstractmethod def get_file_name(self, file_path_abs: str) -> str: - raise NotImplementedError + return os.path.basename(file_path_abs) - @abstractmethod def get_file_name_without_ext(self, file_path_abs: str) -> str: - raise NotImplementedError - - @abstractmethod - def get_file_ext(self, file_path_abs: str) -> str: - raise NotImplementedError + base: str = self.get_file_name(file_path_abs) + dot_pos: int = base.rfind(".") + return base[:dot_pos] if dot_pos != -1 else base @abstractmethod def get_file_type(self, file_path_abs: str) -> Optional[str]: raise NotImplementedError - @abstractmethod - def get_drive_letter(self, file_path_abs: str) -> str: - raise NotImplementedError - - @abstractmethod def get_shebang(self, file_path_abs: str) -> Optional[str]: - raise NotImplementedError + if not os.path.exists(file_path_abs): + return None + + try: + with open(file_path_abs, "r", encoding="utf-8") as fin: + first_line: str = fin.readline().strip() + return first_line[2:] if first_line.startswith("#!") and not first_line.startswith("#![") else None + except (IOError, UnicodeDecodeError): + return None diff --git a/python_coderunner/src/file_info_extractor/vim_file_info_extractor.py b/python_coderunner/src/file_info_extractor/vim_file_info_extractor.py new file mode 100644 index 0000000..c9ec8c6 --- /dev/null +++ b/python_coderunner/src/file_info_extractor/vim_file_info_extractor.py @@ -0,0 +1,11 @@ +from typing import Optional + +import vim + +from src.file_info_extractor.interface import IFileInfoExtractor + + +class TVimFileInfoExtractor(IFileInfoExtractor): + def get_file_type(self, file_path_abs: str) -> Optional[str]: + file_type: Optional[str] = vim.eval("&filetype") + return None if not file_type else file_type diff --git a/python_coderunner/tests/unit/command_builder/test_interpolator.py b/python_coderunner/tests/unit/command_builder/test_interpolator.py deleted file mode 100644 index 1d60d5c..0000000 --- a/python_coderunner/tests/unit/command_builder/test_interpolator.py +++ /dev/null @@ -1,42 +0,0 @@ -from src.command_builder import TInterpolatorCommandBuilder -from src.file_info_extractor import IFileInfoExtractor -from src.project_info_extractor import IProjectInfoExtractor - - -class THelper: - def __init__(self, project_info_extractor: IProjectInfoExtractor, file_info_extractor: IFileInfoExtractor): - self._project_info_extractor: IProjectInfoExtractor = project_info_extractor - self._file_info_extractor: IFileInfoExtractor = file_info_extractor - - def __call__(self, pattern: str, file_path_abs: str, expected_result: str): - builder: TInterpolatorCommandBuilder = TInterpolatorCommandBuilder( - pattern, self._project_info_extractor, self._file_info_extractor - ) - assert builder.build(file_path_abs) == expected_result - - -def test_interpolator_file_info_extractor( - fixture_project_info_extractor: IProjectInfoExtractor, fixture_file_info_extractor: IFileInfoExtractor -): - helper: THelper = THelper(fixture_project_info_extractor, fixture_file_info_extractor) - helper("$dir", "/home/user/file.txt", "/home/user/") - helper("$dirWithoutTrailingSlash", "/home/user/file.txt", "/home/user") - helper("$dirWithoutTrailingSlash$dir", "/home/user/file.txt", "/home/user/home/user/") - helper("$dir$fileName", "/home/user/file.txt", "/home/user/file.txt") - helper("$dir$fileName", "/home/user/file", "/home/user/file") - helper("$fullFileName", "/home/user/file.txt", "/home/user/file.txt") - helper("$dir$fileNameWithoutExt", "/home/user/file", "/home/user/file") - helper("$dir$fileNameWithoutExt", "/home/user/file.txt", "/home/user/file") - helper("$dir$fileNameWithoutExt", "/home/user/file.txt.cpp", "/home/user/file") - helper("$dir$fileNameWithoutExt$fileExt", "/home/user/file.txt.cpp", "/home/user/file.txt.cpp") - - -def test_interpolator_project_info_extractor( - fixture_project_info_extractor: IProjectInfoExtractor, fixture_file_info_extractor: IFileInfoExtractor -): - helper: THelper = THelper(fixture_project_info_extractor, fixture_file_info_extractor) - workspace_root: str = fixture_project_info_extractor.get_workspace_root() - - helper("$workspaceRoot", "/home/user/file", workspace_root) - helper("$workspaceRoot/$fileName", "/home/user/file.txt", f"{workspace_root}/file.txt") - helper("$workspaceRoot/$fileNameWithoutExt", "/home/user/file", f"{workspace_root}/file") diff --git a/python_coderunner/tests/unit/command_builder/test_interpolator_command_builder.py b/python_coderunner/tests/unit/command_builder/test_interpolator_command_builder.py new file mode 100644 index 0000000..3b5677f --- /dev/null +++ b/python_coderunner/tests/unit/command_builder/test_interpolator_command_builder.py @@ -0,0 +1,37 @@ +from src.command_builder import TInterpolatorCommandBuilder +from src.file_info_extractor import IFileInfoExtractor +from src.project_info_extractor import IProjectInfoExtractor + + +class TestInterpolatorCommandBuilder: + def test_build( + self, fixture_project_info_extractor: IProjectInfoExtractor, fixture_file_info_extractor: IFileInfoExtractor + ): + comparator: TestInterpolatorCommandBuilder.TComparator = TestInterpolatorCommandBuilder.TComparator( + fixture_project_info_extractor, fixture_file_info_extractor + ) + workspace_root: str = fixture_project_info_extractor.get_workspace_root() + comparator("$dir", "/home/user/file.txt", "/home/user/") + comparator("$dirWithoutTrailingSlash", "/home/user/file.txt", "/home/user") + comparator("$dirWithoutTrailingSlash$dir", "/home/user/file.txt", "/home/user/home/user/") + comparator("$dir$fileName", "/home/user/file.txt", "/home/user/file.txt") + comparator("$dir$fileName", "/home/user/file", "/home/user/file") + comparator("$fullFileName", "/home/user/file.txt", "/home/user/file.txt") + comparator("$dir$fileNameWithoutExt", "/home/user/file", "/home/user/file") + comparator("$dir$fileNameWithoutExt", "/home/user/file.txt", "/home/user/file") + comparator("$dir$fileNameWithoutExt", "/home/user/file.txt.cpp", "/home/user/file.txt") + comparator("$dir$fileNameWithoutExt$fileExt", "/home/user/file.txt.cpp", "/home/user/file.txt.cpp") + comparator("$workspaceRoot", "/home/user/file", workspace_root) + comparator("$workspaceRoot/$fileName", "/home/user/file.txt", f"{workspace_root}/file.txt") + comparator("$workspaceRoot/$fileNameWithoutExt", "/home/user/file", f"{workspace_root}/file") + + class TComparator: + def __init__(self, project_info_extractor: IProjectInfoExtractor, file_info_extractor: IFileInfoExtractor): + self._project_info_extractor: IProjectInfoExtractor = project_info_extractor + self._file_info_extractor: IFileInfoExtractor = file_info_extractor + + def __call__(self, pattern: str, file_path_abs: str, expected_result: str): + builder: TInterpolatorCommandBuilder = TInterpolatorCommandBuilder( + pattern, self._project_info_extractor, self._file_info_extractor + ) + assert builder.build(file_path_abs) == expected_result diff --git a/python_coderunner/tests/unit/command_builders_dispatcher/test_file_ext_command_builders_dispatcher.py b/python_coderunner/tests/unit/command_builders_dispatcher/test_file_ext_command_builders_dispatcher.py index 9bdd0ab..88d671b 100644 --- a/python_coderunner/tests/unit/command_builders_dispatcher/test_file_ext_command_builders_dispatcher.py +++ b/python_coderunner/tests/unit/command_builders_dispatcher/test_file_ext_command_builders_dispatcher.py @@ -11,9 +11,9 @@ [ ("/home/script.py", ".py"), ("/app.js", ".js"), - (".8xp.txt", ".8xp.txt"), - ("/home/unknown.x.y.z", ".x.y.z"), - ("/home/unknownx.y.z", None), + (".8xp.txt", ".txt"), + ("/home/unknown.x.y.z", ".z"), + ("/home/unknown.unknown", None), ], ) def test_file_ext_command_builders_dispatcher( diff --git a/python_coderunner/tests/unit/command_builders_dispatcher/test_file_type_command_builders_dispatcher.py b/python_coderunner/tests/unit/command_builders_dispatcher/test_file_type_command_builders_dispatcher.py index 493dc58..966db20 100644 --- a/python_coderunner/tests/unit/command_builders_dispatcher/test_file_type_command_builders_dispatcher.py +++ b/python_coderunner/tests/unit/command_builders_dispatcher/test_file_type_command_builders_dispatcher.py @@ -9,16 +9,10 @@ @pytest.mark.parametrize( ("file_path", "expected_build_result"), [ - ("/home/script.py", "Python"), - ("/app.js", "JavaScript"), - ("module.ts", "TypeScript"), - ("Main.java", "Java"), - ("program.cpp", "C++"), - ("kernel.c", "C"), - ("server.go", "Go"), - ("app.rb", "Ruby"), - ("index.php", "PHP"), - ("start.sh", "Shell"), + ("/home/script.py", "python"), + ("/app.js", "javascript"), + ("module.ts", "typescript"), + ("program.cpp", "cpp"), ("unknown.xyz", None), ], ) diff --git a/python_coderunner/tests/unit/command_dispatcher_strategy_selector/test_basic_command_dispatcher_strategy_selector.py b/python_coderunner/tests/unit/command_dispatcher_strategy_selector/test_basic_command_dispatcher_strategy_selector.py index 224b3d8..bcaa82e 100644 --- a/python_coderunner/tests/unit/command_dispatcher_strategy_selector/test_basic_command_dispatcher_strategy_selector.py +++ b/python_coderunner/tests/unit/command_dispatcher_strategy_selector/test_basic_command_dispatcher_strategy_selector.py @@ -59,7 +59,7 @@ b"#!/usr/bin/python", [EDispatchersTypes.BY_FILE_TYPE, EDispatchersTypes.BY_GLOB, EDispatchersTypes.BY_FILE_EXT], False, - "Python", + "python", ), ], ) diff --git a/python_coderunner/tests/unit/conftest.py b/python_coderunner/tests/unit/conftest.py index 71e1e51..0c33525 100644 --- a/python_coderunner/tests/unit/conftest.py +++ b/python_coderunner/tests/unit/conftest.py @@ -3,10 +3,11 @@ import sys import tempfile import unittest -from typing import Callable, Dict, Generator, Sequence, Tuple +from typing import Dict, Generator, Tuple from unittest.mock import MagicMock import pytest +from pytest_lazyfixture import lazy_fixture sys.modules["vim"] = MagicMock() from src.command_builder import ICommandBuilder @@ -23,51 +24,40 @@ TVimConfigGetter, TVimConfigManager, ) -from src.file_info_extractor import IFileInfoExtractor, TBasicFileInfoExtractor +from src.file_info_extractor import IFileInfoExtractor, TVimFileInfoExtractor from src.project_info_extractor import IProjectInfoExtractor, TVimProjectInfoExtractor -def get_all_config_getters_factories() -> Sequence[Callable[[], IConfigGetter]]: - return (TVimConfigGetter,) +@pytest.fixture(params=(lazy_fixture("fixture_vim_config_manager"),)) +def fixture_config_manager(request: pytest.FixtureRequest) -> TBasicConfigManager: + return request.param -def get_all_config_validators_factory() -> Sequence[Callable[[], TBasicConfigValidator]]: - return (TBasicConfigValidator,) - +@pytest.fixture +def fixture_vim_config_manager( + fixture_config_getter: IConfigGetter, fixture_config_validator: TBasicConfigValidator +) -> TBasicConfigManager: + return TVimConfigManager(fixture_config_getter, fixture_config_validator) -@pytest.fixture(params=get_all_config_validators_factory()) -def fixture_config_validator(request: pytest.FixtureRequest) -> TBasicConfigValidator: - return request.param() +@pytest.fixture(params=(lazy_fixture("fixture_vim_config_getter"),)) +def fixture_config_getter(request: pytest.FixtureRequest) -> IConfigGetter: + return request.param -def vim_config_manager_factory( - config_getter: IConfigGetter, config_validator: TBasicConfigValidator -) -> Generator[TBasicConfigManager]: - yield TVimConfigManager(config_getter, config_validator) +@pytest.fixture +def fixture_vim_config_getter() -> IConfigGetter: + return TVimConfigGetter() -def get_all_config_managers_factories( - config_getters_factories: Sequence[Callable[[], IConfigGetter]], - config_validators_factories: Sequence[Callable[[], TBasicConfigValidator]], -) -> Sequence[Callable[..., Generator[TBasicConfigManager]]]: - config_managers_factories: Sequence[Callable[..., Generator[TBasicConfigManager]]] = [] - for config_getter_factory in config_getters_factories: - for config_validator_factory in config_validators_factories: - config_managers_factories.append( - lambda config_getter_factory=config_getter_factory, - config_validator_factory=config_validator_factory: vim_config_manager_factory( - config_getter_factory(), config_validator_factory() - ) - ) - return config_managers_factories +@pytest.fixture(params=(lazy_fixture("fixture_basic_config_validator"),)) +def fixture_config_validator(request: pytest.FixtureRequest) -> TBasicConfigValidator: + return request.param -@pytest.fixture( - params=get_all_config_managers_factories(get_all_config_getters_factories(), get_all_config_validators_factory()) -) -def fixture_config_manager(request: pytest.FixtureRequest) -> Generator[TBasicConfigManager]: - yield from request.param() +@pytest.fixture +def fixture_basic_config_validator() -> TBasicConfigValidator: + return TBasicConfigValidator() @pytest.fixture @@ -81,7 +71,7 @@ def fixture_shebang_command_builders_dispatcher( def fixture_file_ext_command_builders_dispatcher( fixture_file_info_extractor: IFileInfoExtractor, ) -> TFileExtCommandBuildersDispatcher: - extensions: Tuple[str, ...] = (".py", ".js", ".ts", ".java", ".cpp", ".c", ".go", ".rb", ".8xp.txt", ".x.y.z") + extensions: Tuple[str, ...] = (".py", ".js", ".ts", ".java", ".cpp", ".c", ".go", ".rb", ".txt", ".z") file_ext_to_builder: Dict[str, ICommandBuilder] = { ext: MagicMock(spec=ICommandBuilder, build=MagicMock(return_value=ext)) for ext in extensions } @@ -96,16 +86,10 @@ def fixture_file_type_command_builders_dispatcher( fixture_file_info_extractor: IFileInfoExtractor, ) -> TFileTypeCommandBuildersDispatcher: languages: Tuple[str, ...] = ( - "Python", - "JavaScript", - "TypeScript", - "Java", - "C++", - "C", - "Go", - "Ruby", - "PHP", - "Shell", + "python", + "javascript", + "typescript", + "cpp", ) file_type_to_builder: Dict[str, ICommandBuilder] = { lang: MagicMock(spec=ICommandBuilder, build=MagicMock(return_value=lang)) for lang in languages @@ -140,9 +124,17 @@ def fixture_glob_command_builders_dispatcher() -> TGlobCommandBuildersDispatcher return TGlobCommandBuildersDispatcher(glob_to_builder) -def vim_project_info_extractor_factory(file_info_extractor: IFileInfoExtractor) -> Generator[IProjectInfoExtractor]: +@pytest.fixture(params=(lazy_fixture("fixture_vim_project_info_extractor"),)) +def fixture_project_info_extractor(request: pytest.FixtureRequest) -> IProjectInfoExtractor: + return request.param + + +@pytest.fixture +def fixture_vim_project_info_extractor( + fixture_file_info_extractor: IFileInfoExtractor, +) -> Generator[IProjectInfoExtractor]: with tempfile.TemporaryDirectory() as temp_dir: - extractor = TVimProjectInfoExtractor(file_info_extractor) + extractor: TVimProjectInfoExtractor = TVimProjectInfoExtractor(fixture_file_info_extractor) with unittest.mock.patch.object( extractor, "get_workspace_root", @@ -151,33 +143,24 @@ def vim_project_info_extractor_factory(file_info_extractor: IFileInfoExtractor) yield extractor -def get_all_project_info_extractors_factories( - file_info_extractors_factories: Sequence[Callable[[], IFileInfoExtractor]], -) -> Sequence[Callable[..., Generator[IProjectInfoExtractor]]]: - """ - The problem is that the params in the fixture are defined only once, so if you use the fixture - more than once, all the generators inside are invalidated, so you need to create a generator factory. - """ - project_info_extractors_factories: list[Callable[..., Generator[IProjectInfoExtractor]]] = [] - for file_info_extractor_factory in file_info_extractors_factories: - project_info_extractors_factories.append( - lambda file_info_extractor_factory=file_info_extractor_factory: vim_project_info_extractor_factory( - file_info_extractor_factory() - ) - ) - - return project_info_extractors_factories - - -def get_all_file_info_extractors_factories() -> Sequence[Callable[[], IFileInfoExtractor]]: - return (TBasicFileInfoExtractor,) - - -@pytest.fixture(params=get_all_project_info_extractors_factories(get_all_file_info_extractors_factories())) -def fixture_project_info_extractor(request: pytest.FixtureRequest) -> Generator[IFileInfoExtractor]: - yield from request.param() +@pytest.fixture(params=(lazy_fixture("fixture_vim_file_info_extractor"),)) +def fixture_file_info_extractor(request: pytest.FixtureRequest) -> IFileInfoExtractor: + return request.param -@pytest.fixture(params=get_all_file_info_extractors_factories()) -def fixture_file_info_extractor(request: pytest.FixtureRequest) -> IFileInfoExtractor: - return request.param() +@pytest.fixture +def fixture_vim_file_info_extractor() -> Generator[IFileInfoExtractor]: + extractor: TVimFileInfoExtractor = TVimFileInfoExtractor() + ext_to_lang: Dict[str, str] = { + ".py": "python", + ".cpp": "cpp", + ".rs": "rust", + ".ts": "typescript", + ".js": "javascript", + } + with unittest.mock.patch.object( + extractor, + "get_file_type", + side_effect=lambda file_path_abs: ext_to_lang.get(extractor.get_file_ext(file_path_abs)), + ): + yield extractor diff --git a/python_coderunner/tests/unit/file_info_extractor/test_interface_file_info_extractor.py b/python_coderunner/tests/unit/file_info_extractor/test_interface_file_info_extractor.py index 89c5c80..dfcf3f8 100644 --- a/python_coderunner/tests/unit/file_info_extractor/test_interface_file_info_extractor.py +++ b/python_coderunner/tests/unit/file_info_extractor/test_interface_file_info_extractor.py @@ -4,100 +4,90 @@ import pytest -from src.file_info_extractor import TBasicFileInfoExtractor - - -def test_get_dir(fixture_file_info_extractor: TBasicFileInfoExtractor): - assert fixture_file_info_extractor.get_dir("/home/user/project/file.py") == "/home/user/project/" - assert fixture_file_info_extractor.get_dir("/home/file.txt") == "/home/" - assert fixture_file_info_extractor.get_dir("/home/") == "/home/" - assert fixture_file_info_extractor.get_dir("/home") == "/" - assert fixture_file_info_extractor.get_dir("/file.txt") == "/" - assert fixture_file_info_extractor.get_dir("/") == "/" - - -def test_get_dir_without_trailing_slash(fixture_file_info_extractor: TBasicFileInfoExtractor): - assert fixture_file_info_extractor.get_dir_without_trailing_slash("/file.py") == "" - assert ( - fixture_file_info_extractor.get_dir_without_trailing_slash("/home/user/project/file.py") == "/home/user/project" +from src.file_info_extractor import IFileInfoExtractor + + +class TestFileInfoExtractorInterface: + def test_get_dir(self, fixture_file_info_extractor: IFileInfoExtractor): + assert fixture_file_info_extractor.get_dir("/home/user/project/file.py") == "/home/user/project/" + assert fixture_file_info_extractor.get_dir("/home/file.txt") == "/home/" + assert fixture_file_info_extractor.get_dir("/home/") == "/home/" + assert fixture_file_info_extractor.get_dir("/home") == "/" + assert fixture_file_info_extractor.get_dir("/file.txt") == "/" + assert fixture_file_info_extractor.get_dir("/") == "/" + + def test_get_dir_without_trailing_slash(self, fixture_file_info_extractor: IFileInfoExtractor): + assert fixture_file_info_extractor.get_dir_without_trailing_slash("/file.py") == "" + assert ( + fixture_file_info_extractor.get_dir_without_trailing_slash("/home/user/project/file.py") + == "/home/user/project" + ) + + def test_get_file_name(self, fixture_file_info_extractor: IFileInfoExtractor): + assert fixture_file_info_extractor.get_file_name("/home/user/project/file.py") == "file.py" + + def test_get_file_name_without_ext(self, fixture_file_info_extractor: IFileInfoExtractor): + assert fixture_file_info_extractor.get_file_name_without_ext("/home/user/project/file") == "file" + assert fixture_file_info_extractor.get_file_name_without_ext("/home/user/project/file.py") == "file" + assert fixture_file_info_extractor.get_file_name_without_ext("/home/user/project/file.tar.gz") == "file.tar" + assert fixture_file_info_extractor.get_file_name_without_ext("/home/user/project/.zig.zon") == ".zig" + + def test_get_file_ext(self, fixture_file_info_extractor: IFileInfoExtractor): + assert fixture_file_info_extractor.get_file_ext("/home/user/project/.zig.zon") == ".zon" + assert fixture_file_info_extractor.get_file_ext("/home/user/project/file") == "" + assert fixture_file_info_extractor.get_file_ext("/home/user/project/file.py") == ".py" + assert fixture_file_info_extractor.get_file_ext("/home/user/project/file.cPp") == ".cpp" + + def test_get_file_type(self, fixture_file_info_extractor: IFileInfoExtractor): + assert fixture_file_info_extractor.get_file_type("/home/user/project/file.py") == "python" + assert fixture_file_info_extractor.get_file_type("/home/user/project/file.rs") == "rust" + assert fixture_file_info_extractor.get_file_type("/home/user/project/file.cpp") == "cpp" + assert fixture_file_info_extractor.get_file_type("/home/user/project/file.unknownext") is None + assert fixture_file_info_extractor.get_file_type("/home/user/project/file.PY") == "python" + + def test_get_drive_letter_windows(self, fixture_file_info_extractor: IFileInfoExtractor): + # On Unix, drive will be '', on Windows, 'C:' + path = "C:\\Users\\user\\file.py" + expected = os.path.splitdrive(path)[0] + assert fixture_file_info_extractor.get_drive_letter(path) == expected + + def test_get_drive_letter_unix(self, fixture_file_info_extractor: IFileInfoExtractor): + path = "/home/user/file.py" + assert fixture_file_info_extractor.get_drive_letter(path) == "" + + @pytest.mark.parametrize( + ("content", "expected"), + [ + pytest.param(None, None, id="nonexistent file"), + pytest.param("print('Hello')", None, id="no shebang"), + pytest.param("\n\ncode", None, id="empty first line"), + pytest.param("#!/bin/bash", "/bin/bash", id="basic shebang"), + pytest.param("#!/usr/bin/env python", "/usr/bin/env python", id="env shebang"), + pytest.param(" #!/bin/sh ", "/bin/sh", id="whitespace around shebang"), + pytest.param("#![feature(test)]", None, id="Rust attribute"), + pytest.param("#!", "", id="empty shebang"), + pytest.param("#!\ncode", "", id="shebang with no path"), + pytest.param("#!/usr/bin/python", "/usr/bin/python", id="shebang without newline"), + pytest.param("#!/bin/bash -x\ndef f():\n\treturn0", "/bin/bash -x", id="shebang with multiple lines"), + ], ) - - -def test_get_file_name(fixture_file_info_extractor: TBasicFileInfoExtractor): - assert fixture_file_info_extractor.get_file_name("/home/user/project/file.py") == "file.py" - - -def test_get_file_name_without_ext(fixture_file_info_extractor: TBasicFileInfoExtractor): - assert fixture_file_info_extractor.get_file_name_without_ext("/home/user/project/file") == "file" - assert fixture_file_info_extractor.get_file_name_without_ext("/home/user/project/file.py") == "file" - assert fixture_file_info_extractor.get_file_name_without_ext("/home/user/project/file.tar.gz") == "file" - assert fixture_file_info_extractor.get_file_name_without_ext("/home/user/project/.zig.zon") == "" - - -def test_get_file_ext(fixture_file_info_extractor: TBasicFileInfoExtractor): - assert fixture_file_info_extractor.get_file_ext("/home/user/project/.zig.zon") == ".zig.zon" - assert fixture_file_info_extractor.get_file_ext("/home/user/project/file") == "" - assert fixture_file_info_extractor.get_file_ext("/home/user/project/file.py") == ".py" - assert fixture_file_info_extractor.get_file_ext("/home/user/project/file.cPp") == ".cpp" - - -def test_get_file_type(fixture_file_info_extractor: TBasicFileInfoExtractor): - assert fixture_file_info_extractor.get_file_type("/home/user/project/file.py") == "Python" - assert fixture_file_info_extractor.get_file_type("/home/user/project/file.rs") == "Rust" - assert fixture_file_info_extractor.get_file_type("/home/user/project/file.cpp") == "C++" - assert fixture_file_info_extractor.get_file_type("/home/user/project/file.unknownext") is None - assert fixture_file_info_extractor.get_file_type("/home/user/project/file.PY") == "Python" - assert fixture_file_info_extractor.get_file_type("/home/user/project/file.axs.erb") == "NetLinx+ERB" - assert fixture_file_info_extractor.get_file_type("/home/user/project/.zig.zon") == "Zig" - - -def test_get_drive_letter_windows(fixture_file_info_extractor: TBasicFileInfoExtractor): - # On Unix, drive will be '', on Windows, 'C:' - path = "C:\\Users\\user\\file.py" - expected = os.path.splitdrive(path)[0] - assert fixture_file_info_extractor.get_drive_letter(path) == expected - - -def test_get_drive_letter_unix(fixture_file_info_extractor: TBasicFileInfoExtractor): - path = "/home/user/file.py" - assert fixture_file_info_extractor.get_drive_letter(path) == "" - - -@pytest.mark.parametrize( - ("content", "expected"), - [ - pytest.param(None, None, id="nonexistent file"), - pytest.param("print('Hello')", None, id="no shebang"), - pytest.param("\n\ncode", None, id="empty first line"), - pytest.param("#!/bin/bash", "/bin/bash", id="basic shebang"), - pytest.param("#!/usr/bin/env python", "/usr/bin/env python", id="env shebang"), - pytest.param(" #!/bin/sh ", "/bin/sh", id="whitespace around shebang"), - pytest.param("#![feature(test)]", None, id="Rust attribute"), - pytest.param("#!", "", id="empty shebang"), - pytest.param("#!\ncode", "", id="shebang with no path"), - pytest.param("#!/usr/bin/python", "/usr/bin/python", id="shebang without newline"), - pytest.param("#!/bin/bash -x\ndef f():\n\treturn0", "/bin/bash -x", id="shebang with multiple lines"), - ], -) -def test_get_shebang(fixture_file_info_extractor, content, expected): - if content is None: - assert fixture_file_info_extractor.get_shebang("/bad/path") is expected - return - - with tempfile.NamedTemporaryFile(mode="w", delete=False) as temp_file: - temp_file.write(content) - file_path = temp_file.name - try: - assert fixture_file_info_extractor.get_shebang(file_path) == expected - finally: - os.unlink(file_path) - - -def test_get_shebang_io_error_handling(fixture_file_info_extractor: TBasicFileInfoExtractor): - with patch("builtins.open", side_effect=IOError("Permission denied")): - assert fixture_file_info_extractor.get_shebang("/some/file") is None - - -def test_get_shebang_unicode_decode_error_handling(fixture_file_info_extractor: TBasicFileInfoExtractor): - with patch("builtins.open", side_effect=UnicodeDecodeError("utf-8", b"", 0, 1, "Invalid byte")): - assert fixture_file_info_extractor.get_shebang("/some/file") is None + def test_get_shebang(self, fixture_file_info_extractor: IFileInfoExtractor, content, expected): + if content is None: + assert fixture_file_info_extractor.get_shebang("/bad/path") is expected + return + + with tempfile.NamedTemporaryFile(mode="w", delete=False) as temp_file: + temp_file.write(content) + file_path = temp_file.name + try: + assert fixture_file_info_extractor.get_shebang(file_path) == expected + finally: + os.unlink(file_path) + + def test_get_shebang_io_error_handling(self, fixture_file_info_extractor: IFileInfoExtractor): + with patch("builtins.open", side_effect=IOError("Permission denied")): + assert fixture_file_info_extractor.get_shebang("/some/file") is None + + def test_get_shebang_unicode_decode_error_handling(self, fixture_file_info_extractor: IFileInfoExtractor): + with patch("builtins.open", side_effect=UnicodeDecodeError("utf-8", b"", 0, 1, "Invalid byte")): + assert fixture_file_info_extractor.get_shebang("/some/file") is None diff --git a/python_coderunner/tests/unit/project_info_extractor/test_interface_project_info_extractor.py b/python_coderunner/tests/unit/project_info_extractor/test_interface_project_info_extractor.py index 0b885cd..db6b954 100644 --- a/python_coderunner/tests/unit/project_info_extractor/test_interface_project_info_extractor.py +++ b/python_coderunner/tests/unit/project_info_extractor/test_interface_project_info_extractor.py @@ -4,53 +4,53 @@ from src.project_info_extractor import IProjectInfoExtractor -def test_get_all_files_filter_by_exts(fixture_project_info_extractor: IProjectInfoExtractor): - workspace_root = Path(fixture_project_info_extractor.get_workspace_root()) - - test_files = ( - workspace_root / "file_0.py", - workspace_root / "file_0.txt", - workspace_root / "file_1.py", - workspace_root / "dir_0" / "file_1.py", - workspace_root / "dir_0" / "py.py", - ) - - for file_path in test_files: - file_path.parent.mkdir(parents=True, exist_ok=True) - file_path.touch(exist_ok=True) - - exts: Set[str] = {".py", ".md"} - result = {Path(p) for p in fixture_project_info_extractor.get_all_files_filter_by_exts(exts)} - assert result == { - workspace_root / "file_0.py", - workspace_root / "file_1.py", - workspace_root / "dir_0" / "file_1.py", - workspace_root / "dir_0" / "py.py", - } - - -def test_get_all_files_filter_by_file_type(fixture_project_info_extractor: IProjectInfoExtractor): - workspace_root = Path(fixture_project_info_extractor.get_workspace_root()) - - test_files = ( - workspace_root / "file_0.py", - workspace_root / "file_0.cpp", - workspace_root / "file_0.txt", - workspace_root / "file_1.py", - workspace_root / "dir_0" / "file_1.py", - workspace_root / "dir_0" / "py.py", - ) - - for file_path in test_files: - file_path.parent.mkdir(parents=True, exist_ok=True) - file_path.touch(exist_ok=True) - - file_types: Set[str] = {"Python", "C++", "COBOL"} - result = {Path(p) for p in fixture_project_info_extractor.get_all_files_filter_by_file_type(file_types)} - assert result == { - workspace_root / "file_0.py", - workspace_root / "file_0.cpp", - workspace_root / "file_1.py", - workspace_root / "dir_0" / "file_1.py", - workspace_root / "dir_0" / "py.py", - } +class TestProjectInfoExtractorInterface: + def test_get_all_files_filter_by_exts(self, fixture_project_info_extractor: IProjectInfoExtractor): + workspace_root = Path(fixture_project_info_extractor.get_workspace_root()) + + test_files = ( + workspace_root / "file_0.py", + workspace_root / "file_0.txt", + workspace_root / "file_1.py", + workspace_root / "dir_0" / "file_1.py", + workspace_root / "dir_0" / "py.py", + ) + + for file_path in test_files: + file_path.parent.mkdir(parents=True, exist_ok=True) + file_path.touch(exist_ok=True) + + exts: Set[str] = {".py", ".md"} + result = {Path(p) for p in fixture_project_info_extractor.get_all_files_filter_by_exts(exts)} + assert result == { + workspace_root / "file_0.py", + workspace_root / "file_1.py", + workspace_root / "dir_0" / "file_1.py", + workspace_root / "dir_0" / "py.py", + } + + def test_get_all_files_filter_by_file_type(self, fixture_project_info_extractor: IProjectInfoExtractor): + workspace_root = Path(fixture_project_info_extractor.get_workspace_root()) + + test_files = ( + workspace_root / "file_0.py", + workspace_root / "file_0.cpp", + workspace_root / "file_0.txt", + workspace_root / "file_1.py", + workspace_root / "dir_0" / "file_1.py", + workspace_root / "dir_0" / "py.py", + ) + + for file_path in test_files: + file_path.parent.mkdir(parents=True, exist_ok=True) + file_path.touch(exist_ok=True) + + file_types: Set[str] = {"python", "cpp", "cobol"} + result = {Path(p) for p in fixture_project_info_extractor.get_all_files_filter_by_file_type(file_types)} + assert result == { + workspace_root / "file_0.py", + workspace_root / "file_0.cpp", + workspace_root / "file_1.py", + workspace_root / "dir_0" / "file_1.py", + workspace_root / "dir_0" / "py.py", + } diff --git a/python_coderunner/uv.lock b/python_coderunner/uv.lock index af22c61..52e0124 100644 --- a/python_coderunner/uv.lock +++ b/python_coderunner/uv.lock @@ -245,6 +245,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/51/ff/f6e8b8f39e08547faece4bd80f89d5a8de68a38b2d179cc1c4490ffa3286/pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8", size = 325287, upload_time = "2023-12-31T12:00:13.963Z" }, ] +[[package]] +name = "pytest-lazy-fixture" +version = "0.6.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b2/82/ae6d2f6903719c4ec410dcd31ee24e3bce74b2cef3c5b9150ad36e8594b6/pytest-lazy-fixture-0.6.3.tar.gz", hash = "sha256:0e7d0c7f74ba33e6e80905e9bfd81f9d15ef9a790de97993e34213deb5ad10ac", size = 7878, upload_time = "2020-02-01T18:04:02.321Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2d/a1/2f2c1c2353350d66c4d110d283e422e4943eb5ad10effa9357ba66f7b5b9/pytest_lazy_fixture-0.6.3-py3-none-any.whl", hash = "sha256:e0b379f38299ff27a653f03eaa69b08a6fd4484e46fd1c9907d984b9f9daeda6", size = 4948, upload_time = "2020-02-01T18:04:00.347Z" }, +] + [[package]] name = "pytest-mock" version = "3.14.1" @@ -351,6 +363,7 @@ dependencies = [ { name = "pre-commit" }, { name = "pylint" }, { name = "pytest" }, + { name = "pytest-lazy-fixture" }, { name = "pytest-mock" }, { name = "pyyaml" }, { name = "requests" }, @@ -364,6 +377,7 @@ requires-dist = [ { name = "pre-commit", specifier = ">=1.21.0" }, { name = "pylint", specifier = ">=2.6.2" }, { name = "pytest", specifier = ">=6.1.2" }, + { name = "pytest-lazy-fixture", specifier = ">=0.6.3" }, { name = "pytest-mock", specifier = ">=3.5.1" }, { name = "pyyaml", specifier = ">=5.4.1" }, { name = "requests", specifier = ">=2.27.1" }, From 203547ad8c023c280c19c0a718ab21ada17bb10d Mon Sep 17 00:00:00 2001 From: ZaharChernenko Date: Sun, 22 Jun 2025 20:08:35 +0300 Subject: [PATCH 2/2] fix: limitation of the pytest version due to lazy_fixture --- python_coderunner/pyproject.toml | 2 +- python_coderunner/uv.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/python_coderunner/pyproject.toml b/python_coderunner/pyproject.toml index 5f96eff..b6ce9ca 100644 --- a/python_coderunner/pyproject.toml +++ b/python_coderunner/pyproject.toml @@ -7,7 +7,7 @@ dependencies = [ "mypy>=0.910", "pre-commit>=1.21.0", "pylint>=2.6.2", - "pytest>=6.1.2", + "pytest>=6.1.2,<8.0.0", "pytest-lazy-fixture>=0.6.3", "pytest-mock>=3.5.1", "pyyaml>=5.4.1", diff --git a/python_coderunner/uv.lock b/python_coderunner/uv.lock index 52e0124..6575818 100644 --- a/python_coderunner/uv.lock +++ b/python_coderunner/uv.lock @@ -376,7 +376,7 @@ requires-dist = [ { name = "mypy", specifier = ">=0.910" }, { name = "pre-commit", specifier = ">=1.21.0" }, { name = "pylint", specifier = ">=2.6.2" }, - { name = "pytest", specifier = ">=6.1.2" }, + { name = "pytest", specifier = ">=6.1.2,<8.0.0" }, { name = "pytest-lazy-fixture", specifier = ">=0.6.3" }, { name = "pytest-mock", specifier = ">=3.5.1" }, { name = "pyyaml", specifier = ">=5.4.1" },