diff --git a/CHANGELOG.md b/CHANGELOG.md index eb80f1dad..9e3b19fdb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Workaround for edge case of object from Faiss with no `__class__` https://github.com/Textualize/rich/issues/1838 - Add `Syntax.guess_lexer`, add support for more lexers (e.g. Django templates etc.) https://github.com/Textualize/rich/pull/1869 +- Add `lexer` parameter to `Syntax.from_path` to allow for overrides https://github.com/Textualize/rich/pull/1873 - Ensure `Syntax` always justifies left https://github.com/Textualize/rich/pull/1872 diff --git a/rich/syntax.py b/rich/syntax.py index 380710ba7..142f48f46 100644 --- a/rich/syntax.py +++ b/rich/syntax.py @@ -266,6 +266,7 @@ def from_path( cls, path: str, encoding: str = "utf-8", + lexer: Optional[Union[Lexer, str]] = None, theme: Union[str, SyntaxTheme] = DEFAULT_THEME, dedent: bool = False, line_numbers: bool = False, @@ -283,6 +284,7 @@ def from_path( Args: path (str): Path to file to highlight. encoding (str): Encoding of file. + lexer (str | Lexer, optional): Lexer to use. If None, lexer will be auto-detected from path/file content. theme (str, optional): Color theme, aka Pygments style (see https://pygments.org/docs/styles/#getting-a-list-of-available-styles). Defaults to "emacs". dedent (bool, optional): Enable stripping of initial whitespace. Defaults to True. line_numbers (bool, optional): Enable rendering of line numbers. Defaults to False. @@ -301,11 +303,12 @@ def from_path( with open(path, "rt", encoding=encoding) as code_file: code = code_file.read() - lexer_name = cls.guess_lexer(path, code=code) + if not lexer: + lexer = cls.guess_lexer(path, code=code) return cls( code, - lexer_name, + lexer, theme=theme, dedent=dedent, line_numbers=line_numbers, @@ -756,6 +759,7 @@ def __rich_console__( else: syntax = Syntax.from_path( args.path, + lexer=args.lexer_name, line_numbers=args.line_numbers, word_wrap=args.word_wrap, theme=args.theme, diff --git a/tests/test_syntax.py b/tests/test_syntax.py index a8ac05b6b..46d0126e1 100644 --- a/tests/test_syntax.py +++ b/tests/test_syntax.py @@ -241,8 +241,13 @@ def test_ansi_theme(): assert theme.get_background_style() == Style() -@pytest.mark.skipif(sys.platform == "win32", reason="permissions error on Windows") -def test_from_file(): +skip_windows_permission_error = pytest.mark.skipif( + sys.platform == "win32", reason="permissions error on Windows" +) + + +@skip_windows_permission_error +def test_from_path(): fh, path = tempfile.mkstemp("example.py") try: os.write(fh, b"import this\n") @@ -254,8 +259,8 @@ def test_from_file(): os.remove(path) -@pytest.mark.skipif(sys.platform == "win32", reason="permissions error on Windows") -def test_from_file_unknown_lexer(): +@skip_windows_permission_error +def test_from_path_unknown_lexer(): fh, path = tempfile.mkstemp("example.nosuchtype") try: os.write(fh, b"import this\n") @@ -266,6 +271,30 @@ def test_from_file_unknown_lexer(): os.remove(path) +@skip_windows_permission_error +def test_from_path_lexer_override(): + fh, path = tempfile.mkstemp("example.nosuchtype") + try: + os.write(fh, b"import this\n") + syntax = Syntax.from_path(path, lexer="rust") + assert syntax.lexer.name is "Rust" + assert syntax.code == "import this\n" + finally: + os.remove(path) + + +@skip_windows_permission_error +def test_from_path_lexer_override_invalid_lexer(): + fh, path = tempfile.mkstemp("example.nosuchtype") + try: + os.write(fh, b"import this\n") + syntax = Syntax.from_path(path, lexer="blah") + assert syntax.lexer is None + assert syntax.code == "import this\n" + finally: + os.remove(path) + + def test_syntax_guess_lexer(): assert Syntax.guess_lexer("banana.py") == "python" assert Syntax.guess_lexer("banana.py", "import this") == "python"