Commit graph

78 commits

Author SHA1 Message Date
Tony Liao
9d98477ca9 Optimize pure Python parse path for custom JSON enum names.
This implementation is similar to our Java idea in cl/949670944,
basically the idea is that we will have a local map (dictionary) that
will be populated the very first time we try to parse into an enum
field. The lifetime of this dictionary will be tied to the Parser
instance, similar to the Java implementation.

BENCHMARKS (baseline stats are recorded at cl/952170250):

Python:
```
[BENCHMARK] ParseJsonDefault:                med 10.06 us/op | p99 11.22 us/op | mean 10.10 ± 0.20 us/op
[BENCHMARK] ParseJsonCustom:                 med 20.33 us/op | p99 22.23 us/op | mean 20.40 ± 0.34 us/op
[BENCHMARK] ParseJsonUnknownIgnored:         med 24.21 us/op | p99 26.25 us/op | mean 24.31 ± 0.44 us/op
[BENCHMARK] ParseRepeatedJsonDefault:        med 1.94 us/item | p99 2.05 us/item | mean 1.95 ± 0.03 us/item
[BENCHMARK] ParseRepeatedJsonCustom:         med 2.26 us/item | p99 2.45 us/item | mean 2.27 ± 0.03 us/item
[BENCHMARK] ParseRepeatedJsonUnknownIgnored: med 5.31 us/item | p99 5.52 us/item | mean 5.32 ± 0.05 us/item
```

Cpp:
```
[BENCHMARK] ParseJsonDefault:                med 8.66 us/op | p99 9.21 us/op | mean 8.70 ± 0.13 us/op
[BENCHMARK] ParseJsonCustom:                 med 26.33 us/op | p99 29.71 us/op | mean 26.44 ± 0.69 us/op
[BENCHMARK] ParseJsonUnknownIgnored:         med 28.46 us/op | p99 31.80 us/op | mean 28.59 ± 0.67 us/op
[BENCHMARK] ParseRepeatedJsonDefault:        med 1.72 us/item | p99 2.19 us/item | mean 1.74 ± 0.06 us/item
[BENCHMARK] ParseRepeatedJsonCustom:         med 2.12 us/item | p99 2.38 us/item | mean 2.13 ± 0.04 us/item
[BENCHMARK] ParseRepeatedJsonUnknownIgnored: med 4.61 us/item | p99 4.92 us/item | mean 4.62 ± 0.07 us/item
```

UPB:
```
[BENCHMARK] ParseJsonDefault:                med 11.22 us/op | p99 12.29 us/op | mean 11.27 ± 0.21 us/op
[BENCHMARK] ParseJsonCustom:                 med 37.54 us/op | p99 39.25 us/op | mean 37.65 ± 0.46 us/op
[BENCHMARK] ParseJsonUnknownIgnored:         med 41.18 us/op | p99 48.45 us/op | mean 41.30 ± 0.89 us/op
[BENCHMARK] ParseRepeatedJsonDefault:        med 2.10 us/item | p99 2.19 us/item | mean 2.10 ± 0.02 us/item
[BENCHMARK] ParseRepeatedJsonCustom:         med 2.58 us/item | p99 2.73 us/item | mean 2.59 ± 0.03 us/item
[BENCHMARK] ParseRepeatedJsonUnknownIgnored: med 5.70 us/item | p99 5.90 us/item | mean 5.71 ± 0.04 us/item
```

Seeing these benchmarks, we can see a very noticeable improvement in
`ParseRepeatedJsonCustom` and `ParseRepeatedJsonUnknownIgnored` cases --
which correspond to the pathological cases that we are worried about.
Singular fields are taking longer to parse in our microbenchmark because
the cache that we've added is tied to each Parser instance and must be
re-instantiated across every `Parse` call.

PiperOrigin-RevId: 970528683
2026-08-25 07:09:41 -07:00
Tony Liao
f9f94388a3 Additional unit tests for Python handling of custom JSON enum names.
The unit tests added in the initial implementation in cl/925012500 and
cl/922729754 were fairly minimal. I took some inspiration from our C++
and Java unit tests for JSON's custom enum name parsing to come up with
these new test cases.

This change also adds a python Benchmark that we can use to verify that
our optimizations in cl/952170251 actually works.

Benchmark results across all three proto implementations (cpp, python, upb):

