Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
_LEADING_ALPHA_INVALID = re.compile(r"^[_a-zA-Z][_a-zA-Z0-9]*[~*/\[\]]")
PATH_ELEMENT_TOKENS = [
("SIMPLE", r"[_a-zA-Z][_a-zA-Z0-9]*"), # unquoted elements
("QUOTED", r"`(?:\\`|[^`])*?`"), # quoted elements, unquoted
("QUOTED", r"`(?:\\.|[^`\\])*`"), # quoted elements, unquoted

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The regex pattern uses \. to match escaped characters. However, in Python, the . character does not match newlines (\n) by default unless the re.DOTALL flag is active. If a field path contains an escaped newline, this pattern will fail to match.

To ensure robustness and handle any escaped character (including newlines) regardless of compilation flags, use \\[\s\S] instead of \. .

Suggested change
("QUOTED", r"`(?:\\.|[^`\\])*`"), # quoted elements, unquoted
("QUOTED", r"`(?:\\[\\s\\S]|[^`\\])*`"), # quoted elements, unquoted

("DOT", r"\."), # separator
]
TOKENS_PATTERN = "|".join("(?P<{}>{})".format(*pair) for pair in PATH_ELEMENT_TOKENS)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ def test__tokenize_field_path_w_dotted_escaped():
_expect_tokenize_field_path("_0.`1`.`+2`", ["_0", ".", "`1`", ".", "`+2`"])


def test__tokenize_field_path_w_quoted_trailing_escaped_backslash():
_expect_tokenize_field_path(r"`a\\`.`b c`", [r"`a\\`", ".", "`b c`"])


def test__tokenize_field_path_w_unconsumed_characters():
from google.cloud.firestore_v1 import field_path

Expand Down Expand Up @@ -150,6 +154,12 @@ def test_parse_field_path_w_escaped_backslash():
assert field_path.parse_field_path("`a\\\\b`.c.d") == ["a\\b", "c", "d"]


def test_parse_field_path_w_trailing_escaped_backslash():
from google.cloud.firestore_v1 import field_path

assert field_path.parse_field_path("`a\\\\`.`b c`") == ["a\\", "b c"]


def test_parse_field_path_w_first_name_escaped_wo_closing_backtick():
from google.cloud.firestore_v1 import field_path

Expand Down Expand Up @@ -451,6 +461,14 @@ def test_fieldpath_from_string_w_non_ascii_dotted():
assert field_path.parts == ("a", "一")


def test_fieldpath_from_string_w_trailing_escaped_backslash():
from google.cloud.firestore_v1 import field_path

path_string = field_path.FieldPath("a\\", "b c").to_api_repr()
field_path = field_path.FieldPath.from_string(path_string)
assert field_path.parts == ("a\\", "b c")
Comment on lines +468 to +469

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Reassigning the variable field_path shadows the imported field_path module within the function scope. This makes the code fragile and harder to maintain or extend if other module-level attributes or classes need to be accessed later in the test.

Consider renaming the variable to parsed_path to avoid shadowing.

Suggested change
field_path = field_path.FieldPath.from_string(path_string)
assert field_path.parts == ("a\\", "b c")
parsed_path = field_path.FieldPath.from_string(path_string)
assert parsed_path.parts == ('a\\\\', 'b c')



def test_fieldpath___hash___w_single_part():
field_path = _make_field_path("a")
assert hash(field_path) == hash("a")
Expand Down
Loading