Skip to content

Commit 39014d0

Browse files
authored
Merge pull request #7 from kraken-tech/strict-mode
Strict mode
2 parents 3dc1946 + 80f482d commit 39014d0

File tree

5 files changed

+47
-19
lines changed

5 files changed

+47
-19
lines changed

README.md

+6-5
Original file line numberDiff line numberDiff line change
@@ -67,15 +67,16 @@ bundle = rustfluent.Bundle(
6767

6868
#### Parameters
6969

70-
| Name | Type | Description |
71-
|-------------|-------------|-------------------------------------------------------------------------------------------------------------------------|
72-
| `language` | `str` | [Unicode Language Identifier](https://unicode.org/reports/tr35/tr35.html#Unicode_language_identifier) for the language. |
73-
| `ftl_files` | `list[str]` | Full paths to the FTL files containing the translations. Entries in later files overwrite earlier ones. |
70+
| Name | Type | Description |
71+
|-------------|------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
72+
| `language` | `str` | [Unicode Language Identifier](https://unicode.org/reports/tr35/tr35.html#Unicode_language_identifier) for the language. |
73+
| `ftl_files` | `list[str]` | Full paths to the FTL files containing the translations. Entries in later files overwrite earlier ones. |
74+
| `strict` | `bool`, optional | In strict mode, a `ParserError` will be raised if there are any errors in the file. In non-strict mode, invalid Fluent messages will be excluded from the Bundle. |
7475

7576
#### Raises
7677

7778
- `FileNotFoundError` if any of the FTL files could not be found.
78-
- `ValueError` if any of the FTL files contain errors.
79+
- `rustfluent.ParserError` if any of the FTL files contain errors (strict mode only).
7980

8081
### `Bundle.get_translation`
8182

src/lib.rs

+20-8
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,14 @@ use pyo3::types::{PyDict, PyList};
77
use std::fs;
88
use unic_langid::LanguageIdentifier;
99

10+
use pyo3::create_exception;
11+
12+
create_exception!(rustfluent, PyParserError, pyo3::exceptions::PyException);
13+
1014
#[pymodule]
1115
fn rustfluent(m: &Bound<'_, PyModule>) -> PyResult<()> {
1216
m.add_class::<Bundle>()?;
17+
m.add("ParserError", m.py().get_type_bound::<PyParserError>())?;
1318
Ok(())
1419
}
1520

@@ -21,7 +26,8 @@ struct Bundle {
2126
#[pymethods]
2227
impl Bundle {
2328
#[new]
24-
fn new(language: &str, ftl_filenames: &'_ Bound<'_, PyList>) -> PyResult<Self> {
29+
#[pyo3(signature = (language, ftl_filenames, strict=false))]
30+
fn new(language: &str, ftl_filenames: &'_ Bound<'_, PyList>, strict: bool) -> PyResult<Self> {
2531
let langid: LanguageIdentifier = language.parse().expect("Parsing failed");
2632
let mut bundle = FluentBundle::new_concurrent(vec![langid]);
2733

@@ -32,16 +38,22 @@ impl Bundle {
3238
Err(_) => return Err(PyFileNotFoundError::new_err(file_path.to_string())),
3339
};
3440

35-
let res = match FluentResource::try_new(contents) {
36-
Ok(res) => res,
41+
let resource = match FluentResource::try_new(contents) {
42+
Ok(resource) => resource,
3743
Err(error) => {
38-
return Err(PyValueError::new_err(format!(
39-
"{error:?} - Fluent file contains errors"
40-
)))
44+
if strict {
45+
return Err(PyParserError::new_err(format!(
46+
"Error when parsing {}.",
47+
file_path
48+
)));
49+
} else {
50+
// The first element of the error is the parsed resource, minus any
51+
// invalid messages.
52+
error.0
53+
}
4154
}
4255
};
43-
44-
bundle.add_resource_overriding(res);
56+
bundle.add_resource_overriding(resource);
4557
}
4658

4759
Ok(Self { bundle })

src/rustfluent.pyi

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
class Bundle:
2-
def __init__(self, language: str, ftl_filenames: list[str]) -> None: ...
2+
def __init__(self, language: str, ftl_filenames: list[str], strict: bool = False) -> None: ...
33
def get_translation(self, identifier: str, variables: dict[str, str] | None = None) -> str: ...

tests/data/errors.ftl

+3-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1-
bobbins
1+
invalid-message
2+
3+
valid-message = I'm valid.

tests/test_python_interface.py

+17-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#!/usr/bin/env python
2-
32
import pathlib
3+
import re
44

55
import pytest
66

@@ -66,6 +66,19 @@ def test_file_not_found():
6666
fluent.Bundle("fr", [str(data_dir / "none.ftl")])
6767

6868

69-
def test_file_has_errors():
70-
with pytest.raises(ValueError):
71-
fluent.Bundle("fr", [str(data_dir / "errors.ftl")])
69+
@pytest.mark.parametrize("pass_strict_argument_explicitly", (True, False))
70+
def test_parses_other_parts_of_file_that_contains_errors_in_non_strict_mode(
71+
pass_strict_argument_explicitly,
72+
):
73+
kwargs = dict(strict=False) if pass_strict_argument_explicitly else {}
74+
75+
bundle = fluent.Bundle("fr", [str(data_dir / "errors.ftl")], **kwargs)
76+
translation = bundle.get_translation("valid-message")
77+
78+
assert translation == "I'm valid."
79+
80+
81+
def test_raises_parser_error_on_file_that_contains_errors_in_strict_mode():
82+
filename = str(data_dir / "errors.ftl")
83+
with pytest.raises(fluent.ParserError, match=re.escape(f"Error when parsing {filename}.")):
84+
fluent.Bundle("fr", [filename], strict=True)

0 commit comments

Comments
 (0)