Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow override of lexer in Syntax.from_path #1873

Merged
merged 4 commits into from Jan 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -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


Expand Down
8 changes: 6 additions & 2 deletions rich/syntax.py
Expand Up @@ -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,
Expand All @@ -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.
Expand All @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
37 changes: 33 additions & 4 deletions tests/test_syntax.py
Expand Up @@ -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")
Expand All @@ -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")
Expand All @@ -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"
Expand Down