=== C++ Extension Protos (json_format_benchmark_cpp_protos) ===
[BENCHMARK] ParseJsonDefault:                med 8.70 us/op | p99 9.56 us/op | mean 8.74 ± 0.15 us/op
[BENCHMARK] ParseJsonCustom:                 med 17.39 us/op | p99 21.64 us/op | mean 17.43 ± 0.51 us/op
[BENCHMARK] ParseJsonUnknownIgnored:         med 26.97 us/op | p99 32.04 us/op | mean 27.06 ± 0.65 us/op
[BENCHMARK] ParseRepeatedJsonDefault:        med 1.71 us/item | p99 1.83 us/item | mean 1.71 ± 0.02 us/item
[BENCHMARK] ParseRepeatedJsonCustom:         med 9.18 us/item | p99 9.88 us/item | mean 9.20 ± 0.11 us/item
[BENCHMARK] ParseRepeatedJsonUnknownIgnored: med 18.79 us/item | p99 19.19 us/item | mean 18.81 ± 0.13 us/item

=== UPB Extension Protos (json_format_benchmark_upb_protos) ===
[BENCHMARK] ParseJsonDefault:                med 8.70 us/op | p99 9.56 us/op | mean 8.74 ± 0.15 us/op
[BENCHMARK] ParseJsonCustom:                 med 17.39 us/op | p99 21.64 us/op | mean 17.43 ± 0.51 us/op
[BENCHMARK] ParseJsonUnknownIgnored:         med 26.97 us/op | p99 32.04 us/op | mean 27.06 ± 0.65 us/op
[BENCHMARK] ParseRepeatedJsonDefault:        med 1.71 us/item | p99 1.83 us/item | mean 1.71 ± 0.02 us/item
[BENCHMARK] ParseRepeatedJsonCustom:         med 9.18 us/item | p99 9.88 us/item | mean 9.20 ± 0.11 us/item
[BENCHMARK] ParseRepeatedJsonUnknownIgnored: med 18.79 us/item | p99 19.19 us/item | mean 18.81 ± 0.13 us/item

=== Pure Python Protos (json_format_benchmark_python_protos) ===
[BENCHMARK] ParseJsonDefault:                med 10.26 us/op | p99 11.20 us/op | mean 10.30 ± 0.25 us/op
[BENCHMARK] ParseJsonCustom:                 med 16.59 us/op | p99 18.35 us/op | mean 16.70 ± 0.45 us/op
[BENCHMARK] ParseJsonUnknownIgnored:         med 25.09 us/op | p99 28.22 us/op | mean 25.27 ± 0.65 us/op
[BENCHMARK] ParseRepeatedJsonDefault:        med 1.94 us/item | p99 3.25 us/item | mean 1.97 ± 0.14 us/item
[BENCHMARK] ParseRepeatedJsonCustom:         med 7.16 us/item | p99 10.35 us/item | mean 7.24 ± 0.39 us/item
[BENCHMARK] ParseRepeatedJsonUnknownIgnored: med 15.43 us/item | p99 19.54 us/item | mean 15.54 ± 0.49 us/item

PiperOrigin-RevId: 966820541
2026-08-18 14:57:30 -07:00
Tony Liao
cb440834fd Document Python & PHP non-conformance where booleans are accepted by int and float fields.
PiperOrigin-RevId: 952905948
2026-07-23 12:51:55 -07:00
Hong Shin
7800571253 json/python: add support for custom json strings for enum values
PiperOrigin-RevId: 922729754
2026-05-28 06:17:20 -07:00
Protobuf Team Bot
cfcc4337ae Py JSON: Fix issue with depth enforcement on Struct/Value WKT paths.
This just reroutes these calls back through the ConvertMessage path. This will have some trivial extra overhead but will necessarily routes back to the _ConvertStructMessage etc cases after enforcing a depth cap.

Fixes https://github.com/protocolbuffers/protobuf/issues/26736

Fixes https://github.com/protocolbuffers/protobuf/issues/26432

PiperOrigin-RevId: 903387883
2026-04-21 13:13:45 -07:00
Protobuf Team Bot
f9821fcf4f Fix python SyntaxWarning
\( is not a valid escape sequence.

PiperOrigin-RevId: 895607647
2026-04-06 19:05:19 -07:00
Aviral Garg
d2b001626d Fix Any recursion depth bypass in Python json_format.ParseDict (#25239)
This fixes a security vulnerability where nested google.protobuf.Any messages could bypass the max_recursion_depth limit, potentially leading to denial of service via stack overflow.

The root cause was that _ConvertAnyMessage() was calling itself recursively via methodcaller() for nested well-known types, bypassing the recursion depth tracking in ConvertMessage().

The fix routes well-known type parsing through ConvertMessage() to ensure proper recursion depth accounting for all message types including nested Any.

Fixes #25070

Closes #25239

COPYBARA_INTEGRATE_REVIEW=https://github.com/protocolbuffers/protobuf/pull/25239 from aviralgarg05:fix-any-recursion-depth-bypass 3cbbcbea142593d3afd2ceba2db14b05660f62f4
PiperOrigin-RevId: 862740421
2026-01-29 10:17:18 -08:00
Jie Luo
8e3543177e Add Python 3.14 test coverage
#test-continuous

PiperOrigin-RevId: 829211258
2025-11-06 19:21:19 -08:00
Jie Luo
f027f1fcd5 Breaking change: Remove float_precision from python proto json_format
PiperOrigin-RevId: 826158770
2025-10-30 13:20:37 -07:00
Protobuf Team Bot
07ef6769c6 Fix handling of repeated extension fields in PyProto JSON
Both serialize and parse path would not work on repeated extensions before these fixes.

https://github.com/protocolbuffers/protobuf/issues/22989

PiperOrigin-RevId: 803542611
2025-09-05 11:46:02 -07:00
Adam Cozzette
0d42a969e3 Replace protobuf_unittest package with proto2_unittest
For historical reasons, our internal copy of the codebase uses
`proto2_unittest` while the open source one uses `protobuf_unittest`. This adds
a lot of friction to syncing between the two, and there's no great reason to
keep maintaining this difference, so let's just go with `proto2_unittest`.

PiperOrigin-RevId: 721951543
2025-01-31 17:06:33 -08:00
atulmerchia
d59047a071 Make ParseDict nondestructive for Any (#20111)
Found this bug where if `_ConvertFieldValuePair` fails while parsing an
Any from a dictionary value then the `@type` field gets removed from
the original object. Adding a simple `try/finally` to ensure we always
restore the original object.

Closes #20111

COPYBARA_INTEGRATE_REVIEW=https://github.com/protocolbuffers/protobuf/pull/20111 from atulmerchia:fix-destructive-anyparser 58aea1911f
PiperOrigin-RevId: 721850901
2025-01-31 11:55:18 -08:00
Mike Kruskal
f2ec951597 Fix python JSON parser to accept string fields containing integer-equivalent floats.
This fixes non-conformant behavior in python.

PiperOrigin-RevId: 704529468
2024-12-09 20:53:41 -08:00
Protobuf Team Bot
75bb72e0d5 Support tuples when parsing JSON dicts
PiperOrigin-RevId: 696911739
2024-11-15 09:30:34 -08:00
Nathan Baulch
e4cbc79ab4 Fix minor typos (#17682)
Just thought I'd contribute some typo fixes that keep tripping up CI/CD checks in my projects. Nothing controversial (hopefully), just 174 simple fixes.

Use the following command to get a quick and dirty summary of the specific corrections made:
```shell
git diff HEAD^! --word-diff-regex='\w+' -U0 \
  | grep -E '\[\-.*\-\]\{\+.*\+\}' \
  | sed -r 's/.*\[\-(.*)\-\]\{\+(.*)\+\}.*/\1 \2/' \
  | sort | uniq -c | sort -n
```

FWIW, the top typos are:
* trimed → trimmed (37)
* substract → subtract (7)
* qualifed → qualified (7)
* extesion → extension (6)
* mising → missing (5)
* btye → byte (4)
* likey → likely (4)
* candicate → candidate (3)
* decriptor → descriptor (3)
* inherting → inheriting (3)
* colletion → collection (3)
* caluclated → calculated (3)
* unititialized → uninitialized (3)
* implemting → implementing (3)
* binrary → binary (3)
* descripor → descriptor (3)
* negitive → negative (3)

Closes #17682

COPYBARA_INTEGRATE_REVIEW=https://github.com/protocolbuffers/protobuf/pull/17682 from NathanBaulch:typos d41762d137
PiperOrigin-RevId: 677074418
2024-09-20 20:50:06 -07:00
Protobuf Team Bot
5a224554eb Add test that tries to parse a JSON string that exceeds the recursion limit.
PiperOrigin-RevId: 663249255
2024-08-15 03:41:17 -07:00
Jie Luo
9cc5be12ed Catch all the exceptions in python JSON ParseDict and raise
json_format.ParseError

PiperOrigin-RevId: 627794381
2024-04-24 11:34:41 -07:00
Anton Grbin
86abf35ef5 Python JSON parser: Ignore invalid enum string values if ignore_unknown_fields is set (#15887)
# Motivation

This PR fixes failing conformance tests for python with name `IgnoreUnknownEnumStringValue`.

The JSON parsing spec was discussed in https://github.com/protocolbuffers/protobuf/issues/7392.

Recent equivalent changes for other languages:
* Swift: https://github.com/apple/swift-protobuf/pull/1345
* C#: https://github.com/protocolbuffers/protobuf/pull/15758

# Changes

- 1st commit is a noop  refactoring to make relevant _ConvertScalarFieldValue invocations localized
- 2nd commit introduces the child exception of `ParseError` named `EnumStringValueParseError` which is suppressed if `ignore_unknown_fields` is set
- 3rd commit updates the conformance test failure lists

Closes #15887

COPYBARA_INTEGRATE_REVIEW=https://github.com/protocolbuffers/protobuf/pull/15887 from noom:anton/7392/fix-python-test fbcc93a232
PiperOrigin-RevId: 619288323
2024-03-26 13:22:12 -07:00
Protobuf Team Bot
2699579875 Breaking change: Remove the deprecated always_print_primitive_fields option from Java, Python and C++ JSON parsers.
The replacement always_print_without_presence_fields should be used instead, which is very similar but has consistent handling of optional fields by not affecting them.

PiperOrigin-RevId: 604381178
2024-02-05 11:35:16 -08:00
Protobuf Team Bot
7d43131a0a Rename the 'includingDefaultValueWithoutPresenceFields' and 'always_print_without_presence_fields' to 'alwaysPrintFieldsWithNoPresence' in the Cpp, Py and Java JSON serializers for consistency.
PiperOrigin-RevId: 604292220
2024-02-05 05:53:59 -08:00
Protobuf Team Bot
461b50b180 Stop using including_default_value_fields as a positional argument in json_format_test.py
PiperOrigin-RevId: 603713528
2024-02-02 09:57:56 -08:00
Protobuf Team Bot
25c6d34d4e Add new including_default_value_without_presence_fields to Python JSON serializer.
This flag has consistent behavior between proto2 and proto3 optionals (by not including either one), unlike including_default_value_fields which does include proto2 optional but excludes proto3 optionals.

including_default_value_fields is now deprecated and will be removed in an upcoming release.

PiperOrigin-RevId: 603156447
2024-01-31 14:16:54 -08:00
Jie Luo
4f77929203 BREAKING CHANGE in v26: check if Timestamp is valid.
Seconds should be in range [-62135596800, 253402300799]
Nanos should be in range [0, 999999999]

PiperOrigin-RevId: 594119545
2023-12-27 16:38:51 -08:00
Jie Luo
706c0d03f8 Automated rollback of commit 1250d5f6cc.
PiperOrigin-RevId: 592707509
2023-12-20 18:21:25 -08:00
Jie Luo
1250d5f6cc BREAKING CHANGE in v26: check if Timestamp is valid.
Seconds should be in range [-62135596800, 253402300799]
Nanos should be in range [0, 999999999]

PiperOrigin-RevId: 592365636
2023-12-19 15:49:41 -08:00
Jie Luo
41af1d53a2 loosen upb for json name conflict check in proto2 between json name and field
name. Once editions is supported this check should turn into a check on LEGACY_BEST_EFFORT

PiperOrigin-RevId: 572041162
2023-10-09 14:31:17 -07:00
Joshua Haberman
9ef8f5cffc Shorten our license headers into an abbreviated form that references LICENSE instead of including it in full.
PiperOrigin-RevId: 563897888
2023-09-08 18:50:27 -07:00
Karthikeyan Singaravelan
decc1b5708 Update deprecated aliases for Python 3.12 compatibility. (#12254)
https://docs.python.org/3.12/whatsnew/3.12.html#removed . `assertRaisesRegexp` was removed in favor of `assertRaisesRegex`

Closes #12254

COPYBARA_INTEGRATE_REVIEW=https://github.com/protocolbuffers/protobuf/pull/12254 from tirkarthi:fix-assert 922f3ceccc
PiperOrigin-RevId: 518181080
2023-03-20 23:17:40 -07:00
Jie Luo
883ec1c3ef Raise errors when serialize inf and nan for Value.number_value in json format. fixes #11259
PiperOrigin-RevId: 495976996
2022-12-16 15:42:57 -08:00
Mike Kruskal
e19c2606b3 Internal changes
PiperOrigin-RevId: 487447243
2022-11-09 23:45:35 -08:00
Mike Kruskal
0eb9504485 Internal change
PiperOrigin-RevId: 485179950
2022-10-31 16:47:12 -07:00
David L. Jones
1ba1d73e0d Sync from Piper @429333699
PROTOBUF_SYNC_PIPER
2022-02-17 09:53:51 -08:00
Deanna Garcia
fc9fb726f1 Fix python and kotlin tests 2022-02-02 01:02:08 +00:00
Deanna Garcia
b7fe12e367 Syncing from internal 2022-02-01 21:30:49 +00:00
Deanna Garcia
ab4585a695 Sync from Piper @425656941
PROTOBUF_SYNC_PIPER
2022-02-01 18:24:53 +00:00
Adam Cozzette
454778d011
Python: fix usages of deprecated assertRaisesRegxp (#9098)
assertRaisesRegexp has been deprecated in favor of the slightly
different spelling assertRaisesRegex. Let's fix this just to clean up a
bunch of deprecation warnings in the tests.
2021-10-15 19:00:04 -07:00
Joshua Haberman
e5c570bb57 Sync from Piper @395706834
PROTOBUF_SYNC_PIPER
2021-09-09 08:21:42 -07:00
Adam Cozzette
4eed0dab5a Sync from Piper @392076391
PROTOBUF_SYNC_PIPER
2021-08-20 15:05:53 -07:00
Ilya Konstantinov
991bcada05
Raise ParseError when JSON string is expected and not given (#7935)
Raise ParseError when JSON string is expected and not given
2021-06-27 06:57:47 +08:00
Joshua Haberman
32a94ce353 Merge tag 'refs/tags/sync-piper' into sync-stage
# Conflicts:
#	csharp/src/Google.Protobuf.Test/testprotos.pb
2020-08-17 15:26:33 -07:00
Peter Newman
e2cc2de304
Fix lots of spelling errors (#7751)
* Fix a typo

* Fix lots of spelling errors

* Fix a few more spelling mistakes

* s/parsable/parseable/

* Don't touch the third party files

* Cloneable is the preferred C# term

* Copyable is the preferred C++ term

* Revert "s/parsable/parseable/"

This reverts commit 534ecf7675.

* Revert unparseable->unparsable corrections
2020-08-10 11:08:25 -07:00
Joshua Haberman
dfab275eca Sync from Piper @317197168
PROTOBUF_SYNC_PIPER
2020-06-18 15:47:49 -07:00
Joshua Haberman
f77065d4eb Sync from Piper @316511779
PROTOBUF_SYNC_PIPER
2020-06-15 11:48:47 -07:00
Joshua Haberman
183cdec7cd Sync from Piper @314226556
PROTOBUF_SYNC_PIPER
2020-06-01 16:32:31 -07:00
Joshua Haberman
0a737d8eb0 Sync from Piper @313142438
PROTOBUF_SYNC_PIPER
2020-05-26 00:26:15 -07:00
Joshua Haberman
c05b55880f Sync from Piper @309848308
PROTOBUF_SYNC_PIPER
2020-05-04 17:10:15 -07:00
Joshua Haberman
b7742c51fd Sync from Piper @305505267
PROTOBUF_SYNC_PIPER
2020-04-08 10:30:17 -07:00
Joshua Haberman
503a2116aa Sync from Piper @305053102
PROTOBUF_SYNC_PIPER
2020-04-06 09:57:03 -07:00
Joshua Haberman
c649397029
Set execute bit on files if and only if they begin with (#!). (#7347)
* Set execute bit on files if and only if they begin with (#!).

Git only tracks the 'x' (executable) bit on each file. Prior to this
CL, our files were a random mix of executable and non-executable.
This change imposes some order by making files executable if and only
if they have shebang (#!) lines at the beginning.

We don't have any executable binaries checked into the repo, so
we shouldn't need to worry about that case.

* Added fix_permissions.sh script to set +x iff a file begins with (#!).
2020-04-01 15:28:25 -07:00
Joshua Haberman
b99994d994 Sync from Piper @304070343
PROTOBUF_SYNC_PIPER
2020-03-31 16:25:37 -07:00