Compare commits

...

250 commits
0.5.1 ... main

Author SHA1 Message Date
Inada Naoki
f9806368ae
update cython and cibuildwheel (#658) 2025-12-01 14:16:03 +09:00
Inada Naoki
c1ecd23dbf
drop Python 3.9 (#656) 2025-10-09 18:33:23 +09:00
Inada Naoki
af45640970
cython: freethreading_compatible (#654)
```
$ v3/bin/python -VV
Python 3.14.0 free-threading build (main, Oct  7 2025, 15:35:12) [Clang 20.1.4 ]

$ v3/bin/python -c 'import sys,msgpack; print(sys._is_gil_enabled())'
False
```
2025-10-09 15:53:08 +09:00
Inada Naoki
c2546eabc4
update setuptools requirements to >=78.1.1 (#653)
https://github.com/advisories/GHSA-5rjg-fvgr-3xxf
2025-10-09 15:28:01 +09:00
Inada Naoki
ef4f83df16
relax setuptools version (#652) 2025-10-09 13:00:46 +09:00
Inada Naoki
19b5d33ded
release v1.1.2 (#649) 2025-10-08 17:56:20 +09:00
TW
0f3c4be465
README: fix typos and grammar (#648) 2025-10-08 16:10:46 +09:00
MS-GITS
c2a9f1fda5
ci: add support for building windows on arm wheels (#643) 2025-09-26 13:17:17 +09:00
Inada Naoki
d9873dab04
ci: update cibuildwheel and drop Python 3.8 (#642) 2025-07-26 10:59:05 +09:00
Inada Naoki
42f056f3cf v1.1.1 2025-06-13 15:41:08 +09:00
Inada Naoki
e6445d3b92 v1.1.1rc1 2025-06-06 09:56:15 +09:00
Inada Naoki
fe9e620a60
upload to PyPI on create a release (#639) 2025-06-02 14:46:53 +09:00
Inada Naoki
cdc7644503
update cibuildwheel to v2.23.3 (#638) 2025-06-01 16:56:44 +09:00
Inada Naoki
868aa2cd83
update Cython to 3.1.1 (#637) 2025-05-31 12:45:06 +09:00
Edgar Ramírez Mondragón
0eeabfb453
Add Python 3.13 trove classifier (#626) 2024-10-08 18:04:56 +09:00
Inada Naoki
4587393b1a
release v1.1.0 (#622) 2024-09-10 01:58:00 +09:00
Thomas A Caswell
20a2b8eaa2
use PyLong_* instead of PyInt_* (#620)
9af421163cb8081414be347038dee7a82b29e8dd in Cython removed back-compatibility `#define`.
2024-08-21 14:56:00 +09:00
Inada Naoki
9d0c7f2f9c
Release v1.1.0rc2 (#619) 2024-08-19 20:36:26 +09:00
Inada Naoki
9e26d80ab2
update cibuildwheel to 2.20.0 (#618) 2024-08-19 17:56:01 +09:00
Inada Naoki
6e11368f5d
update Cython to 3.0.11 (#617) 2024-08-19 17:35:16 +09:00
Inada Naoki
0b1c47b06b
do not install cython as build dependency (#610)
User can not cythonize during `pip install msgpack`.
So remove cython from build dependency.

If user need to use another Cython, user should download sdist, unzip,
manually cythonize, and `pip install .`.
2024-05-07 22:01:54 +09:00
Inada Naoki
9cea8b6da2
Release v1.1.0rc1 (#609) 2024-05-07 20:49:23 +09:00
Inada Naoki
33e0e86f4e
Cleanup code and pyproject (#608)
* use isort
* fallback: use BytesIO instead of StringIO. We had dropped Python 2
already.
2024-05-06 11:46:31 +09:00
Inada Naoki
e0f0e145f1
better error checks (#607)
* check buffer exports
* add error messages
2024-05-06 03:33:48 +09:00
Inada Naoki
e1068087e0
cython: better exception handling (#606)
- use `except -1` instead of manual error handling
- use `PyUnicode_AsUTF8AndSize()`
- use `_pack()` and `_pack_inner()` instead of `while True:`
2024-05-06 02:13:12 +09:00
Inada Naoki
3da5818a3a
update readme (#605) 2024-05-06 02:12:46 +09:00
Inada Naoki
72e65feb0e
packer: add buf_size option (#604)
And change the default buffer size to 256KiB.

Signed-off-by: Rodrigo Tobar <rtobar@icrar.org>
Co-authored-by: Rodrigo Tobar <rtobar@icrar.org>
2024-05-06 00:49:12 +09:00
Inada Naoki
bf2413f915 ignore msgpack/*.c 2024-05-06 00:30:07 +09:00
Inada Naoki
a97b31437d
Remove unused code (#603) 2024-05-06 00:13:59 +09:00
Inada Naoki
52f8bc2e55
implement buffer protocol (#602)
Fix #479
2024-05-05 23:14:27 +09:00
Inada Naoki
526ec9c923
update cibuildwheel to 2.17 (#601) 2024-05-04 16:49:22 +09:00
Inada Naoki
b389ccf2f7
update README (#561) 2024-05-04 16:10:37 +09:00
Inada Naoki
3e9a2a7419
Stop using c++ (#600)
Python 3.13a6+ & C++ & Cython cause compile error on some compilers.
2024-05-04 16:01:48 +09:00
Inada Naoki
0602baf3ea
update Cython and setuptools (#599) 2024-05-03 18:20:09 +09:00
Inada Naoki
2eca765533
use ruff instead of black (#598) 2024-05-03 15:17:54 +09:00
hakan akyürek
e77672200b
Avoid using floating points during timestamp-datetime conversions (#591) 2024-04-20 07:46:30 +09:00
Inada Naoki
9aedf8ed7f
Release v1.0.8 (#583) 2024-03-01 20:35:28 +09:00
Inada Naoki
bf7bf88ad0
ci: update workflows (#582) 2024-03-01 20:09:55 +09:00
Inada Naoki
039022cecb
update Cython (#581) 2024-03-01 19:24:06 +09:00
Inada Naoki
140864249f
exclude C/Cython files from wheel (#577) 2023-12-20 20:46:04 +09:00
Inada Naoki
c78026102c
doc: use sphinx-rtd-theme (#575) 2023-11-15 23:34:32 +09:00
Inada Naoki
2982e9ff72
release v1.0.7 (#569) 2023-09-28 17:31:52 +09:00
Inada Naoki
acd0684392
do not fallback on build error (#568) 2023-09-28 15:25:10 +09:00
Inada Naoki
ecf03748c7
remove inline macro for msvc (#567) 2023-09-28 15:03:16 +09:00
Inada Naoki
b1b0edaeed
release v1.0.6 (#564) 2023-09-21 14:58:37 +09:00
Inada Naoki
e1d3d5d5c3
update actions (#563) 2023-09-15 12:02:06 +09:00
Inada Naoki
4e10c10aaa
prepare for 1.0.6rc1 (#557) 2023-09-13 18:40:04 +09:00
TW
41d6239c0a
fix .readthedocs.yaml, fixes #559 (#560) 2023-09-13 02:51:12 +09:00
TW
ef15f4a62c
add a basic .readthedocs.yaml file (#558) 2023-09-07 21:25:07 +09:00
TW
423c6df265
move project metadata to pyproject.toml (#555)
also: replace flake8 by ruff.
2023-09-05 10:51:04 +09:00
TW
7b75b4f368
sphinx-related work (#554)
fixes #510
2023-08-31 12:56:24 +09:00
Inada Naoki
715126c67b
CI: update cibuildwheel to v2.15.0 (#551) 2023-08-09 18:20:05 +09:00
Inada Naoki
7cfced5150 start v1.0.6 development 2023-08-09 18:09:42 +09:00
Inada Naoki
427736bbcc
try Cython 3.0 (#548) 2023-07-21 11:11:04 +09:00
Inada Naoki
e5249f877c ci: add Python 3.12 and drop 3.7 2023-07-21 02:53:58 +09:00
Evgeny Markov
c8d0751fe3
Drop Python 3.6 support (#543)
The following steps have been taken:

1. Black was updated to latest version. The code has been formatted with
the new version.
2. The pyupgrade utility is installed. This helped to remove all the
code that was needed to support Python < 3.7.

Fix #541.

Co-authored-by: Inada Naoki <songofacandy@gmail.com>
2023-05-24 01:41:08 +09:00
sblondon
feec06206c
Drop python2 support (#519)
The PR removes python2 references and cases.

Close #518

Co-authored-by: Inada Naoki <songofacandy@gmail.com>
2023-05-21 16:26:39 +09:00
Laerte Pereira
45f848695c
fix: build status badge (#538) 2023-04-08 14:18:25 +09:00
Inada Naoki
802cbc9495 Add security policy 2023-04-01 00:02:25 +09:00
Inada Naoki
0516c2c2a9 Action: Update test workflow 2023-03-09 01:22:38 +09:00
Inada Naoki
35b2d246cf Action: Update wheel workflow 2023-03-09 00:47:52 +09:00
Inada Naoki
4c55f809fe
Release v1.0.5 (#534) 2023-03-09 00:43:28 +09:00
Inada Naoki
aa9ce3e2bb Action: Run publish on tag creation. 2023-03-08 22:19:17 +09:00
Anthon van der Neut
dcb775031c
minor type in exception message (#533)
interger -> integer
2023-03-05 23:45:38 +09:00
Inada Naoki
e3ef909c47 Action: Use setup-python@v4 2023-01-18 13:07:24 +00:00
Inada Naoki
1008229553
Release v1.0.5rc1 (#528) 2023-01-18 19:47:15 +09:00
Inada Naoki
b82d0b62f1
fallback: Fix packing multidim memoryview (#527)
Fix #526
2023-01-18 19:13:44 +09:00
Inada Naoki
c3995669f1 Remove unused code 2023-01-18 08:08:58 +00:00
Matthieu Darbois
44a8060383
Add python 3.11 wheels (#517) 2022-09-09 16:16:12 +09:00
Inada Naoki
edca770071
Fix build error caused by ntohs, ntohl (#514) 2022-08-08 15:08:40 +09:00
Jakub Kulík
9d45926a59
Usef __BYTE_ORDER__ instead of __BYTE_ORDER (#513)
__BYTE_ORDER__ is common predefined macro available on at least gcc and clang.
__BYTE_ORDER is macro defined in platform specific headers.
2022-08-02 13:19:56 +09:00
Inada Naoki
b5acfd5383
Release v1.0.4 (#509) 2022-06-03 13:46:51 +09:00
Inada Naoki
caadbf2df5 Use Actions to publish to PyPI 2022-05-25 12:10:47 +09:00
Inada Naoki
a34dc945bf 1.0.4rc1 2022-05-25 10:00:57 +09:00
Inada Naoki
63837a44d8
ci: Update action versions. (#507) 2022-05-24 20:13:07 +09:00
Inada Naoki
500a238028
Fix Unpacker max_buffer_length handling (#506) 2022-05-24 19:46:51 +09:00
Inada Naoki
b75e3412fb Fix pip upgrade 2022-05-23 05:01:08 +00:00
Inada Naoki
b901b179d1 Update Cython to 0.29.30 2022-05-23 04:52:09 +00:00
Shantanu
6a721faa77
Upgrade black (#505) 2022-05-02 17:26:53 +09:00
Victor Stinner
849c806381
Use PyFloat_Pack8() on Python 3.11a7 (#499)
Python 3.11a7 adds public functions:

* PyFloat_Pack4(), PyFloat_Pack8()
* PyFloat_Unpack4(), PyFloat_Unpack8()

https://bugs.python.org/issue46906
2022-03-14 11:23:11 +09:00
Inada Naoki
cb50b2081b
Update setuptools and black (#498)
* Use setuptools
* Use black==22.1.0
2022-03-03 12:29:55 +09:00
Inada Naoki
89ea57747e
Don't define __*_ENDIAN__ macro on Unix. (#495) 2022-01-19 14:42:28 +09:00
Inada Naoki
bdf0511e29
Refactor CI (#492)
* Use cibuildwheel to build wheels.
* Use matrix
2021-11-25 14:43:55 +09:00
Inada Naoki
6129789e9f
Release v1.0.3 (#491) 2021-11-24 16:18:17 +09:00
Inada Naoki
e29b423de7 black 2021-11-17 11:03:06 +09:00
Inada Naoki
724e6200fd 1.0.3rc1 2021-11-16 17:52:01 +09:00
Benjamin Egelund-Müller
e464cb44fa
Nicer error when packing a datetime without tzinfo (#466) 2021-11-16 17:49:47 +09:00
Inada Naoki
cfa05d3fdc
Actions: Run CI only for PRs from forks. (#489) 2021-11-16 17:47:16 +09:00
Inada Naoki
8e358617e7
mac: Provide Universal2 wheel (#488)
* mac: Use cibuildwheel
* Do not build wheel for PyPy.
2021-11-16 17:42:42 +09:00
Inada Naoki
b3f7254192
Support Python 3.10 and Drop Python 3.5 (#487)
* linux: Use manylinux2014
* mac: Drop Python 3.6 too
2021-11-16 16:19:47 +09:00
Inada Naoki
9b84e490e7 Fix black formatting 2021-11-16 14:53:08 +09:00
Paul Melis
09187421eb
Improve exception message relating to strict_map_key (#485) 2021-11-16 14:47:40 +09:00
Vladimir Matveev
38dba9634e
cimport uint64_t instead of using ctypedef (#473) 2021-03-19 06:35:54 +09:00
Andrey Bienkowski
010de11bed
Make pure-python wheels and eggs possible (#467) 2021-02-27 10:50:24 +09:00
Alexander Shadchin
44fd577705
Remove unused PyObject_AsReadBuffer definition (#468)
Also "old" buffer API was removed in Python 3.10
2021-02-27 09:30:46 +09:00
Andrey Bienkowski
4ace82f108
Fix tox.ini (#465)
There is no such thing as [variants] in the tox syntax. This resulted
in MSGPACK_PUREPYTHON being unset in the "pure" test environments
2021-02-26 21:08:06 +09:00
Andrey Bienkowski
38357b928a
Fix error formatting (#463) 2021-02-26 11:39:36 +09:00
Andrey Bienkowski
4b0819dca9
Remove redundant code (#460) 2021-02-16 22:38:06 +09:00
Inada Naoki
1e728a2e0b
fix docstring (#459) 2021-02-12 16:20:14 +09:00
laike9m
cfae52437b
Updated readme about Python 2 support (#456) 2021-01-28 08:33:14 +09:00
Alex Willmer
02e1f7623c
build: Create tox environments using a known Cython version (#408)
This change causes Tox to run the project's setup.py in a virtualenv
(default path is .tox/.package). The required version of Cython is
installed, rather than whatever version is installed system wide.
2021-01-27 10:11:32 +09:00
Guy Tuval
3b71818bb0
Refactor fallback read header (#441) 2021-01-02 15:39:37 +09:00
Inada Naoki
431ef45c8e Use manylinux1 instead of manylinux2010 2020-12-18 17:43:37 +09:00
Inada Naoki
c0516c603f v1.0.2 2020-12-18 16:43:04 +09:00
Inada Naoki
f34fca7fb5 Update readme 2020-12-18 16:21:41 +09:00
Inada Naoki
051f9ded1f format markdown 2020-12-18 16:13:35 +09:00
Inada Naoki
94336cf914
Fix some travis builds. (#453) 2020-12-18 16:03:05 +09:00
Inada Naoki
753b3706d8
Fix overflow in unpacking timestamp to datetime (#452) 2020-12-18 14:21:27 +09:00
Inada Naoki
8029f95516 Add Python 3.9 wheels 2020-12-11 19:33:20 +09:00
Inada Naoki
edd5603661 Actions: Add Python 3.9 2020-12-11 19:31:24 +09:00
Inada Naoki
d893697eab v1.0.1 2020-12-11 19:16:14 +09:00
Tsahi Zidenberg
7d6b4dfb51
Build arm64 wheels (#439) 2020-12-11 14:30:49 +09:00
Inada Naoki
2df517999b Travis: Reduce build
Save credits.
2020-12-11 13:39:24 +09:00
Inada Naoki
44bc2bd439 Update docstring 2020-12-04 17:52:24 +09:00
Peter Fischer
8fb709f2e0
Fix datetime before epoch on windows in cython implementation (#436)
Cython implementation still used datetime.from_timestamp method, which does not work on windows.
Update the cython implementation to use utc time and delta and add a regression test to highlight the issue.
2020-07-30 23:48:51 +09:00
Peter Fischer
772c830841
Synchronize handling of datetime in Packer implementations (#434)
The handling of datetime is different in the cython and Python implementations. In contrast to the docs, timezone is not required in the Python implementation.
2020-07-24 16:29:15 +09:00
Tom Pohl
5614dd5a89
Allow for timestamps before UNIX epoch (#433) 2020-07-23 17:53:55 +09:00
Markus Gerstel
d9ead81021
Fix a typo in the changelog (#429) 2020-06-26 18:15:46 +09:00
Contextualist
3508ca524e
Fix benchmark extension module import (#428) 2020-06-22 11:27:52 +09:00
jfolz
c1b1a23f62
Fix Unpacker.tell() (#427)
Fixes #426.

Co-authored-by: folz <joachim.folz@dfki.de>
2020-06-08 12:14:50 +09:00
Inada Naoki
b04690012d
Update doc version
Fixes #425
2020-05-24 02:15:04 +09:00
Kevin Tewouda
4e10222b51
Fix an example in README.md (#423) 2020-05-13 13:41:15 +09:00
Dan Salmon
692e0ee8ff
Fix typo (#416) 2020-03-18 09:29:51 +09:00
Charles-Axel Dein
2bfc2d0566
Upgrade msgpack if already installed (#414) 2020-02-24 17:51:56 +09:00
Inada Naoki
2849f5582a
Build linux and macOS wheels on GitHub Actions. (#409) 2020-02-19 00:53:00 +09:00
Inada Naoki
12506d8d91 update README 2020-02-17 17:12:47 +09:00
Inada Naoki
fa7d7447fc 1.0.0 2020-02-17 17:07:18 +09:00
Inada Naoki
64f59884a1 Add note 2020-02-17 16:58:25 +09:00
Alex Willmer
fcb19a0e1a
Clean msgpack/_cmsgpack.cpp and msgpack/_cmsgpack.*.so (#407) 2020-02-14 15:51:19 +09:00
Alex Willmer
cd6561db52
build: Don't test C extension on CPython 2.7 under Tox (#406)
As the Changelog notes, release 1.0 will drop support for the native
extension on CPython 2.x. So there seems little benefit of testing it.
2020-02-14 15:45:17 +09:00
Erik Cederstrand
f0952f1dd6
travis: Python 3.9 is the new dev version. (#405) 2020-02-14 12:31:09 +09:00
Inada Naoki
9d79351e99
Add some test for timestamp (#403) 2020-02-06 22:11:04 +09:00
Inada Naoki
ff1f5f89d9 README: ` -> 2020-02-06 21:06:04 +09:00
Inada Naoki
0dad821169 Fix markdown 2020-02-06 20:35:41 +09:00
Inada Naoki
24950990f4 Remove broken example 2020-02-06 20:29:33 +09:00
Emilio Tagua
1bd6fc36d0
Update README.md (#402) 2020-02-05 21:20:17 +09:00
ossdev07
030bb2f1f7 travis: Add test for arm64 (#399)
Signed-off-by: ossdev07 <ossdev@puresoftware.com>
2019-12-31 10:12:21 +09:00
Inada Naoki
ebfe55e637 travis: Use build config validation. 2019-12-16 15:14:34 +09:00
Inada Naoki
42f5ecfd51 Fix some typo 2019-12-13 15:10:32 +09:00
Inada Naoki
5e1fe818e3 Reintroduce __ne__ 2019-12-12 20:05:25 +09:00
Inada Naoki
9e5ec95e02
Make Timestamp hashable (#396)
When overriding __eq__, __hash__ should be overridden too.
2019-12-12 19:59:06 +09:00
Inada Naoki
887d3a7d22
Refine Timestamp APIs (#395) 2019-12-12 19:43:59 +09:00
Inada Naoki
aab29ff277 Remove TRANSITIONAL package support 2019-12-12 18:48:16 +09:00
Inada Naoki
a05fc5e7c5 black 2019-12-12 18:46:55 +09:00
Inada Naoki
3df431cafd Prepare 1.0rc1 2019-12-12 18:25:38 +09:00
Inada Naoki
c60e6c7a6f Update README 2019-12-12 18:09:07 +09:00
Inada Naoki
2186455d15
Support datetime. (#394) 2019-12-11 23:48:16 +09:00
Marty B
5fd6119093 Simplify check for bool type (#362) 2019-12-09 19:29:47 +09:00
Inada Naoki
d10f12db8f typo 2019-12-09 18:12:51 +09:00
Inada Naoki
c356035a57
Unpacker: Change max_buffer_size to 100MiB (#391) 2019-12-09 17:03:12 +09:00
Inada Naoki
5399f8180d
Update README (#393) 2019-12-09 17:02:35 +09:00
Inada Naoki
d8e3cf0563
Make strict_map_key default to True (#392) 2019-12-06 22:23:15 +09:00
Inada Naoki
0fc0eb2f16 Update README 2019-12-06 21:26:28 +09:00
Inada Naoki
5ba496c79a
Move Black from Travis to Github Actions (#390) 2019-12-06 21:23:54 +09:00
Inada Naoki
f6f6f328eb
Fix fallback Unpacker.read() (#388)
Fixes #352.
2019-12-06 21:16:27 +09:00
Inada Naoki
7a8ce0f9ca Remove unused import 2019-12-06 20:34:18 +09:00
Inada Naoki
235c6036ea
travis: Use codecov (#387) 2019-12-06 19:28:23 +09:00
Inada Naoki
7e9905bdfa
Use new msgpack spec by default. (#386) 2019-12-05 21:34:10 +09:00
Inada Naoki
de320488ae
fallback: Remove old buffer protocol support (#384) 2019-12-05 20:47:20 +09:00
Inada Naoki
9f4b2d53b7
Remove deprecated submodule unpack (#385) 2019-12-05 20:47:01 +09:00
Inada Naoki
9ae43709e4
Drop old buffer protocol support (#383) 2019-12-05 20:20:53 +09:00
Inada Naoki
af4eea430e travis: Add Black 2019-12-05 19:05:00 +09:00
Inada Naoki
bc8c86203a blacken all files. 2019-12-05 18:53:49 +09:00
Inada Naoki
10e5e39ff9 blacken test 2019-12-05 18:51:45 +09:00
Inada Naoki
e557e17cbd blacken 2019-12-05 18:50:13 +09:00
Inada Naoki
641406902e
Add Timestamp support (#382) 2019-12-05 18:29:15 +09:00
Inada Naoki
2c6668941f
Intern map keys (#381)
Fixes #372.
2019-12-03 21:18:17 +09:00
Inada Naoki
e419cd8e2d
Remove encoding option from Unpacker. (#380) 2019-12-03 21:13:05 +09:00
Inada Naoki
83ebb63c44
Ressurect unicode_errors of the Packer. (#379) 2019-12-03 20:53:11 +09:00
Inada Naoki
a0480c7602 Update ChangeLog 2019-12-03 18:54:18 +09:00
Inada Naoki
e1ed0044bf
Remove encoding/unicode_errors options from Packer (#378) 2019-12-03 18:54:01 +09:00
Inada Naoki
cc3a8665d6
Use Github Actions for Windows (#377) 2019-12-03 17:46:28 +09:00
Inada Naoki
891f2d8743
Drop Python 2 support from _cmsgpack (#376) 2019-11-28 20:23:34 +09:00
Terence Honles
b458e9a6a2 update for Python 3.8 (#374) 2019-11-23 12:58:55 +09:00
Inada Naoki
997b524f06 0.6.2 2019-09-20 16:37:08 +09:00
Inada Naoki
144f276e88 Update ChangeLog 2019-09-20 16:36:37 +09:00
Inada Naoki
fd3f004863
Add Python 3.8 to travis (#371) 2019-09-19 20:37:19 +09:00
Inada Naoki
c25e2a0984
update Cython to 0.29.13 (#370) 2019-09-19 20:14:33 +09:00
Inada Naoki
3146ebd330
Use Py_SIZE() when it is safe (#369) 2019-09-19 13:20:57 +09:00
Marty B
b98b8cab99 Avoid calling __Pyx_GetModuleGlobalName for ExtType (#363) 2019-09-19 01:15:09 +09:00
Felix Schwarz
05ff11dbcc use relative imports (#357)
Some applications use msgpack to store persistent data and require a specific
msgpack version (e.g. borgbackup). Bundling helps in case there is an
(incompatible) version of msgpack in a system-wide install.
2019-05-12 21:44:32 +09:00
Hugues
737f08a885 Update requirements.txt (#346)
bytearray.pxd is only available starting with Cython 0.29
2019-03-27 22:37:26 +09:00
INADA Naoki
381c2eff5f Fix changelog.
Fixes #343
2019-02-04 12:08:07 +09:00
Inada Naoki
8f513af999 v0.6.1 2019-01-25 21:43:28 +09:00
Inada Naoki
280308e8ce Recommend max_buffer_len instead of max_(str|bin|ext)_len 2019-01-25 21:27:46 +09:00
Inada Naoki
9951b89455 travis: Install new pytest 2019-01-25 21:04:14 +09:00
Inada Naoki
464fe277e1 Remove pytest warnings 2019-01-25 20:52:57 +09:00
Inada Naoki
28b5f46a34
Auto limit configuration (#342) 2019-01-24 18:46:39 +09:00
INADA Naoki
f46523b1af
use _PyFloat APIs to (de)serialize (#340) 2019-01-07 21:10:40 +09:00
Inada Naoki
197e30723a Fix docstring 2018-12-04 20:10:21 +09:00
Inada Naoki
b8bf3c950c Build linux wheel for Python 3.7 2018-12-04 17:18:34 +09:00
Inada Naoki
b1d658e7a0 AppVeyor: Add Python 3.7 and remove 3.6 2018-11-30 19:25:14 +09:00
Inada Naoki
cc7fd5722b 0.6.0 2018-11-30 19:03:44 +09:00
Inada Naoki
bbdfd4d92e cleanup 2018-11-30 16:28:41 +09:00
Inada Naoki
93b5953eae Update tox.ini 2018-11-30 16:05:31 +09:00
Inada Naoki
04cf8fc7f4 Update ChangeLog 2018-11-30 14:04:18 +09:00
INADA Naoki
760e30b77e
unpacker: Add strict_map_key option (#334) 2018-11-30 11:47:15 +09:00
Inada Naoki
8ae6320072 Fix fallback 2018-11-30 11:42:51 +09:00
Inada Naoki
ab789813b8 Fix test 2018-11-30 11:36:15 +09:00
Inada Naoki
e76091a82c Fix test 2018-11-29 22:38:22 +09:00
Inada Naoki
dc1b993079 Implement strict_map_key to fallback unpacker. 2018-11-29 22:35:12 +09:00
Inada Naoki
e9086a34e4 Add strict_map_key option to unpacker 2018-11-29 22:29:38 +09:00
INADA Naoki
3c9c6edbc8 Update README 2018-11-20 15:48:44 +09:00
jkorvin
ab2415eaa0 Unpacker: allow to use buffer with size greater than 2 GB (#332) 2018-11-20 15:24:35 +09:00
INADA Naoki
44254dd35e
Add StackError and FormatError (#331) 2018-11-20 13:12:49 +09:00
INADA Naoki
8b6ce53cce
s/iteritems/items/g (#330) 2018-11-14 21:06:16 +09:00
INADA Naoki
2f808b6e01
Try language_level=3 (#329) 2018-11-14 20:04:22 +09:00
INADA Naoki
d782464c91
Refactor Cython code (#328)
_msgpack -> _cmsgpack
2018-11-14 16:35:37 +09:00
INADA Naoki
2b5f59166b
fallback: Fix warning stacklevel (#327) 2018-11-14 16:34:51 +09:00
INADA Naoki
39f8aa78c7
Remove deprecated write_bytes option (#322) 2018-11-12 02:33:31 +09:00
INADA Naoki
07f0beeabb
Remove deprecated exception classes (#323) 2018-11-12 02:19:01 +09:00
INADA Naoki
1bf62ba6f8
PendingDeprecationWarning -> DeprecationWarning (#321) 2018-11-09 21:39:25 +09:00
INADA Naoki
9e210bfc1a
Add Packer.buffer() (#320) 2018-11-09 20:55:13 +09:00
Inada Naoki
a8b3e97fe5 Update changelog 2018-11-08 22:25:05 +09:00
INADA Naoki
3b80233592
unpacker: Make default size limit smaller (#319)
To avoid DoS attack, make default size limit smaller.

Fixes #295
2018-11-08 22:21:44 +09:00
Inada Naoki
ae90b26c30 Update ChangeLog 2018-11-08 22:21:05 +09:00
INADA Naoki
08e65bdd03
Merge extension modules (#314)
There were `_packer.so` and `_unpacker.so`.
But single module is simpler than double module.

Merge extension module into single `_msgpack.so`.
2018-11-08 21:39:18 +09:00
INADA Naoki
9d11249d89 Update docker/runtests 2018-11-08 20:31:07 +09:00
INADA Naoki
6c8e539eec Update travis config 2018-11-08 20:31:06 +09:00
INADA Naoki
f6f9597249 Merge extension module
There were `_packer.so` and `_unpacker.so`.
But single module is simpler than double module.

Merge extension module into single `_msgpack.so`.
2018-11-08 20:27:35 +09:00
Inada Naoki
91ec9e1daf Update travis.yml 2018-11-07 23:04:45 +09:00
Marat Sharafutdinov
b077a21f89 Fix stream unpacking example in README (#317) 2018-11-05 01:14:11 +09:00
INADA Naoki
205f7d39b2
Start 0.6 development 2018-10-03 21:06:20 +09:00
Raymond E Ferguson
70b5f21b34 Alternate fixes for jython and legacy CPython (#310)
Python 3.4 is not supported officially.
But keep running test for a while, to know when msgpack-python
stop working on Python 3.4 actually.

The current patches did not work under jython-2.7.1 where implicit
casting of buffer or memoryview doesn't work. It may also be the
jython is a little pickier about string casting non string bytes
due to the underlying strong typing of java.

See issues #303 & #304.
2018-10-02 20:20:06 +09:00
INADA Naoki
d1060de293
travis: Run test on Python 3.4 (#307)
Python 3.4 is not supported officially.
But keep running test for a while, to know when msgpack-python
stop working on Python 3.4 actually.
2018-07-13 19:54:44 +09:00
INADA Naoki
aa41e2fef7
fallback: Fix error on Jython (#304)
Jython doesn't support memoryview += bytes

Fixes #303
2018-07-06 12:40:33 +09:00
Inada Naoki
5f684aed82 fallback: Fix error on Jython
Fixes #303
2018-06-27 01:27:31 +09:00
Alex Gaynor
b10cf78f54 Fix TypeError in fallback.unpack() on <Python 2.7.6 2018-04-16 12:18:35 +09:00
INADA Naoki
984116bd18 Update setup() 2018-04-13 23:41:01 +09:00
INADA Naoki
d4675bee6c 0.5.6 2018-02-23 15:45:34 +09:00
INADA Naoki
ae3a6ba0b0
Deprecate implementation module's unpack() (#290) 2018-02-23 15:41:21 +09:00
INADA Naoki
f38c1a3674
Fix Unpacker.feed() drops unused data in buffer. (#289)
Fixes #287
2018-02-23 11:52:48 +09:00
INADA Naoki
fbaa1360be Fix #285 again 2018-02-23 11:35:09 +09:00
INADA Naoki
3ca8eff31d
Revert "Move unpack() from each implementation to __init__." (#288)
This reverts commit da902f9c1d.
2018-02-23 11:33:26 +09:00
INADA Naoki
9455fccc52 Revert "Move unpack() from each implementation to __init__. (#286)"
This reverts commit da902f9c1d.
2018-02-23 11:32:26 +09:00
INADA Naoki
9bf38105f7 Revert "0.5.5"
This reverts commit 02c881c7cb.
2018-02-23 11:32:26 +09:00
INADA Naoki
02c881c7cb 0.5.5 2018-02-22 17:56:57 +09:00
INADA Naoki
da902f9c1d
Move unpack() from each implementation to __init__. (#286)
Fixes #285
2018-02-22 00:55:32 +09:00
INADA Naoki
ae8d469482
Fix memory leak in pure Python Unpacker.feed() (#284)
fixes #283
2018-02-16 16:35:22 +09:00
INADA Naoki
4b72b61773 Add Makefile target for updating docker image 2018-02-05 15:08:19 +09:00
INADA Naoki
2644cbdcb7
Use cython's cast for converting encoding and errors (#279)
It is little faster on Python 3 because we can skip temporary bytes object
2018-02-05 11:44:17 +09:00
INADA Naoki
351023946f 0.5.4 2018-02-05 02:25:12 +09:00
INADA Naoki
9fdb83719d
Undeprecate unicode_errors (#278) 2018-02-05 02:19:48 +09:00
INADA Naoki
618b2cb027 0.5.3 2018-02-03 10:54:21 +09:00
Andrew Rabert
a0ba076c35 Fix encoding and unicode_errors (#277)
Previously, unicode_errors was either set to NULL or to
the result of PyBytes_AsString. This restores that behavior while also
keeping the existing NULL default behavior.
Original defaults were restored to keep API compatibility until these
deprecated options are finally removed.
2018-02-03 10:34:42 +09:00
INADA Naoki
52fb85a2c5 0.5.2 2018-02-02 19:43:42 +09:00
INADA Naoki
5569a4efcd
s/raw_as_bytes/raw/g (#276)
fixes #273
2018-01-12 19:22:36 +09:00
INADA Naoki
d9ec8fc905
Packer.pack() reset buffer on exception (#274)
fixes #210
2018-01-11 23:50:41 +09:00
INADA Naoki
60ef3879d7
packer: Use PyUnicode_AsUTF8AndSize() for utf-8 (#272) 2018-01-11 19:41:05 +09:00
INADA Naoki
5534d0c7af
Add raw_as_bytes option to Unpacker. (#265) 2018-01-11 17:02:41 +09:00
63 changed files with 3306 additions and 2474 deletions

33
.github/workflows/docs.yaml vendored Normal file
View file

@ -0,0 +1,33 @@
name: docs
on: ["push", "pull_request"]
jobs:
docs:
# We want to run on external PRs, but not on our own internal PRs as they'll be run
# by the push to the branch.
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.x'
cache: "pip"
cache-dependency-path: |
requirements.txt
docs/requirements.txt
- name: Build
run: |
pip install -r requirements.txt
make cython
- name: Sphinx Documentation Generator
run: |
pip install -r docs/requirements.txt
make docs

22
.github/workflows/lint.yaml vendored Normal file
View file

@ -0,0 +1,22 @@
name: lint
on: ["push", "pull_request"]
jobs:
lint:
# We want to run on external PRs, but not on our own internal PRs as they'll be run
# by the push to the branch.
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: ruff check
run: |
pipx run ruff check --diff msgpack/ test/ setup.py
- name: ruff format
run: |
pipx run ruff format --diff msgpack/ test/ setup.py

61
.github/workflows/test.yml vendored Normal file
View file

@ -0,0 +1,61 @@
name: Run tests
on:
push:
branches: [main]
pull_request:
create:
jobs:
test:
strategy:
matrix:
os: ["ubuntu-latest", "windows-latest", "windows-11-arm", "macos-latest"]
py: ["3.14", "3.14t", "3.13", "3.12", "3.11", "3.10"]
exclude:
- os: windows-11-arm
py: "3.10"
runs-on: ${{ matrix.os }}
name: Run test with Python ${{ matrix.py }} on ${{ matrix.os }}
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.py }}
allow-prereleases: true
cache: "pip"
- name: Prepare
shell: bash
run: |
python -m pip install -r requirements.txt pytest
- name: Build
shell: bash
run: |
make cython
pip install .
- name: Test (C extension)
shell: bash
run: |
pytest -v test
- name: Test (pure Python fallback)
shell: bash
run: |
MSGPACK_PUREPYTHON=1 pytest -v test
- name: build packages
shell: bash
run: |
python -m build -nv
- name: upload packages
uses: actions/upload-artifact@v4
with:
name: dist-${{ matrix.os }}-${{ matrix.py }}
path: dist

88
.github/workflows/wheel.yml vendored Normal file
View file

@ -0,0 +1,88 @@
name: Build sdist and Wheels
on:
push:
branches: [main]
release:
types:
- published
workflow_dispatch:
jobs:
build_wheels:
strategy:
matrix:
# macos-13 is for intel
os: ["ubuntu-24.04", "ubuntu-24.04-arm", "windows-latest", "windows-11-arm", "macos-13", "macos-latest"]
runs-on: ${{ matrix.os }}
name: Build wheels on ${{ matrix.os }}
steps:
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
with:
python-version: "3.x"
cache: "pip"
- name: Cythonize
shell: bash
run: |
pip install -r requirements.txt
make cython
- name: Build
uses: pypa/cibuildwheel@v3.3.0
env:
CIBW_TEST_REQUIRES: "pytest"
CIBW_TEST_COMMAND: "pytest {package}/test"
CIBW_SKIP: "pp* cp38-* cp39-* cp310-win_arm64"
- name: Build sdist
if: runner.os == 'Linux' && runner.arch == 'X64'
run: |
pip install build
python -m build -s -o wheelhouse
- name: Upload Wheels to artifact
uses: actions/upload-artifact@v4
with:
name: wheels-${{ matrix.os }}
path: wheelhouse
# combine all wheels into one artifact
combine_wheels:
needs: [build_wheels]
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v4
with:
# unpacks all CIBW artifacts into dist/
pattern: wheels-*
path: dist
merge-multiple: true
- name: Upload Wheels to artifact
uses: actions/upload-artifact@v4
with:
name: wheels-all
path: dist
# https://github.com/pypa/cibuildwheel/blob/main/examples/github-deploy.yml
upload_pypi:
needs: [build_wheels]
runs-on: ubuntu-latest
environment: pypi
permissions:
id-token: write
if: github.event_name == 'release' && github.event.action == 'published'
# or, alternatively, upload to PyPI on every tag starting with 'v' (remove on: release above to use this)
# if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
steps:
- uses: actions/download-artifact@v4
with:
# unpacks all CIBW artifacts into dist/
pattern: wheels-*
path: dist
merge-multiple: true
- uses: pypa/gh-action-pypi-publish@release/v1
#with:
# To test: repository-url: https://test.pypi.org/legacy/

2
.gitignore vendored
View file

@ -2,11 +2,13 @@ MANIFEST
build/*
dist/*
.tox
.python-version
*.pyc
*.pyo
*.so
*~
msgpack/__version__.py
msgpack/*.c
msgpack/*.cpp
*.egg-info
/venv

24
.readthedocs.yaml Normal file
View file

@ -0,0 +1,24 @@
# Read the Docs configuration file for Sphinx projects.
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details.
version: 2
build:
os: ubuntu-22.04
tools:
python: "3.11"
apt_packages:
- build-essential
jobs:
pre_install:
- pip install -r requirements.txt
- make cython
python:
install:
- method: pip
path: .
- requirements: docs/requirements.txt
sphinx:
configuration: docs/conf.py

View file

@ -1,45 +0,0 @@
sudo: false
language: python
cache: pip
python:
- "2.7"
- "3.5"
- "3.6"
- "3.7-dev"
matrix:
include:
- sudo: required
language: python
services:
- docker
env:
- DOCKER_IMAGE=quay.io/pypa/manylinux1_i686
install:
- pip install -U pip
- pip install cython
- cython --cplus msgpack/_packer.pyx msgpack/_unpacker.pyx
- docker pull $DOCKER_IMAGE
script:
- docker run --rm -v `pwd`:/io -w /io $DOCKER_IMAGE /io/docker/runtests.sh
- python: "pypy"
install:
- pip install -e .
script:
- py.test -v test
install:
- pip install -U pip
- pip install cython
- cython --cplus msgpack/_packer.pyx msgpack/_unpacker.pyx
- pip install -e .
script:
- python -c 'import sys; print(hex(sys.maxsize))'
- python -c 'from msgpack import _packer, _unpacker'
- py.test -v test
- MSGPACK_PUREPYTHON=x py.test -v test
# vim: sw=2 ts=2

View file

@ -1,3 +1,259 @@
1.1.2
=====
Release Date: 2025-10-08
This release does not change source code. It updates only building wheels:
* Update Cython to v3.1.4
* Update cibuildwheel to v3.2.0
* Drop Python 3.8
* Add Python 3.14
* Add windows-arm
1.1.1
=====
Release Date: 2025-06-13
* No change from 1.1.1rc1.
1.1.1rc1
========
Release Date: 2025-06-06
* Update Cython to 3.1.1 and cibuildwheel to 2.23.3.
1.1.0
=====
Release Date: 2024-09-10
* use ``PyLong_*`` instead of ``PyInt_*`` for compatibility with
future Cython. (#620)
1.1.0rc2
========
Release Date: 2024-08-19
* Update Cython to 3.0.11 for better Python 3.13 support.
* Update cibuildwheel to 2.20.0 to build Python 3.13 wheels.
1.1.0rc1
========
Release Date: 2024-05-07
* Update Cython to 3.0.10 to reduce C warnings and future support for Python 3.13.
* Stop using C++ mode in Cython to reduce compile error on some compilers.
* ``Packer()`` has ``buf_size`` option to specify initial size of
internal buffer to reduce reallocation.
* The default internal buffer size of ``Packer()`` is reduced from
1MiB to 256KiB to optimize for common use cases. Use ``buf_size``
if you are packing large data.
* ``Timestamp.to_datetime()`` and ``Timestamp.from_datetime()`` become
more accurate by avoiding floating point calculations. (#591)
* The Cython code for ``Unpacker`` has been slightly rewritten for maintainability.
* The fallback implementation of ``Packer()`` and ``Unpacker()`` now uses keyword-only
arguments to improve compatibility with the Cython implementation.
1.0.8
=====
Release Date: 2024-03-01
* Update Cython to 3.0.8. This fixes memory leak when iterating
``Unpacker`` object on Python 3.12.
* Do not include C/Cython files in binary wheels.
1.0.7
=====
Release Date: 2023-09-28
* Fix build error of extension module on Windows. (#567)
* ``setup.py`` doesn't skip build error of extension module. (#568)
1.0.6
=====
Release Date: 2023-09-21
.. note::
v1.0.6 Wheels for Windows don't contain extension module.
Please upgrade to v1.0.7 or newer.
* Add Python 3.12 wheels (#517)
* Remove Python 2.7, 3.6, and 3.7 support
1.0.5
=====
Release Date: 2023-03-08
* Use ``__BYTE_ORDER__`` instead of ``__BYTE_ORDER`` for portability. (#513, #514)
* Add Python 3.11 wheels (#517)
* fallback: Fix packing multidimensional memoryview (#527)
1.0.4
=====
Release Date: 2022-06-03
* Support Python 3.11 (beta).
* Don't define `__*_ENDIAN__` macro on Unix. by @methane in https://github.com/msgpack/msgpack-python/pull/495
* Use PyFloat_Pack8() on Python 3.11a7 by @vstinner in https://github.com/msgpack/msgpack-python/pull/499
* Fix Unpacker max_buffer_length handling by @methane in https://github.com/msgpack/msgpack-python/pull/506
1.0.3
=====
Release Date: 2021-11-24 JST
* Fix Docstring (#459)
* Fix error formatting (#463)
* Improve error message about strict_map_key (#485)
1.0.2
=====
* Fix year 2038 problem regression in 1.0.1. (#451)
1.0.1
=====
* Add Python 3.9 and linux/arm64 wheels. (#439)
* Fixed Unpacker.tell() after read_bytes() (#426)
* Fixed unpacking datetime before epoch on Windows (#433)
* Fixed fallback Packer didn't check DateTime.tzinfo (#434)
1.0.0
=====
Release Date: 2020-02-17
* Remove Python 2 support from the ``msgpack/_cmsgpack``.
``msgpack/fallback`` still supports Python 2.
* Remove ``encoding`` option from the Packer and Unpacker.
* Unpacker: The default value of ``max_buffer_size`` is changed to 100MiB.
* Unpacker: ``strict_map_key`` is True by default now.
* Unpacker: String map keys are interned.
* Drop old buffer protocol support.
* Support Timestamp type.
* Support serializing and decerializing ``datetime`` object
with tzinfo.
* Unpacker: ``Fix Unpacker.read_bytes()`` in fallback implementation. (#352)
0.6.2
=====
Release Date: 2019-09-20
* Support Python 3.8.
* Update Cython to 0.29.13 for support Python 3.8.
* Some small optimizations.
0.6.1
======
Release Date: 2019-01-25
This release is for mitigating pain caused by v0.6.0 reduced max input limits
for security reason.
* ``unpackb(data)`` configures ``max_*_len`` options from ``len(data)``,
instead of static default sizes.
* ``Unpacker(max_buffer_len=N)`` configures ``max_*_len`` options from ``N``,
instead of static default sizes.
* ``max_bin_len``, ``max_str_len``, and ``max_ext_len`` are deprecated.
Since this is minor release, it's document only deprecation.
0.6.0
======
Release Date: 2018-11-30
This release contains some backward incompatible changes for security reason (DoS).
Important changes
-----------------
* unpacker: Default value of input limits are smaller than before to avoid DoS attack.
If you need to handle large data, you need to specify limits manually. (#319)
* Unpacker doesn't wrap underlying ``ValueError`` (including ``UnicodeError``) into
``UnpackValueError``. If you want to catch all exception during unpack, you need
to use ``try ... except Exception`` with minimum try code block. (#323, #233)
* ``PackValueError`` and ``PackOverflowError`` are also removed. You need to catch
normal ``ValueError`` and ``OverflowError``. (#323, #233)
* Unpacker has ``strict_map_key`` option now. When it is true, only bytes and str
(unicode in Python 2) are allowed for map keys. It is recommended to avoid
hashdos. Default value of this option is False for backward compatibility reason.
But it will be changed True in 1.0. (#296, #334)
Other changes
-------------
* Extension modules are merged. There is ``msgpack._cmsgpack`` instead of
``msgpack._packer`` and ``msgpack._unpacker``. (#314, #328)
* Add ``Unpacker.getbuffer()`` method. (#320)
* unpacker: ``msgpack.StackError`` is raised when input data contains too
nested data. (#331)
* unpacker: ``msgpack.FormatError`` is raised when input data is not valid
msgpack format. (#331)
0.5.6
======
* Fix fallback.Unpacker.feed() dropped unused data from buffer (#287)
* Resurrect fallback.unpack() and _unpacker.unpack().
They were removed at 0.5.5 but it breaks backward compatibility. (#288, #290)
0.5.5
======
* Fix memory leak in pure Python Unpacker.feed() (#283)
* Fix unpack() didn't support `raw` option (#285)
0.5.4
======
* Undeprecate ``unicode_errors`` option. (#278)
0.5.3
======
* Fixed regression when passing ``unicode_errors`` to Packer but not ``encoding``. (#277)
0.5.2
======
* Add ``raw`` option to Unpacker. It is preferred way than ``encoding`` option.
* Packer.pack() reset buffer on exception (#274)
0.5.1
======
* Remove FutureWarning about use_bin_type option (#271)
0.5.0
======

17
DEVELOP.md Normal file
View file

@ -0,0 +1,17 @@
# Developer's note
### Build
```
$ make cython
```
### Test
MessagePack uses `pytest` for testing.
Run test with following command:
```
$ make test
```

View file

@ -1,5 +1,5 @@
include setup.py
include COPYING
include README.rst
recursive-include msgpack *.h *.c *.pyx *.cpp
include README.md
recursive-include msgpack *.h *.c *.pyx
recursive-include test *.py

View file

@ -1,14 +1,34 @@
PYTHON_SOURCES = msgpack test setup.py
.PHONY: all
all: cython
python setup.py build_ext -i -f
.PHONY: format
format:
ruff format $(PYTHON_SOURCES)
.PHONY: lint
lint:
ruff check $(PYTHON_SOURCES)
.PHONY: doc
doc:
cd docs && sphinx-build -n -v -W --keep-going -b html -d doctrees . html
.PHONY: pyupgrade
pyupgrade:
@find $(PYTHON_SOURCES) -name '*.py' -type f -exec pyupgrade --py37-plus '{}' \;
.PHONY: cython
cython:
cython --cplus msgpack/*.pyx
cython msgpack/_cmsgpack.pyx
.PHONY: test
test:
py.test -v test
test: cython
pip install -e .
pytest -v test
MSGPACK_PUREPYTHON=1 pytest -v test
.PHONY: serve-doc
serve-doc: all
@ -17,11 +37,23 @@ serve-doc: all
.PHONY: clean
clean:
rm -rf build
rm msgpack/*.so
rm -f msgpack/_cmsgpack.cpp
rm -f msgpack/_cmsgpack.*.so
rm -f msgpack/_cmsgpack.*.pyd
rm -rf msgpack/__pycache__
rm -rf test/__pycache__
.PHONY: update-docker
update-docker:
docker pull quay.io/pypa/manylinux2014_i686
docker pull quay.io/pypa/manylinux2014_x86_64
docker pull quay.io/pypa/manylinux2014_aarch64
.PHONY: linux-wheel
linux-wheel:
docker run --rm -ti -v `pwd`:/project -w /project quay.io/pypa/manylinux1_i686 bash docker/buildwheel.sh
docker run --rm -ti -v `pwd`:/project -w /project quay.io/pypa/manylinux1_x86_64 bash docker/buildwheel.sh
docker run --rm -v `pwd`:/project -w /project quay.io/pypa/manylinux2014_i686 bash docker/buildwheel.sh
docker run --rm -v `pwd`:/project -w /project quay.io/pypa/manylinux2014_x86_64 bash docker/buildwheel.sh
.PHONY: linux-arm64-wheel
linux-arm64-wheel:
docker run --rm -v `pwd`:/project -w /project quay.io/pypa/manylinux2014_aarch64 bash docker/buildwheel.sh

242
README.md Normal file
View file

@ -0,0 +1,242 @@
# MessagePack for Python
[![Build Status](https://github.com/msgpack/msgpack-python/actions/workflows/wheel.yml/badge.svg)](https://github.com/msgpack/msgpack-python/actions/workflows/wheel.yml)
[![Documentation Status](https://readthedocs.org/projects/msgpack-python/badge/?version=latest)](https://msgpack-python.readthedocs.io/en/latest/?badge=latest)
## What is this?
[MessagePack](https://msgpack.org/) is an efficient binary serialization format.
It lets you exchange data among multiple languages like JSON.
But it's faster and smaller.
This package provides CPython bindings for reading and writing MessagePack data.
## Install
```
$ pip install msgpack
```
### Pure Python implementation
The extension module in msgpack (`msgpack._cmsgpack`) does not support PyPy.
But msgpack provides a pure Python implementation (`msgpack.fallback`) for PyPy.
### Windows
If you can't use a binary distribution, you need to install Visual Studio
or the Windows SDK on Windows.
Without the extension, the pure Python implementation on CPython runs slowly.
## How to use
### One-shot pack & unpack
Use `packb` for packing and `unpackb` for unpacking.
msgpack provides `dumps` and `loads` as aliases for compatibility with
`json` and `pickle`.
`pack` and `dump` pack to a file-like object.
`unpack` and `load` unpack from a file-like object.
```pycon
>>> import msgpack
>>> msgpack.packb([1, 2, 3])
'\x93\x01\x02\x03'
>>> msgpack.unpackb(_)
[1, 2, 3]
```
Read the docstring for options.
### Streaming unpacking
`Unpacker` is a "streaming unpacker". It unpacks multiple objects from one
stream (or from bytes provided through its `feed` method).
```py
import msgpack
from io import BytesIO
buf = BytesIO()
for i in range(100):
buf.write(msgpack.packb(i))
buf.seek(0)
unpacker = msgpack.Unpacker(buf)
for unpacked in unpacker:
print(unpacked)
```
### Packing/unpacking of custom data types
It is also possible to pack/unpack custom data types. Here is an example for
`datetime.datetime`.
```py
import datetime
import msgpack
useful_dict = {
"id": 1,
"created": datetime.datetime.now(),
}
def decode_datetime(obj):
if '__datetime__' in obj:
obj = datetime.datetime.strptime(obj["as_str"], "%Y%m%dT%H:%M:%S.%f")
return obj
def encode_datetime(obj):
if isinstance(obj, datetime.datetime):
return {'__datetime__': True, 'as_str': obj.strftime("%Y%m%dT%H:%M:%S.%f")}
return obj
packed_dict = msgpack.packb(useful_dict, default=encode_datetime)
this_dict_again = msgpack.unpackb(packed_dict, object_hook=decode_datetime)
```
`Unpacker`'s `object_hook` callback receives a dict; the
`object_pairs_hook` callback may instead be used to receive a list of
key-value pairs.
NOTE: msgpack can encode datetime with tzinfo into standard ext type for now.
See `datetime` option in `Packer` docstring.
### Extended types
It is also possible to pack/unpack custom data types using the **ext** type.
```pycon
>>> import msgpack
>>> import array
>>> def default(obj):
... if isinstance(obj, array.array) and obj.typecode == 'd':
... return msgpack.ExtType(42, obj.tostring())
... raise TypeError("Unknown type: %r" % (obj,))
...
>>> def ext_hook(code, data):
... if code == 42:
... a = array.array('d')
... a.fromstring(data)
... return a
... return ExtType(code, data)
...
>>> data = array.array('d', [1.2, 3.4])
>>> packed = msgpack.packb(data, default=default)
>>> unpacked = msgpack.unpackb(packed, ext_hook=ext_hook)
>>> data == unpacked
True
```
### Advanced unpacking control
As an alternative to iteration, `Unpacker` objects provide `unpack`,
`skip`, `read_array_header`, and `read_map_header` methods. The former two
read an entire message from the stream, respectively deserializing and returning
the result, or ignoring it. The latter two methods return the number of elements
in the upcoming container, so that each element in an array, or key-value pair
in a map, can be unpacked or skipped individually.
## Notes
### String and binary types in the old MessagePack spec
Early versions of msgpack didn't distinguish string and binary types.
The type for representing both string and binary types was named **raw**.
You can pack into and unpack from this old spec using `use_bin_type=False`
and `raw=True` options.
```pycon
>>> import msgpack
>>> msgpack.unpackb(msgpack.packb([b'spam', 'eggs'], use_bin_type=False), raw=True)
[b'spam', b'eggs']
>>> msgpack.unpackb(msgpack.packb([b'spam', 'eggs'], use_bin_type=True), raw=False)
[b'spam', 'eggs']
```
### ext type
To use the **ext** type, pass a `msgpack.ExtType` object to the packer.
```pycon
>>> import msgpack
>>> packed = msgpack.packb(msgpack.ExtType(42, b'xyzzy'))
>>> msgpack.unpackb(packed)
ExtType(code=42, data='xyzzy')
```
You can use it with `default` and `ext_hook`. See below.
### Security
When unpacking data received from an unreliable source, msgpack provides
two security options.
`max_buffer_size` (default: `100*1024*1024`) limits the internal buffer size.
It is also used to limit preallocated list sizes.
`strict_map_key` (default: `True`) limits the type of map keys to bytes and str.
While the MessagePack spec doesn't limit map key types,
there is a risk of a hash DoS.
If you need to support other types for map keys, use `strict_map_key=False`.
### Performance tips
CPython's GC starts when the number of allocated objects grows.
This means unpacking may trigger unnecessary GC.
You can use `gc.disable()` when unpacking a large message.
A list is the default sequence type in Python.
However, a tuple is lighter than a list.
You can use `use_list=False` while unpacking when performance is important.
## Major breaking changes in the history
### msgpack 0.5
The package name on PyPI was changed from `msgpack-python` to `msgpack` in 0.5.
When upgrading from msgpack-0.4 or earlier, do `pip uninstall msgpack-python` before
`pip install -U msgpack`.
### msgpack 1.0
* Python 2 support
* The extension module no longer supports Python 2.
The pure Python implementation (`msgpack.fallback`) is used for Python 2.
* msgpack 1.0.6 drops official support of Python 2.7, as pip and
GitHub Action "setup-python" no longer supports Python 2.7.
* Packer
* Packer uses `use_bin_type=True` by default.
Bytes are encoded in the bin type in MessagePack.
* The `encoding` option is removed. UTF-8 is always used.
* Unpacker
* Unpacker uses `raw=False` by default. It assumes str values are valid UTF-8 strings
and decodes them to Python str (Unicode) objects.
* `encoding` option is removed. You can use `raw=True` to support old format (e.g. unpack into bytes, not str).
* The default value of `max_buffer_size` is changed from 0 to 100 MiB to avoid DoS attacks.
You need to pass `max_buffer_size=0` if you have large but safe data.
* The default value of `strict_map_key` is changed to True to avoid hash DoS.
You need to pass `strict_map_key=False` if you have data that contain map keys
whose type is neither bytes nor str.

View file

@ -1,302 +0,0 @@
======================
MessagePack for Python
======================
.. image:: https://travis-ci.org/msgpack/msgpack-python.svg?branch=master
:target: https://travis-ci.org/msgpack/msgpack-python
:alt: Build Status
.. image:: https://readthedocs.org/projects/msgpack-python/badge/?version=latest
:target: https://msgpack-python.readthedocs.io/en/latest/?badge=latest
:alt: Documentation Status
IMPORTANT: Upgrading from msgpack-0.4
--------------------------------------
TL;DR: When upgrading from msgpack-0.4 or earlier, don't do `pip install -U msgpack-python`.
Do `pip uninstall msgpack-python; pip install msgpack` instead.
Package name on PyPI was changed to msgpack from 0.5.
I upload transitional package (msgpack-python 0.5 which depending on msgpack)
for smooth transition from msgpack-python to msgpack.
Sadly, this doesn't work for upgrade install. After `pip install -U msgpack-python`,
msgpack is removed and `import msgpack` fail.
What's this
-----------
`MessagePack <https://msgpack.org/>`_ is an efficient binary serialization format.
It lets you exchange data among multiple languages like JSON.
But it's faster and smaller.
This package provides CPython bindings for reading and writing MessagePack data.
Install
-------
::
$ pip install msgpack
PyPy
^^^^
msgpack provides a pure Python implementation. PyPy can use this.
Windows
^^^^^^^
When you can't use a binary distribution, you need to install Visual Studio
or Windows SDK on Windows.
Without extension, using pure Python implementation on CPython runs slowly.
For Python 2.7, `Microsoft Visual C++ Compiler for Python 2.7 <https://www.microsoft.com/en-us/download/details.aspx?id=44266>`_
is recommended solution.
For Python 3.5, `Microsoft Visual Studio 2015 <https://www.visualstudio.com/en-us/products/vs-2015-product-editions.aspx>`_
Community Edition or Express Edition can be used to build extension module.
How to use
----------
One-shot pack & unpack
^^^^^^^^^^^^^^^^^^^^^^
Use ``packb`` for packing and ``unpackb`` for unpacking.
msgpack provides ``dumps`` and ``loads`` as an alias for compatibility with
``json`` and ``pickle``.
``pack`` and ``dump`` packs to a file-like object.
``unpack`` and ``load`` unpacks from a file-like object.
.. code-block:: pycon
>>> import msgpack
>>> msgpack.packb([1, 2, 3], use_bin_type=True)
'\x93\x01\x02\x03'
>>> msgpack.unpackb(_)
[1, 2, 3]
``unpack`` unpacks msgpack's array to Python's list, but can also unpack to tuple:
.. code-block:: pycon
>>> msgpack.unpackb(b'\x93\x01\x02\x03', use_list=False)
(1, 2, 3)
You should always specify the ``use_list`` keyword argument for backward compatibility.
See performance issues relating to `use_list option`_ below.
Read the docstring for other options.
Streaming unpacking
^^^^^^^^^^^^^^^^^^^
``Unpacker`` is a "streaming unpacker". It unpacks multiple objects from one
stream (or from bytes provided through its ``feed`` method).
.. code-block:: python
import msgpack
from io import BytesIO
buf = BytesIO()
for i in range(100):
buf.write(msgpack.packb(range(i), use_bin_type=True))
buf.seek(0)
unpacker = msgpack.Unpacker(buf)
for unpacked in unpacker:
print(unpacked)
Packing/unpacking of custom data type
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
It is also possible to pack/unpack custom data types. Here is an example for
``datetime.datetime``.
.. code-block:: python
import datetime
import msgpack
useful_dict = {
"id": 1,
"created": datetime.datetime.now(),
}
def decode_datetime(obj):
if b'__datetime__' in obj:
obj = datetime.datetime.strptime(obj["as_str"], "%Y%m%dT%H:%M:%S.%f")
return obj
def encode_datetime(obj):
if isinstance(obj, datetime.datetime):
return {'__datetime__': True, 'as_str': obj.strftime("%Y%m%dT%H:%M:%S.%f")}
return obj
packed_dict = msgpack.packb(useful_dict, default=encode_datetime, use_bin_type=True)
this_dict_again = msgpack.unpackb(packed_dict, object_hook=decode_datetime)
``Unpacker``'s ``object_hook`` callback receives a dict; the
``object_pairs_hook`` callback may instead be used to receive a list of
key-value pairs.
Extended types
^^^^^^^^^^^^^^
It is also possible to pack/unpack custom data types using the **ext** type.
.. code-block:: pycon
>>> import msgpack
>>> import array
>>> def default(obj):
... if isinstance(obj, array.array) and obj.typecode == 'd':
... return msgpack.ExtType(42, obj.tostring())
... raise TypeError("Unknown type: %r" % (obj,))
...
>>> def ext_hook(code, data):
... if code == 42:
... a = array.array('d')
... a.fromstring(data)
... return a
... return ExtType(code, data)
...
>>> data = array.array('d', [1.2, 3.4])
>>> packed = msgpack.packb(data, default=default, use_bin_type=True)
>>> unpacked = msgpack.unpackb(packed, ext_hook=ext_hook)
>>> data == unpacked
True
Advanced unpacking control
^^^^^^^^^^^^^^^^^^^^^^^^^^
As an alternative to iteration, ``Unpacker`` objects provide ``unpack``,
``skip``, ``read_array_header`` and ``read_map_header`` methods. The former two
read an entire message from the stream, respectively de-serialising and returning
the result, or ignoring it. The latter two methods return the number of elements
in the upcoming container, so that each element in an array, or key-value pair
in a map, can be unpacked or skipped individually.
Each of these methods may optionally write the packed data it reads to a
callback function:
.. code-block:: python
from io import BytesIO
def distribute(unpacker, get_worker):
nelems = unpacker.read_map_header()
for i in range(nelems):
# Select a worker for the given key
key = unpacker.unpack()
worker = get_worker(key)
# Send the value as a packed message to worker
bytestream = BytesIO()
unpacker.skip(bytestream.write)
worker.send(bytestream.getvalue())
Notes
-----
string and binary type
^^^^^^^^^^^^^^^^^^^^^^
Early versions of msgpack didn't distinguish string and binary types (like Python 1).
The type for representing both string and binary types was named **raw**.
For backward compatibility reasons, msgpack-python will still default all
strings to byte strings, unless you specify the `use_bin_type=True` option in
the packer. If you do so, it will use a non-standard type called **bin** to
serialize byte arrays, and **raw** becomes to mean **str**. If you want to
distinguish **bin** and **raw** in the unpacker, specify `encoding='utf-8'`.
**In future version, default value of ``use_bin_type`` will be changed to ``True``.
To avoid this change will break your code, you must specify it explicitly
even when you want to use old format.**
Note that Python 2 defaults to byte-arrays over Unicode strings:
.. code-block:: pycon
>>> import msgpack
>>> msgpack.unpackb(msgpack.packb([b'spam', u'eggs']))
['spam', 'eggs']
>>> msgpack.unpackb(msgpack.packb([b'spam', u'eggs'], use_bin_type=True),
encoding='utf-8')
['spam', u'eggs']
This is the same code in Python 3 (same behaviour, but Python 3 has a
different default):
.. code-block:: pycon
>>> import msgpack
>>> msgpack.unpackb(msgpack.packb([b'spam', u'eggs']))
[b'spam', b'eggs']
>>> msgpack.unpackb(msgpack.packb([b'spam', u'eggs'], use_bin_type=True),
encoding='utf-8')
[b'spam', 'eggs']
ext type
^^^^^^^^
To use the **ext** type, pass ``msgpack.ExtType`` object to packer.
.. code-block:: pycon
>>> import msgpack
>>> packed = msgpack.packb(msgpack.ExtType(42, b'xyzzy'))
>>> msgpack.unpackb(packed)
ExtType(code=42, data='xyzzy')
You can use it with ``default`` and ``ext_hook``. See below.
Note about performance
----------------------
GC
^^
CPython's GC starts when growing allocated object.
This means unpacking may cause useless GC.
You can use ``gc.disable()`` when unpacking large message.
use_list option
^^^^^^^^^^^^^^^
List is the default sequence type of Python.
But tuple is lighter than list.
You can use ``use_list=False`` while unpacking when performance is important.
Python's dict can't use list as key and MessagePack allows array for key of mapping.
``use_list=False`` allows unpacking such message.
Another way to unpacking such object is using ``object_pairs_hook``.
Development
-----------
Test
^^^^
MessagePack uses `pytest` for testing.
Run test with following command:
$ pytest -v test
..
vim: filetype=rst

5
SECURITY.md Normal file
View file

@ -0,0 +1,5 @@
## Security contact information
To report a security vulnerability, please use the
[Tidelift security contact](https://tidelift.com/security).
Tidelift will coordinate the fix and disclosure.

View file

@ -1,45 +0,0 @@
environment:
matrix:
# For Python versions available on Appveyor, see
# http://www.appveyor.com/docs/installed-software#python
- PYTHON: "C:\\Python36"
install:
# We need wheel installed to build wheels
- "%PYTHON%\\python.exe -m pip install -U cython"
- "%PYTHON%\\Scripts\\cython --cplus msgpack/_packer.pyx msgpack/_unpacker.pyx"
build: off
test_script:
# Put your test command here.
# Note that you must use the environment variable %PYTHON% to refer to
# the interpreter you're using - Appveyor does not do anything special
# to put the Python version you want to use on PATH.
- set PYTHON="C:\\Python27"
- ci\\runtests.bat
- set PYTHON="C:\\Python27-x64"
- ci\\runtests.bat
- set PYTHON="C:\\Python35"
- ci\\runtests.bat
- set PYTHON="C:\\Python35-x64"
- ci\\runtests.bat
- set PYTHON="C:\\Python36"
- ci\\runtests.bat
- set PYTHON="C:\\Python36-x64"
- ci\\runtests.bat
after_test:
# This step builds your wheels.
# Again, you need to use %PYTHON% to get the correct interpreter
artifacts:
# bdist_wheel puts your built wheel in the dist directory
- path: dist\*.whl
#on_success:
# You can use this step to upload your artifacts to a public website.
# See Appveyor's documentation for more details. Or you can simply
# access your wheels from the Appveyor "artifacts" tab for your build.
# vim: set shiftwidth=2

View file

@ -1,6 +1,8 @@
from msgpack import fallback
try:
from msgpack import _unpacker, _packer
from msgpack import _cmsgpack
has_ext = True
except ImportError:
has_ext = False
@ -9,26 +11,28 @@ import timeit
def profile(name, func):
times = timeit.repeat(func, number=1000, repeat=4)
times = ', '.join(["%8f" % t for t in times])
times = ", ".join(["%8f" % t for t in times])
print("%-30s %40s" % (name, times))
def simple(name, data):
if has_ext:
packer = _packer.Packer()
packer = _cmsgpack.Packer()
profile("packing %s (ext)" % name, lambda: packer.pack(data))
packer = fallback.Packer()
profile('packing %s (fallback)' % name, lambda: packer.pack(data))
profile("packing %s (fallback)" % name, lambda: packer.pack(data))
data = packer.pack(data)
if has_ext:
profile('unpacking %s (ext)' % name, lambda: _unpacker.unpackb(data))
profile('unpacking %s (fallback)' % name, lambda: fallback.unpackb(data))
profile("unpacking %s (ext)" % name, lambda: _cmsgpack.unpackb(data))
profile("unpacking %s (fallback)" % name, lambda: fallback.unpackb(data))
def main():
simple("integers", [7]*10000)
simple("bytes", [b'x'*n for n in range(100)]*10)
simple("lists", [[]]*10000)
simple("dicts", [{}]*10000)
simple("integers", [7] * 10000)
simple("bytes", [b"x" * n for n in range(100)] * 10)
simple("lists", [[]] * 10000)
simple("dicts", [{}] * 10000)
main()

View file

@ -1,7 +0,0 @@
%PYTHON%\python.exe -m pip install -U pip wheel pytest
%PYTHON%\python.exe setup.py build_ext -i
%PYTHON%\python.exe setup.py install
%PYTHON%\python.exe -c "import sys; print(hex(sys.maxsize))"
%PYTHON%\python.exe -c "from msgpack import _packer, _unpacker"
%PYTHON%\python.exe -m pytest -v test
%PYTHON%\python.exe setup.py bdist_wheel

View file

@ -1,11 +1,22 @@
#!/bin/bash
DOCKER_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$DOCKER_DIR/shared.env"
set -e -x
ARCH=`uname -p`
echo "arch=$ARCH"
for V in cp36-cp36m cp35-cp35m cp27-cp27m cp27-cp27mu; do
ls /opt/python
for V in "${PYTHON_VERSIONS[@]}"; do
PYBIN=/opt/python/$V/bin
rm -rf build/ # Avoid lib build by narrow Python is used by wide python
$PYBIN/python setup.py bdist_wheel -p manylinux1_${ARCH}
$PYBIN/python -m build -w
done
cd dist
for whl in *.whl; do
auditwheel repair "$whl"
rm "$whl"
done

View file

@ -1,14 +1,17 @@
#!/bin/bash
DOCKER_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$DOCKER_DIR/shared.env"
set -e -x
for V in cp36-cp36m cp35-cp35m cp27-cp27m cp27-cp27mu; do
for V in "${PYTHON_VERSIONS[@]}"; do
PYBIN=/opt/python/$V/bin
$PYBIN/python setup.py install
rm -rf build/ # Avoid lib build by narrow Python is used by wide python
$PYBIN/pip install pytest
pushd test # prevent importing msgpack package in current directory.
$PYBIN/python -c 'import sys; print(hex(sys.maxsize))'
$PYBIN/python -c 'from msgpack import _packer, _unpacker'
$PYBIN/py.test -v
$PYBIN/python -c 'from msgpack import _cmsgpack' # Ensure extension is available
$PYBIN/pytest -v .
popd
done

7
docker/shared.env Normal file
View file

@ -0,0 +1,7 @@
PYTHON_VERSIONS=(
cp310-cp310
cp39-cp39
cp38-cp38
cp37-cp37m
cp36-cp36m
)

View file

@ -153,7 +153,7 @@ doctest:
"results in $(BUILDDIR)/doctest/output.txt."
serve: html
cd _build/html && python3 -m http.server
python3 -m http.server -d _build/html
zip: html
cd _build/html && zip -r ../../../msgpack-doc.zip .

1
docs/_static/README.txt vendored Normal file
View file

@ -0,0 +1 @@
Sphinx will copy the contents of docs/_static/ directory to the build location.

32
docs/advanced.rst Normal file
View file

@ -0,0 +1,32 @@
Advanced usage
===============
Packer
------
autoreset
~~~~~~~~~
When you used ``autoreset=False`` option of :class:`~msgpack.Packer`,
``pack()`` method doesn't return packed ``bytes``.
You can use :meth:`~msgpack.Packer.bytes` or :meth:`~msgpack.Packer.getbuffer` to
get packed data.
``bytes()`` returns ``bytes`` object. ``getbuffer()`` returns some bytes-like
object. It's concrete type is implement detail and it will be changed in future
versions.
You can reduce temporary bytes object by using ``Unpacker.getbuffer()``.
.. code-block:: python
packer = Packer(use_bin_type=True, autoreset=False)
packer.pack([1, 2])
packer.pack([3, 4])
with open('data.bin', 'wb') as f:
f.write(packer.getbuffer())
packer.reset() # reset internal buffer

View file

@ -5,19 +5,19 @@ API reference
.. autofunction:: pack
:func:`dump` is alias for :func:`pack`
``dump()`` is an alias for :func:`pack`
.. autofunction:: packb
:func:`dumps` is alias for :func:`packb`
``dumps()`` is an alias for :func:`packb`
.. autofunction:: unpack
:func:`load` is alias for :func:`unpack`
``load()`` is an alias for :func:`unpack`
.. autofunction:: unpackb
:func:`loads` is alias for :func:`unpackb`
``loads()`` is an alias for :func:`unpackb`
.. autoclass:: Packer
:members:
@ -27,6 +27,10 @@ API reference
.. autoclass:: ExtType
.. autoclass:: Timestamp
:members:
:special-members: __init__
exceptions
----------

View file

@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-
#
# msgpack documentation build configuration file, created by
# sphinx-quickstart on Sun Feb 24 14:20:50 2013.
#
@ -11,37 +9,37 @@
# All configuration values have a default; values that are commented out
# serve to show the default.
import sys, os
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#sys.path.insert(0, os.path.abspath('.'))
# import os
# import sys
# sys.path.insert(0, os.path.abspath('..'))
# -- General configuration -----------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'
# needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode']
extensions = ["sphinx.ext.autodoc", "sphinx.ext.viewcode"]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
templates_path = ["_templates"]
# The suffix of source filenames.
source_suffix = '.rst'
source_suffix = ".rst"
# The encoding of source files.
#source_encoding = 'utf-8-sig'
# source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
master_doc = "index"
# General information about the project.
project = u'msgpack'
copyright = u'2013, INADA Naoki'
project = "msgpack"
copyright = "Inada Naoki"
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
@ -49,176 +47,170 @@ copyright = u'2013, INADA Naoki'
#
# The short X.Y version.
# The full version, including alpha/beta/rc tags.
version = release = '0.5'
version = release = "1.0"
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None
# language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
# today_fmt = '%B %d, %Y'
today_fmt = "%Y-%m-%d"
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build']
exclude_patterns = ["_build"]
# The reST default role (used for this markup: `text`) to use for all documents.
#default_role = None
# default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True
# add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False
# show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
pygments_style = "sphinx"
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
# modindex_common_prefix = []
# -- Options for HTML output ---------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'sphinxdoc'
html_theme = "sphinx_rtd_theme"
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}
# html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []
# html_theme_path = []
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
#html_title = None
# html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
# html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None
# html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
# html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
html_static_path = ["_static"]
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'
# html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
# html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
# html_additional_pages = {}
# If false, no module index is generated.
#html_domain_indices = True
# html_domain_indices = True
# If false, no index is generated.
#html_use_index = True
# html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# html_split_index = False
# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True
# html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True
# html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True
# html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None
# html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = 'msgpackdoc'
htmlhelp_basename = "msgpackdoc"
# -- Options for LaTeX output --------------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#'preamble': '',
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#'preamble': '',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', 'msgpack.tex', u'msgpack Documentation',
u'Author', 'manual'),
("index", "msgpack.tex", "msgpack Documentation", "Author", "manual"),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
# latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False
# latex_use_parts = False
# If true, show page references after internal links.
#latex_show_pagerefs = False
# latex_show_pagerefs = False
# If true, show URL addresses after external links.
#latex_show_urls = False
# latex_show_urls = False
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# latex_appendices = []
# If false, no module index is generated.
#latex_domain_indices = True
# latex_domain_indices = True
# -- Options for manual page output --------------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'msgpack', u'msgpack Documentation',
[u'Author'], 1)
]
man_pages = [("index", "msgpack", "msgpack Documentation", ["Author"], 1)]
# If true, show URL addresses after external links.
#man_show_urls = False
# man_show_urls = False
# -- Options for Texinfo output ------------------------------------------------
@ -227,59 +219,65 @@ man_pages = [
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
('index', 'msgpack', u'msgpack Documentation',
u'Author', 'msgpack', 'One line description of project.',
'Miscellaneous'),
(
"index",
"msgpack",
"msgpack Documentation",
"Author",
"msgpack",
"One line description of project.",
"Miscellaneous",
),
]
# Documents to append as an appendix to all manuals.
#texinfo_appendices = []
# texinfo_appendices = []
# If false, no module index is generated.
#texinfo_domain_indices = True
# texinfo_domain_indices = True
# How to display URL addresses: 'footnote', 'no', or 'inline'.
#texinfo_show_urls = 'footnote'
# texinfo_show_urls = 'footnote'
# -- Options for Epub output ---------------------------------------------------
# Bibliographic Dublin Core info.
epub_title = u'msgpack'
epub_author = u'Author'
epub_publisher = u'Author'
epub_copyright = u'2013, Author'
epub_title = "msgpack"
epub_author = "Author"
epub_publisher = "Author"
epub_copyright = "2013, Author"
# The language of the text. It defaults to the language option
# or en if the language is not set.
#epub_language = ''
# epub_language = ''
# The scheme of the identifier. Typical schemes are ISBN or URL.
#epub_scheme = ''
# epub_scheme = ''
# The unique identifier of the text. This can be a ISBN number
# or the project homepage.
#epub_identifier = ''
# epub_identifier = ''
# A unique identification for the text.
#epub_uid = ''
# epub_uid = ''
# A tuple containing the cover image and cover page html template filenames.
#epub_cover = ()
# epub_cover = ()
# HTML files that should be inserted before the pages created by sphinx.
# The format is a list of tuples containing the path and title.
#epub_pre_files = []
# epub_pre_files = []
# HTML files shat should be inserted after the pages created by sphinx.
# The format is a list of tuples containing the path and title.
#epub_post_files = []
# epub_post_files = []
# A list of files that should not be packed into the epub file.
#epub_exclude_files = []
# epub_exclude_files = []
# The depth of the table of contents in toc.ncx.
#epub_tocdepth = 3
# epub_tocdepth = 3
# Allow duplicate toc entries.
#epub_tocdup = True
# epub_tocdup = True

View file

@ -8,3 +8,4 @@ language data exchange.
:maxdepth: 1
api
advanced

2
docs/requirements.txt Normal file
View file

@ -0,0 +1,2 @@
sphinx~=7.3.7
sphinx-rtd-theme~=2.0.0

View file

@ -1,31 +1,20 @@
# coding: utf-8
from msgpack._version import version
from msgpack.exceptions import *
from collections import namedtuple
class ExtType(namedtuple('ExtType', 'code data')):
"""ExtType represents ext type in msgpack."""
def __new__(cls, code, data):
if not isinstance(code, int):
raise TypeError("code must be int")
if not isinstance(data, bytes):
raise TypeError("data must be bytes")
if not 0 <= code <= 127:
raise ValueError("code must be 0~127")
return super(ExtType, cls).__new__(cls, code, data)
# ruff: noqa: F401
import os
if os.environ.get('MSGPACK_PUREPYTHON'):
from msgpack.fallback import Packer, unpack, unpackb, Unpacker
from .exceptions import * # noqa: F403
from .ext import ExtType, Timestamp
version = (1, 1, 2)
__version__ = "1.1.2"
if os.environ.get("MSGPACK_PUREPYTHON"):
from .fallback import Packer, Unpacker, unpackb
else:
try:
from msgpack._packer import Packer
from msgpack._unpacker import unpack, unpackb, Unpacker
from ._cmsgpack import Packer, Unpacker, unpackb
except ImportError:
from msgpack.fallback import Packer, unpack, unpackb, Unpacker
from .fallback import Packer, Unpacker, unpackb
def pack(o, stream, **kwargs):
@ -46,6 +35,18 @@ def packb(o, **kwargs):
"""
return Packer(**kwargs).pack(o)
def unpack(stream, **kwargs):
"""
Unpack an object from `stream`.
Raises `ExtraData` when `stream` contains extra bytes.
See :class:`Unpacker` for options.
"""
data = stream.read()
return unpackb(data, **kwargs)
# alias for compatibility to simplejson/marshal/pickle.
load = unpack
loads = unpackb

12
msgpack/_cmsgpack.pyx Normal file
View file

@ -0,0 +1,12 @@
#cython: embedsignature=True, c_string_encoding=ascii, language_level=3
#cython: freethreading_compatible = True
import cython
from cpython.datetime cimport import_datetime, datetime_new
import_datetime()
import datetime
cdef object utc = datetime.timezone.utc
cdef object epoch = datetime_new(1970, 1, 1, 0, 0, 0, 0, tz=utc)
include "_packer.pyx"
include "_unpacker.pyx"

View file

@ -1,19 +1,19 @@
# coding: utf-8
#cython: embedsignature=True
from cpython cimport *
#from cpython.exc cimport PyErr_WarnEx
from cpython.bytearray cimport PyByteArray_Check, PyByteArray_CheckExact
from cpython.datetime cimport (
PyDateTime_CheckExact, PyDelta_CheckExact,
datetime_tzinfo, timedelta_days, timedelta_seconds, timedelta_microseconds,
)
from msgpack.exceptions import PackValueError, PackOverflowError
from msgpack import ExtType
cdef ExtType
cdef Timestamp
from .ext import ExtType, Timestamp
cdef extern from "Python.h":
int PyMemoryView_Check(object obj)
int PyByteArray_Check(object obj)
int PyByteArray_CheckExact(object obj)
cdef extern from "pack.h":
struct msgpack_packer:
@ -22,24 +22,24 @@ cdef extern from "pack.h":
size_t buf_size
bint use_bin_type
int msgpack_pack_int(msgpack_packer* pk, int d)
int msgpack_pack_nil(msgpack_packer* pk)
int msgpack_pack_true(msgpack_packer* pk)
int msgpack_pack_false(msgpack_packer* pk)
int msgpack_pack_long(msgpack_packer* pk, long d)
int msgpack_pack_long_long(msgpack_packer* pk, long long d)
int msgpack_pack_unsigned_long_long(msgpack_packer* pk, unsigned long long d)
int msgpack_pack_float(msgpack_packer* pk, float d)
int msgpack_pack_double(msgpack_packer* pk, double d)
int msgpack_pack_array(msgpack_packer* pk, size_t l)
int msgpack_pack_map(msgpack_packer* pk, size_t l)
int msgpack_pack_raw(msgpack_packer* pk, size_t l)
int msgpack_pack_bin(msgpack_packer* pk, size_t l)
int msgpack_pack_raw_body(msgpack_packer* pk, char* body, size_t l)
int msgpack_pack_ext(msgpack_packer* pk, char typecode, size_t l)
int msgpack_pack_nil(msgpack_packer* pk) except -1
int msgpack_pack_true(msgpack_packer* pk) except -1
int msgpack_pack_false(msgpack_packer* pk) except -1
int msgpack_pack_long_long(msgpack_packer* pk, long long d) except -1
int msgpack_pack_unsigned_long_long(msgpack_packer* pk, unsigned long long d) except -1
int msgpack_pack_float(msgpack_packer* pk, float d) except -1
int msgpack_pack_double(msgpack_packer* pk, double d) except -1
int msgpack_pack_array(msgpack_packer* pk, size_t l) except -1
int msgpack_pack_map(msgpack_packer* pk, size_t l) except -1
int msgpack_pack_raw(msgpack_packer* pk, size_t l) except -1
int msgpack_pack_bin(msgpack_packer* pk, size_t l) except -1
int msgpack_pack_raw_body(msgpack_packer* pk, char* body, size_t l) except -1
int msgpack_pack_ext(msgpack_packer* pk, char typecode, size_t l) except -1
int msgpack_pack_timestamp(msgpack_packer* x, long long seconds, unsigned long nanoseconds) except -1
cdef int DEFAULT_RECURSE_LIMIT=511
cdef size_t ITEM_LIMIT = (2**32)-1
cdef long long ITEM_LIMIT = (2**32)-1
cdef inline int PyBytesLike_Check(object o):
@ -50,11 +50,11 @@ cdef inline int PyBytesLike_CheckExact(object o):
return PyBytes_CheckExact(o) or PyByteArray_CheckExact(o)
cdef class Packer(object):
cdef class Packer:
"""
MessagePack Packer
usage::
Usage::
packer = Packer()
astream.write(packer.pack(a))
@ -62,7 +62,8 @@ cdef class Packer(object):
Packer's constructor has some keyword arguments:
:param callable default:
:param default:
When specified, it should be callable.
Convert user type to builtin type that Packer supports.
See also simplejson's document.
@ -75,9 +76,7 @@ cdef class Packer(object):
:param bool use_bin_type:
Use bin type introduced in msgpack spec 2.0 for bytes.
It also enables str8 type for unicode.
Current default value is false, but it will be changed to true
in future version. You should specify it explicitly.
It also enables str8 type for unicode. (default: True)
:param bool strict_types:
If set to true, types will be checked to be exact. Derived classes
@ -87,232 +86,235 @@ cdef class Packer(object):
This is useful when trying to implement accurate serialization
for python types.
:param str encoding:
(deprecated) Convert unicode to bytes with this encoding. (default: 'utf-8')
:param bool datetime:
If set to true, datetime with tzinfo is packed into Timestamp type.
Note that the tzinfo is stripped in the timestamp.
You can get UTC datetime with `timestamp=3` option of the Unpacker.
:param str unicode_errors:
(deprecated) Error handler for encoding unicode. (default: 'strict')
The error handler for encoding unicode. (default: 'strict')
DO NOT USE THIS!! This option is kept for very specific usage.
:param int buf_size:
The size of the internal buffer. (default: 256*1024)
Useful if serialisation size can be correctly estimated,
avoid unnecessary reallocations.
"""
cdef msgpack_packer pk
cdef object _default
cdef object _bencoding
cdef object _berrors
cdef char *encoding
cdef char *unicode_errors
cdef const char *unicode_errors
cdef size_t exports # number of exported buffers
cdef bint strict_types
cdef bool use_float
cdef bint use_float
cdef bint autoreset
cdef bint datetime
def __cinit__(self):
cdef int buf_size = 1024*1024
def __cinit__(self, buf_size=256*1024, **_kwargs):
self.pk.buf = <char*> PyMem_Malloc(buf_size)
if self.pk.buf == NULL:
raise MemoryError("Unable to allocate internal buffer.")
self.pk.buf_size = buf_size
self.pk.length = 0
self.exports = 0
def __init__(self, default=None, encoding='utf-8', unicode_errors='strict',
bint use_single_float=False, bint autoreset=True, bint use_bin_type=False,
bint strict_types=False):
def __dealloc__(self):
PyMem_Free(self.pk.buf)
self.pk.buf = NULL
assert self.exports == 0
cdef _check_exports(self):
if self.exports > 0:
raise BufferError("Existing exports of data: Packer cannot be changed")
@cython.critical_section
def __init__(self, *, default=None,
bint use_single_float=False, bint autoreset=True, bint use_bin_type=True,
bint strict_types=False, bint datetime=False, unicode_errors=None,
buf_size=256*1024):
self.use_float = use_single_float
self.strict_types = strict_types
self.autoreset = autoreset
self.datetime = datetime
self.pk.use_bin_type = use_bin_type
if default is not None:
if not PyCallable_Check(default):
raise TypeError("default must be a callable.")
self._default = default
if encoding is None:
self.encoding = NULL
self._berrors = unicode_errors
if unicode_errors is None:
self.unicode_errors = NULL
else:
if isinstance(encoding, unicode):
self._bencoding = encoding.encode('ascii')
else:
self._bencoding = encoding
self.encoding = PyBytes_AsString(self._bencoding)
if isinstance(unicode_errors, unicode):
self._berrors = unicode_errors.encode('ascii')
else:
self._berrors = unicode_errors
self.unicode_errors = PyBytes_AsString(self._berrors)
self.unicode_errors = self._berrors
def __dealloc__(self):
PyMem_Free(self.pk.buf)
self.pk.buf = NULL
cdef int _pack(self, object o, int nest_limit=DEFAULT_RECURSE_LIMIT) except -1:
# returns -2 when default should(o) be called
cdef int _pack_inner(self, object o, bint will_default, int nest_limit) except -1:
cdef long long llval
cdef unsigned long long ullval
cdef long longval
cdef float fval
cdef double dval
cdef char* rawval
cdef int ret
cdef dict d
cdef size_t L
cdef int default_used = 0
cdef bint strict_types = self.strict_types
cdef unsigned long ulval
cdef const char* rawval
cdef Py_ssize_t L
cdef Py_buffer view
cdef bint strict = self.strict_types
if nest_limit < 0:
raise PackValueError("recursion limit exceeded.")
while True:
if o is None:
ret = msgpack_pack_nil(&self.pk)
elif PyBool_Check(o) if strict_types else isinstance(o, bool):
if o:
ret = msgpack_pack_true(&self.pk)
if o is None:
msgpack_pack_nil(&self.pk)
elif o is True:
msgpack_pack_true(&self.pk)
elif o is False:
msgpack_pack_false(&self.pk)
elif PyLong_CheckExact(o) if strict else PyLong_Check(o):
try:
if o > 0:
ullval = o
msgpack_pack_unsigned_long_long(&self.pk, ullval)
else:
ret = msgpack_pack_false(&self.pk)
elif PyLong_CheckExact(o) if strict_types else PyLong_Check(o):
# PyInt_Check(long) is True for Python 3.
# So we should test long before int.
try:
if o > 0:
ullval = o
ret = msgpack_pack_unsigned_long_long(&self.pk, ullval)
else:
llval = o
ret = msgpack_pack_long_long(&self.pk, llval)
except OverflowError as oe:
if not default_used and self._default is not None:
o = self._default(o)
default_used = True
continue
else:
raise PackOverflowError("Integer value out of range")
elif PyInt_CheckExact(o) if strict_types else PyInt_Check(o):
longval = o
ret = msgpack_pack_long(&self.pk, longval)
elif PyFloat_CheckExact(o) if strict_types else PyFloat_Check(o):
if self.use_float:
fval = o
ret = msgpack_pack_float(&self.pk, fval)
llval = o
msgpack_pack_long_long(&self.pk, llval)
except OverflowError as oe:
if will_default:
return -2
else:
dval = o
ret = msgpack_pack_double(&self.pk, dval)
elif PyBytesLike_CheckExact(o) if strict_types else PyBytesLike_Check(o):
L = len(o)
if L > ITEM_LIMIT:
raise PackValueError("%s is too large" % type(o).__name__)
rawval = o
ret = msgpack_pack_bin(&self.pk, L)
if ret == 0:
ret = msgpack_pack_raw_body(&self.pk, rawval, L)
elif PyUnicode_CheckExact(o) if strict_types else PyUnicode_Check(o):
if not self.encoding:
raise TypeError("Can't encode unicode string: no encoding is specified")
o = PyUnicode_AsEncodedString(o, self.encoding, self.unicode_errors)
L = len(o)
if L > ITEM_LIMIT:
raise PackValueError("unicode string is too large")
rawval = o
ret = msgpack_pack_raw(&self.pk, L)
if ret == 0:
ret = msgpack_pack_raw_body(&self.pk, rawval, L)
elif PyDict_CheckExact(o):
d = <dict>o
L = len(d)
if L > ITEM_LIMIT:
raise PackValueError("dict is too large")
ret = msgpack_pack_map(&self.pk, L)
if ret == 0:
for k, v in d.iteritems():
ret = self._pack(k, nest_limit-1)
if ret != 0: break
ret = self._pack(v, nest_limit-1)
if ret != 0: break
elif not strict_types and PyDict_Check(o):
L = len(o)
if L > ITEM_LIMIT:
raise PackValueError("dict is too large")
ret = msgpack_pack_map(&self.pk, L)
if ret == 0:
for k, v in o.items():
ret = self._pack(k, nest_limit-1)
if ret != 0: break
ret = self._pack(v, nest_limit-1)
if ret != 0: break
elif type(o) is ExtType if strict_types else isinstance(o, ExtType):
# This should be before Tuple because ExtType is namedtuple.
longval = o.code
rawval = o.data
L = len(o.data)
if L > ITEM_LIMIT:
raise PackValueError("EXT data is too large")
ret = msgpack_pack_ext(&self.pk, longval, L)
ret = msgpack_pack_raw_body(&self.pk, rawval, L)
elif PyList_CheckExact(o) if strict_types else (PyTuple_Check(o) or PyList_Check(o)):
L = len(o)
if L > ITEM_LIMIT:
raise PackValueError("list is too large")
ret = msgpack_pack_array(&self.pk, L)
if ret == 0:
for v in o:
ret = self._pack(v, nest_limit-1)
if ret != 0: break
elif PyMemoryView_Check(o):
if PyObject_GetBuffer(o, &view, PyBUF_SIMPLE) != 0:
raise PackValueError("could not get buffer for memoryview")
L = view.len
if L > ITEM_LIMIT:
PyBuffer_Release(&view);
raise PackValueError("memoryview is too large")
ret = msgpack_pack_bin(&self.pk, L)
if ret == 0:
ret = msgpack_pack_raw_body(&self.pk, <char*>view.buf, L)
PyBuffer_Release(&view);
elif not default_used and self._default:
o = self._default(o)
default_used = 1
continue
raise OverflowError("Integer value out of range")
elif PyFloat_CheckExact(o) if strict else PyFloat_Check(o):
if self.use_float:
msgpack_pack_float(&self.pk, <float>o)
else:
raise TypeError("can't serialize %r" % (o,))
return ret
msgpack_pack_double(&self.pk, <double>o)
elif PyBytesLike_CheckExact(o) if strict else PyBytesLike_Check(o):
L = Py_SIZE(o)
if L > ITEM_LIMIT:
PyErr_Format(ValueError, b"%.200s object is too large", Py_TYPE(o).tp_name)
rawval = o
msgpack_pack_bin(&self.pk, L)
msgpack_pack_raw_body(&self.pk, rawval, L)
elif PyUnicode_CheckExact(o) if strict else PyUnicode_Check(o):
if self.unicode_errors == NULL:
rawval = PyUnicode_AsUTF8AndSize(o, &L)
if L >ITEM_LIMIT:
raise ValueError("unicode string is too large")
else:
o = PyUnicode_AsEncodedString(o, NULL, self.unicode_errors)
L = Py_SIZE(o)
if L > ITEM_LIMIT:
raise ValueError("unicode string is too large")
rawval = o
msgpack_pack_raw(&self.pk, L)
msgpack_pack_raw_body(&self.pk, rawval, L)
elif PyDict_CheckExact(o) if strict else PyDict_Check(o):
L = len(o)
if L > ITEM_LIMIT:
raise ValueError("dict is too large")
msgpack_pack_map(&self.pk, L)
for k, v in o.items():
self._pack(k, nest_limit)
self._pack(v, nest_limit)
elif type(o) is ExtType if strict else isinstance(o, ExtType):
# This should be before Tuple because ExtType is namedtuple.
rawval = o.data
L = len(o.data)
if L > ITEM_LIMIT:
raise ValueError("EXT data is too large")
msgpack_pack_ext(&self.pk, <long>o.code, L)
msgpack_pack_raw_body(&self.pk, rawval, L)
elif type(o) is Timestamp:
llval = o.seconds
ulval = o.nanoseconds
msgpack_pack_timestamp(&self.pk, llval, ulval)
elif PyList_CheckExact(o) if strict else (PyTuple_Check(o) or PyList_Check(o)):
L = Py_SIZE(o)
if L > ITEM_LIMIT:
raise ValueError("list is too large")
msgpack_pack_array(&self.pk, L)
for v in o:
self._pack(v, nest_limit)
elif PyMemoryView_Check(o):
PyObject_GetBuffer(o, &view, PyBUF_SIMPLE)
L = view.len
if L > ITEM_LIMIT:
PyBuffer_Release(&view);
raise ValueError("memoryview is too large")
try:
msgpack_pack_bin(&self.pk, L)
msgpack_pack_raw_body(&self.pk, <char*>view.buf, L)
finally:
PyBuffer_Release(&view);
elif self.datetime and PyDateTime_CheckExact(o) and datetime_tzinfo(o) is not None:
delta = o - epoch
if not PyDelta_CheckExact(delta):
raise ValueError("failed to calculate delta")
llval = timedelta_days(delta) * <long long>(24*60*60) + timedelta_seconds(delta)
ulval = timedelta_microseconds(delta) * 1000
msgpack_pack_timestamp(&self.pk, llval, ulval)
elif will_default:
return -2
elif self.datetime and PyDateTime_CheckExact(o):
# this should be later than will_default
PyErr_Format(ValueError, b"can not serialize '%.200s' object where tzinfo=None", Py_TYPE(o).tp_name)
else:
PyErr_Format(TypeError, b"can not serialize '%.200s' object", Py_TYPE(o).tp_name)
cpdef pack(self, object obj):
cdef int _pack(self, object o, int nest_limit=DEFAULT_RECURSE_LIMIT) except -1:
cdef int ret
ret = self._pack(obj, DEFAULT_RECURSE_LIMIT)
if ret == -1:
raise MemoryError
elif ret: # should not happen.
raise TypeError
if nest_limit < 0:
raise ValueError("recursion limit exceeded.")
nest_limit -= 1
if self._default is not None:
ret = self._pack_inner(o, 1, nest_limit)
if ret == -2:
o = self._default(o)
else:
return ret
return self._pack_inner(o, 0, nest_limit)
@cython.critical_section
def pack(self, object obj):
cdef int ret
self._check_exports()
try:
ret = self._pack(obj, DEFAULT_RECURSE_LIMIT)
except:
self.pk.length = 0
raise
if ret: # should not happen.
raise RuntimeError("internal error")
if self.autoreset:
buf = PyBytes_FromStringAndSize(self.pk.buf, self.pk.length)
self.pk.length = 0
return buf
@cython.critical_section
def pack_ext_type(self, typecode, data):
self._check_exports()
if len(data) > ITEM_LIMIT:
raise ValueError("ext data too large")
msgpack_pack_ext(&self.pk, typecode, len(data))
msgpack_pack_raw_body(&self.pk, data, len(data))
@cython.critical_section
def pack_array_header(self, long long size):
self._check_exports()
if size > ITEM_LIMIT:
raise PackValueError
cdef int ret = msgpack_pack_array(&self.pk, size)
if ret == -1:
raise MemoryError
elif ret: # should not happen
raise TypeError
raise ValueError("array too large")
msgpack_pack_array(&self.pk, size)
if self.autoreset:
buf = PyBytes_FromStringAndSize(self.pk.buf, self.pk.length)
self.pk.length = 0
return buf
@cython.critical_section
def pack_map_header(self, long long size):
self._check_exports()
if size > ITEM_LIMIT:
raise PackValueError
cdef int ret = msgpack_pack_map(&self.pk, size)
if ret == -1:
raise MemoryError
elif ret: # should not happen
raise TypeError
raise ValueError("map too learge")
msgpack_pack_map(&self.pk, size)
if self.autoreset:
buf = PyBytes_FromStringAndSize(self.pk.buf, self.pk.length)
self.pk.length = 0
return buf
@cython.critical_section
def pack_map_pairs(self, object pairs):
"""
Pack *pairs* as msgpack map type.
@ -320,26 +322,43 @@ cdef class Packer(object):
*pairs* should be a sequence of pairs.
(`len(pairs)` and `for k, v in pairs:` should be supported.)
"""
cdef int ret = msgpack_pack_map(&self.pk, len(pairs))
if ret == 0:
for k, v in pairs:
ret = self._pack(k)
if ret != 0: break
ret = self._pack(v)
if ret != 0: break
if ret == -1:
raise MemoryError
elif ret: # should not happen
raise TypeError
self._check_exports()
size = len(pairs)
if size > ITEM_LIMIT:
raise ValueError("map too large")
msgpack_pack_map(&self.pk, size)
for k, v in pairs:
self._pack(k)
self._pack(v)
if self.autoreset:
buf = PyBytes_FromStringAndSize(self.pk.buf, self.pk.length)
self.pk.length = 0
return buf
@cython.critical_section
def reset(self):
"""Clear internal buffer."""
"""Reset internal buffer.
This method is useful only when autoreset=False.
"""
self._check_exports()
self.pk.length = 0
@cython.critical_section
def bytes(self):
"""Return buffer content."""
"""Return internal buffer contents as bytes object"""
return PyBytes_FromStringAndSize(self.pk.buf, self.pk.length)
def getbuffer(self):
"""Return memoryview of internal buffer.
Note: Packer now supports buffer protocol. You can use memoryview(packer).
"""
return memoryview(self)
def __getbuffer__(self, Py_buffer *buffer, int flags):
PyBuffer_FillInfo(buffer, self, self.pk.buf, self.pk.length, 1, flags)
self.exports += 1
def __releasebuffer__(self, Py_buffer *buffer):
self.exports -= 1

View file

@ -1,54 +1,39 @@
# coding: utf-8
#cython: embedsignature=True
from cpython.bytes cimport (
PyBytes_AsString,
PyBytes_FromStringAndSize,
PyBytes_Size,
)
from cpython.buffer cimport (
Py_buffer,
PyObject_CheckBuffer,
PyObject_GetBuffer,
PyBuffer_Release,
PyBuffer_IsContiguous,
PyBUF_READ,
PyBUF_SIMPLE,
PyBUF_FULL_RO,
)
from cpython.mem cimport PyMem_Malloc, PyMem_Free
from cpython.object cimport PyCallable_Check
from cpython.ref cimport Py_DECREF
from cpython.exc cimport PyErr_WarnEx
from cpython cimport *
cdef extern from "Python.h":
ctypedef struct PyObject
cdef int PyObject_AsReadBuffer(object o, const void** buff, Py_ssize_t* buf_len) except -1
object PyMemoryView_GetContiguous(object obj, int buffertype, char order)
from libc.stdlib cimport *
from libc.string cimport *
from libc.limits cimport *
ctypedef unsigned long long uint64_t
from libc.stdint cimport uint64_t
from msgpack.exceptions import (
from .exceptions import (
BufferFull,
OutOfData,
UnpackValueError,
ExtraData,
FormatError,
StackError,
)
from msgpack import ExtType
from .ext import ExtType, Timestamp
cdef object giga = 1_000_000_000
cdef extern from "unpack.h":
ctypedef struct msgpack_user:
bint use_list
PyObject* object_hook
bint raw
bint has_pairs_hook # call object_hook with k-v pairs
bint strict_map_key
int timestamp
PyObject* object_hook
PyObject* list_hook
PyObject* ext_hook
char *encoding
char *unicode_errors
PyObject* timestamp_t
PyObject *giga;
PyObject *utc;
const char *unicode_errors
Py_ssize_t max_str_len
Py_ssize_t max_bin_len
Py_ssize_t max_array_len
@ -73,12 +58,16 @@ cdef extern from "unpack.h":
cdef inline init_ctx(unpack_context *ctx,
object object_hook, object object_pairs_hook,
object list_hook, object ext_hook,
bint use_list, char* encoding, char* unicode_errors,
bint use_list, bint raw, int timestamp,
bint strict_map_key,
const char* unicode_errors,
Py_ssize_t max_str_len, Py_ssize_t max_bin_len,
Py_ssize_t max_array_len, Py_ssize_t max_map_len,
Py_ssize_t max_ext_len):
unpack_init(ctx)
ctx.user.use_list = use_list
ctx.user.raw = raw
ctx.user.strict_map_key = strict_map_key
ctx.user.object_hook = ctx.user.list_hook = <PyObject*>NULL
ctx.user.max_str_len = max_str_len
ctx.user.max_bin_len = max_bin_len
@ -112,7 +101,14 @@ cdef inline init_ctx(unpack_context *ctx,
raise TypeError("ext_hook must be a callable.")
ctx.user.ext_hook = <PyObject*>ext_hook
ctx.user.encoding = encoding
if timestamp < 0 or 3 < timestamp:
raise ValueError("timestamp must be 0..3")
# Add Timestamp type to the user object so it may be used in unpack.h
ctx.user.timestamp = timestamp
ctx.user.timestamp_t = <PyObject*>Timestamp
ctx.user.giga = <PyObject*>giga
ctx.user.utc = <PyObject*>utc
ctx.user.unicode_errors = unicode_errors
def default_read_extended_type(typecode, data):
@ -121,53 +117,48 @@ def default_read_extended_type(typecode, data):
cdef inline int get_data_from_buffer(object obj,
Py_buffer *view,
char **buf,
Py_ssize_t *buffer_len,
int *new_protocol) except 0:
Py_ssize_t *buffer_len) except 0:
cdef object contiguous
cdef Py_buffer tmp
if PyObject_CheckBuffer(obj):
new_protocol[0] = 1
if PyObject_GetBuffer(obj, view, PyBUF_FULL_RO) == -1:
raise
if view.itemsize != 1:
PyBuffer_Release(view)
raise BufferError("cannot unpack from multi-byte object")
if PyBuffer_IsContiguous(view, 'A') == 0:
PyBuffer_Release(view)
# create a contiguous copy and get buffer
contiguous = PyMemoryView_GetContiguous(obj, PyBUF_READ, 'C')
PyObject_GetBuffer(contiguous, view, PyBUF_SIMPLE)
# view must hold the only reference to contiguous,
# so memory is freed when view is released
Py_DECREF(contiguous)
buffer_len[0] = view.len
buf[0] = <char*> view.buf
return 1
else:
new_protocol[0] = 0
if PyObject_AsReadBuffer(obj, <const void**> buf, buffer_len) == -1:
raise BufferError("could not get memoryview")
PyErr_WarnEx(RuntimeWarning,
"using old buffer interface to unpack %s; "
"this leads to unpacking errors if slicing is used and "
"will be removed in a future version" % type(obj),
1)
return 1
if PyObject_GetBuffer(obj, view, PyBUF_FULL_RO) == -1:
raise
if view.itemsize != 1:
PyBuffer_Release(view)
raise BufferError("cannot unpack from multi-byte object")
if PyBuffer_IsContiguous(view, b'A') == 0:
PyBuffer_Release(view)
# create a contiguous copy and get buffer
contiguous = PyMemoryView_GetContiguous(obj, PyBUF_READ, b'C')
PyObject_GetBuffer(contiguous, view, PyBUF_SIMPLE)
# view must hold the only reference to contiguous,
# so memory is freed when view is released
Py_DECREF(contiguous)
buffer_len[0] = view.len
buf[0] = <char*> view.buf
return 1
def unpackb(object packed, object object_hook=None, object list_hook=None,
bint use_list=1, encoding=None, unicode_errors="strict",
def unpackb(object packed, *, object object_hook=None, object list_hook=None,
bint use_list=True, bint raw=False, int timestamp=0, bint strict_map_key=True,
unicode_errors=None,
object_pairs_hook=None, ext_hook=ExtType,
Py_ssize_t max_str_len=2147483647, # 2**32-1
Py_ssize_t max_bin_len=2147483647,
Py_ssize_t max_array_len=2147483647,
Py_ssize_t max_map_len=2147483647,
Py_ssize_t max_ext_len=2147483647):
Py_ssize_t max_str_len=-1,
Py_ssize_t max_bin_len=-1,
Py_ssize_t max_array_len=-1,
Py_ssize_t max_map_len=-1,
Py_ssize_t max_ext_len=-1):
"""
Unpack packed_bytes to object. Returns an unpacked object.
Raises `ValueError` when `packed` contains extra bytes.
Raises ``ExtraData`` when *packed* contains extra bytes.
Raises ``ValueError`` when *packed* is incomplete.
Raises ``FormatError`` when *packed* is not valid msgpack.
Raises ``StackError`` when *packed* contains too nested.
Other exceptions can be raised during unpacking.
See :class:`Unpacker` for options.
*max_xxx_len* options are configured automatically from ``len(packed)``.
"""
cdef unpack_context ctx
cdef Py_ssize_t off = 0
@ -176,30 +167,31 @@ def unpackb(object packed, object object_hook=None, object list_hook=None,
cdef Py_buffer view
cdef char* buf = NULL
cdef Py_ssize_t buf_len
cdef char* cenc = NULL
cdef char* cerr = NULL
cdef int new_protocol = 0
cdef const char* cerr = NULL
get_data_from_buffer(packed, &view, &buf, &buf_len, &new_protocol)
if unicode_errors is not None:
cerr = unicode_errors
get_data_from_buffer(packed, &view, &buf, &buf_len)
if max_str_len == -1:
max_str_len = buf_len
if max_bin_len == -1:
max_bin_len = buf_len
if max_array_len == -1:
max_array_len = buf_len
if max_map_len == -1:
max_map_len = buf_len//2
if max_ext_len == -1:
max_ext_len = buf_len
try:
if encoding is not None:
if isinstance(encoding, unicode):
encoding = encoding.encode('ascii')
cenc = PyBytes_AsString(encoding)
if unicode_errors is not None:
if isinstance(unicode_errors, unicode):
unicode_errors = unicode_errors.encode('ascii')
cerr = PyBytes_AsString(unicode_errors)
init_ctx(&ctx, object_hook, object_pairs_hook, list_hook, ext_hook,
use_list, cenc, cerr,
use_list, raw, timestamp, strict_map_key, cerr,
max_str_len, max_bin_len, max_array_len, max_map_len, max_ext_len)
ret = unpack_construct(&ctx, buf, buf_len, &off)
finally:
if new_protocol:
PyBuffer_Release(&view);
PyBuffer_Release(&view);
if ret == 1:
obj = unpack_data(&ctx)
@ -207,94 +199,94 @@ def unpackb(object packed, object object_hook=None, object list_hook=None,
raise ExtraData(obj, PyBytes_FromStringAndSize(buf+off, buf_len-off))
return obj
unpack_clear(&ctx)
raise UnpackValueError("Unpack failed: error = %d" % (ret,))
if ret == 0:
raise ValueError("Unpack failed: incomplete input")
elif ret == -2:
raise FormatError
elif ret == -3:
raise StackError
raise ValueError("Unpack failed: error = %d" % (ret,))
def unpack(object stream, object object_hook=None, object list_hook=None,
bint use_list=1, encoding=None, unicode_errors="strict",
object_pairs_hook=None, ext_hook=ExtType,
Py_ssize_t max_str_len=2147483647, # 2**32-1
Py_ssize_t max_bin_len=2147483647,
Py_ssize_t max_array_len=2147483647,
Py_ssize_t max_map_len=2147483647,
Py_ssize_t max_ext_len=2147483647):
"""
Unpack an object from `stream`.
Raises `ValueError` when `stream` has extra bytes.
See :class:`Unpacker` for options.
"""
return unpackb(stream.read(), use_list=use_list,
object_hook=object_hook, object_pairs_hook=object_pairs_hook, list_hook=list_hook,
encoding=encoding, unicode_errors=unicode_errors, ext_hook=ext_hook,
max_str_len=max_str_len,
max_bin_len=max_bin_len,
max_array_len=max_array_len,
max_map_len=max_map_len,
max_ext_len=max_ext_len,
)
cdef class Unpacker(object):
cdef class Unpacker:
"""Streaming unpacker.
arguments:
Arguments:
:param file_like:
File-like object having `.read(n)` method.
If specified, unpacker reads serialized data from it and :meth:`feed()` is not usable.
If specified, unpacker reads serialized data from it and `.feed()` is not usable.
:param int read_size:
Used as `file_like.read(read_size)`. (default: `min(1024**2, max_buffer_size)`)
Used as `file_like.read(read_size)`. (default: `min(16*1024, max_buffer_size)`)
:param bool use_list:
If true, unpack msgpack array to Python list.
Otherwise, unpack to Python tuple. (default: True)
:param callable object_hook:
:param bool raw:
If true, unpack msgpack raw to Python bytes.
Otherwise, unpack to Python str by decoding with UTF-8 encoding (default).
:param int timestamp:
Control how timestamp type is unpacked:
0 - Timestamp
1 - float (Seconds from the EPOCH)
2 - int (Nanoseconds from the EPOCH)
3 - datetime.datetime (UTC).
:param bool strict_map_key:
If true (default), only str or bytes are accepted for map (dict) keys.
:param object_hook:
When specified, it should be callable.
Unpacker calls it with a dict argument after unpacking msgpack map.
(See also simplejson)
:param callable object_pairs_hook:
:param object_pairs_hook:
When specified, it should be callable.
Unpacker calls it with a list of key-value pairs after unpacking msgpack map.
(See also simplejson)
:param str encoding:
Encoding used for decoding msgpack raw.
If it is None (default), msgpack raw is deserialized to Python bytes.
:param str unicode_errors:
Used for decoding msgpack raw with *encoding*.
(default: `'strict'`)
The error handler for decoding unicode. (default: 'strict')
This option should be used only when you have msgpack data which
contains invalid UTF-8 string.
:param int max_buffer_size:
Limits size of data waiting unpacked. 0 means system's INT_MAX (default).
Limits size of data waiting unpacked. 0 means 2**32-1.
The default value is 100*1024*1024 (100MiB).
Raises `BufferFull` exception when it is insufficient.
You should set this parameter when unpacking data from untrusted source.
:param int max_str_len:
Limits max length of str. (default: 2**31-1)
Deprecated, use *max_buffer_size* instead.
Limits max length of str. (default: max_buffer_size)
:param int max_bin_len:
Limits max length of bin. (default: 2**31-1)
Deprecated, use *max_buffer_size* instead.
Limits max length of bin. (default: max_buffer_size)
:param int max_array_len:
Limits max length of array. (default: 2**31-1)
Limits max length of array.
(default: max_buffer_size)
:param int max_map_len:
Limits max length of map. (default: 2**31-1)
Limits max length of map.
(default: max_buffer_size//2)
:param int max_ext_len:
Deprecated, use *max_buffer_size* instead.
Limits max size of ext type. (default: max_buffer_size)
example of streaming deserialize from file-like object::
Example of streaming deserialize from file-like object::
unpacker = Unpacker(file_like)
for o in unpacker:
process(o)
example of streaming deserialize from socket::
Example of streaming deserialize from socket::
unpacker = Unpacker()
while True:
@ -304,6 +296,12 @@ cdef class Unpacker(object):
unpacker.feed(buf)
for o in unpacker:
process(o)
Raises ``ExtraData`` when *packed* contains extra bytes.
Raises ``OutOfData`` when *packed* is incomplete.
Raises ``FormatError`` when *packed* is not valid msgpack.
Raises ``StackError`` when *packed* contains too nested.
Other exceptions can be raised during unpacking.
"""
cdef unpack_context ctx
cdef char* buf
@ -313,7 +311,7 @@ cdef class Unpacker(object):
cdef Py_ssize_t read_size
# To maintain refcnt.
cdef object object_hook, object_pairs_hook, list_hook, ext_hook
cdef object encoding, unicode_errors
cdef object unicode_errors
cdef Py_ssize_t max_buffer_size
cdef uint64_t stream_offset
@ -324,17 +322,18 @@ cdef class Unpacker(object):
PyMem_Free(self.buf)
self.buf = NULL
def __init__(self, file_like=None, Py_ssize_t read_size=0, bint use_list=1,
@cython.critical_section
def __init__(self, file_like=None, *, Py_ssize_t read_size=0,
bint use_list=True, bint raw=False, int timestamp=0, bint strict_map_key=True,
object object_hook=None, object object_pairs_hook=None, object list_hook=None,
encoding=None, unicode_errors='strict', int max_buffer_size=0,
unicode_errors=None, Py_ssize_t max_buffer_size=100*1024*1024,
object ext_hook=ExtType,
Py_ssize_t max_str_len=2147483647, # 2**32-1
Py_ssize_t max_bin_len=2147483647,
Py_ssize_t max_array_len=2147483647,
Py_ssize_t max_map_len=2147483647,
Py_ssize_t max_ext_len=2147483647):
cdef char *cenc=NULL,
cdef char *cerr=NULL
Py_ssize_t max_str_len=-1,
Py_ssize_t max_bin_len=-1,
Py_ssize_t max_array_len=-1,
Py_ssize_t max_map_len=-1,
Py_ssize_t max_ext_len=-1):
cdef const char *cerr=NULL
self.object_hook = object_hook
self.object_pairs_hook = object_pairs_hook
@ -346,12 +345,25 @@ cdef class Unpacker(object):
self.file_like_read = file_like.read
if not PyCallable_Check(self.file_like_read):
raise TypeError("`file_like.read` must be a callable.")
if not max_buffer_size:
max_buffer_size = INT_MAX
if max_str_len == -1:
max_str_len = max_buffer_size
if max_bin_len == -1:
max_bin_len = max_buffer_size
if max_array_len == -1:
max_array_len = max_buffer_size
if max_map_len == -1:
max_map_len = max_buffer_size//2
if max_ext_len == -1:
max_ext_len = max_buffer_size
if read_size > max_buffer_size:
raise ValueError("read_size should be less or equal to max_buffer_size")
if not read_size:
read_size = min(max_buffer_size, 1024**2)
self.max_buffer_size = max_buffer_size
self.read_size = read_size
self.buf = <char*>PyMem_Malloc(read_size)
@ -362,33 +374,19 @@ cdef class Unpacker(object):
self.buf_tail = 0
self.stream_offset = 0
if encoding is not None:
if isinstance(encoding, unicode):
self.encoding = encoding.encode('ascii')
elif isinstance(encoding, bytes):
self.encoding = encoding
else:
raise TypeError("encoding should be bytes or unicode")
cenc = PyBytes_AsString(self.encoding)
if unicode_errors is not None:
if isinstance(unicode_errors, unicode):
self.unicode_errors = unicode_errors.encode('ascii')
elif isinstance(unicode_errors, bytes):
self.unicode_errors = unicode_errors
else:
raise TypeError("unicode_errors should be bytes or unicode")
cerr = PyBytes_AsString(self.unicode_errors)
self.unicode_errors = unicode_errors
cerr = unicode_errors
init_ctx(&self.ctx, object_hook, object_pairs_hook, list_hook,
ext_hook, use_list, cenc, cerr,
ext_hook, use_list, raw, timestamp, strict_map_key, cerr,
max_str_len, max_bin_len, max_array_len,
max_map_len, max_ext_len)
@cython.critical_section
def feed(self, object next_bytes):
"""Append `next_bytes` to internal buffer."""
cdef Py_buffer pybuff
cdef int new_protocol = 0
cdef char* buf
cdef Py_ssize_t buf_len
@ -396,12 +394,11 @@ cdef class Unpacker(object):
raise AssertionError(
"unpacker.feed() is not be able to use with `file_like`.")
get_data_from_buffer(next_bytes, &pybuff, &buf, &buf_len, &new_protocol)
get_data_from_buffer(next_bytes, &pybuff, &buf, &buf_len)
try:
self.append_buffer(buf, buf_len)
finally:
if new_protocol:
PyBuffer_Release(&pybuff)
PyBuffer_Release(&pybuff)
cdef append_buffer(self, void* _buf, Py_ssize_t _buf_len):
cdef:
@ -443,112 +440,111 @@ cdef class Unpacker(object):
self.buf_size = buf_size
self.buf_tail = tail + _buf_len
cdef read_from_file(self):
next_bytes = self.file_like_read(
min(self.read_size,
self.max_buffer_size - (self.buf_tail - self.buf_head)
))
cdef int read_from_file(self) except -1:
cdef Py_ssize_t remains = self.max_buffer_size - (self.buf_tail - self.buf_head)
if remains <= 0:
raise BufferFull
next_bytes = self.file_like_read(min(self.read_size, remains))
if next_bytes:
self.append_buffer(PyBytes_AsString(next_bytes), PyBytes_Size(next_bytes))
else:
self.file_like = None
return 0
cdef object _unpack(self, execute_fn execute, object write_bytes, bint iter=0):
cdef object _unpack(self, execute_fn execute, bint iter=0):
cdef int ret
cdef object obj
cdef Py_ssize_t prev_head
if write_bytes is not None:
PyErr_WarnEx(DeprecationWarning, "`write_bytes` option is deprecated. Use `.tell()` instead.", 1)
if self.buf_head >= self.buf_tail and self.file_like is not None:
self.read_from_file()
while 1:
prev_head = self.buf_head
if prev_head >= self.buf_tail:
if prev_head < self.buf_tail:
ret = execute(&self.ctx, self.buf, self.buf_tail, &self.buf_head)
self.stream_offset += self.buf_head - prev_head
else:
ret = 0
if ret == 1:
obj = unpack_data(&self.ctx)
unpack_init(&self.ctx)
return obj
elif ret == 0:
if self.file_like is not None:
self.read_from_file()
continue
if iter:
raise StopIteration("No more data to unpack.")
else:
raise OutOfData("No more data to unpack.")
elif ret == -2:
raise FormatError
elif ret == -3:
raise StackError
else:
raise ValueError("Unpack failed: error = %d" % (ret,))
try:
ret = execute(&self.ctx, self.buf, self.buf_tail, &self.buf_head)
self.stream_offset += self.buf_head - prev_head
if write_bytes is not None:
write_bytes(PyBytes_FromStringAndSize(self.buf + prev_head, self.buf_head - prev_head))
if ret == 1:
obj = unpack_data(&self.ctx)
unpack_init(&self.ctx)
return obj
elif ret == 0:
if self.file_like is not None:
self.read_from_file()
continue
if iter:
raise StopIteration("No more data to unpack.")
else:
raise OutOfData("No more data to unpack.")
else:
raise UnpackValueError("Unpack failed: error = %d" % (ret,))
except ValueError as e:
raise UnpackValueError(e)
@cython.critical_section
def read_bytes(self, Py_ssize_t nbytes):
"""Read a specified number of raw bytes from the stream"""
cdef Py_ssize_t nread
nread = min(self.buf_tail - self.buf_head, nbytes)
ret = PyBytes_FromStringAndSize(self.buf + self.buf_head, nread)
self.buf_head += nread
if len(ret) < nbytes and self.file_like is not None:
ret += self.file_like.read(nbytes - len(ret))
if nread < nbytes and self.file_like is not None:
ret += self.file_like.read(nbytes - nread)
nread = len(ret)
self.stream_offset += nread
return ret
def unpack(self, object write_bytes=None):
@cython.critical_section
def unpack(self):
"""Unpack one object
If write_bytes is not None, it will be called with parts of the raw
message as it is unpacked.
Raises `OutOfData` when there are no more bytes to unpack.
"""
return self._unpack(unpack_construct, write_bytes)
return self._unpack(unpack_construct)
def skip(self, object write_bytes=None):
@cython.critical_section
def skip(self):
"""Read and ignore one object, returning None
If write_bytes is not None, it will be called with parts of the raw
message as it is unpacked.
Raises `OutOfData` when there are no more bytes to unpack.
"""
return self._unpack(unpack_skip, write_bytes)
return self._unpack(unpack_skip)
def read_array_header(self, object write_bytes=None):
@cython.critical_section
def read_array_header(self):
"""assuming the next object is an array, return its size n, such that
the next n unpack() calls will iterate over its contents.
Raises `OutOfData` when there are no more bytes to unpack.
"""
return self._unpack(read_array_header, write_bytes)
return self._unpack(read_array_header)
def read_map_header(self, object write_bytes=None):
@cython.critical_section
def read_map_header(self):
"""assuming the next object is a map, return its size n, such that the
next n * 2 unpack() calls will iterate over its key-value pairs.
Raises `OutOfData` when there are no more bytes to unpack.
"""
return self._unpack(read_map_header, write_bytes)
return self._unpack(read_map_header)
@cython.critical_section
def tell(self):
"""Returns the current position of the Unpacker in bytes, i.e., the
number of bytes that were read from the input, also the starting
position of the next object.
"""
return self.stream_offset
def __iter__(self):
return self
@cython.critical_section
def __next__(self):
return self._unpack(unpack_construct, None, 1)
return self._unpack(unpack_construct, 1)
# for debug.
#def _buf(self):

View file

@ -1 +0,0 @@
version = (0, 5, 1)

View file

@ -1,5 +1,10 @@
class UnpackException(Exception):
"""Deprecated. Use Exception instead to catch all exception during unpacking."""
"""Base class for some exceptions raised while unpacking.
NOTE: unpack may raise exception other than subclass of
UnpackException. If you want to catch all error, catch
Exception instead.
"""
class BufferFull(UnpackException):
@ -10,11 +15,25 @@ class OutOfData(UnpackException):
pass
class UnpackValueError(UnpackException, ValueError):
"""Deprecated. Use ValueError instead."""
class FormatError(ValueError, UnpackException):
"""Invalid msgpack format"""
class StackError(ValueError, UnpackException):
"""Too nested"""
# Deprecated. Use ValueError instead
UnpackValueError = ValueError
class ExtraData(UnpackValueError):
"""ExtraData is raised when there is trailing data.
This exception is raised while only one-shot (not streaming)
unpack.
"""
def __init__(self, unpacked, extra):
self.unpacked = unpacked
self.extra = extra
@ -23,19 +42,7 @@ class ExtraData(UnpackValueError):
return "unpack(b) received extra data."
class PackException(Exception):
"""Deprecated. Use Exception instead to catch all exception during packing."""
class PackValueError(PackException, ValueError):
"""PackValueError is raised when type of input data is supported but it's value is unsupported.
Deprecated. Use ValueError instead.
"""
class PackOverflowError(PackValueError, OverflowError):
"""PackOverflowError is raised when integer value is out of range of msgpack support [-2**31, 2**32).
Deprecated. Use ValueError instead.
"""
# Deprecated. Use Exception instead to catch all exception during packing.
PackException = Exception
PackValueError = ValueError
PackOverflowError = OverflowError

170
msgpack/ext.py Normal file
View file

@ -0,0 +1,170 @@
import datetime
import struct
from collections import namedtuple
class ExtType(namedtuple("ExtType", "code data")):
"""ExtType represents ext type in msgpack."""
def __new__(cls, code, data):
if not isinstance(code, int):
raise TypeError("code must be int")
if not isinstance(data, bytes):
raise TypeError("data must be bytes")
if not 0 <= code <= 127:
raise ValueError("code must be 0~127")
return super().__new__(cls, code, data)
class Timestamp:
"""Timestamp represents the Timestamp extension type in msgpack.
When built with Cython, msgpack uses C methods to pack and unpack `Timestamp`.
When using pure-Python msgpack, :func:`to_bytes` and :func:`from_bytes` are used to pack and
unpack `Timestamp`.
This class is immutable: Do not override seconds and nanoseconds.
"""
__slots__ = ["seconds", "nanoseconds"]
def __init__(self, seconds, nanoseconds=0):
"""Initialize a Timestamp object.
:param int seconds:
Number of seconds since the UNIX epoch (00:00:00 UTC Jan 1 1970, minus leap seconds).
May be negative.
:param int nanoseconds:
Number of nanoseconds to add to `seconds` to get fractional time.
Maximum is 999_999_999. Default is 0.
Note: Negative times (before the UNIX epoch) are represented as neg. seconds + pos. ns.
"""
if not isinstance(seconds, int):
raise TypeError("seconds must be an integer")
if not isinstance(nanoseconds, int):
raise TypeError("nanoseconds must be an integer")
if not (0 <= nanoseconds < 10**9):
raise ValueError("nanoseconds must be a non-negative integer less than 999999999.")
self.seconds = seconds
self.nanoseconds = nanoseconds
def __repr__(self):
"""String representation of Timestamp."""
return f"Timestamp(seconds={self.seconds}, nanoseconds={self.nanoseconds})"
def __eq__(self, other):
"""Check for equality with another Timestamp object"""
if type(other) is self.__class__:
return self.seconds == other.seconds and self.nanoseconds == other.nanoseconds
return False
def __ne__(self, other):
"""not-equals method (see :func:`__eq__()`)"""
return not self.__eq__(other)
def __hash__(self):
return hash((self.seconds, self.nanoseconds))
@staticmethod
def from_bytes(b):
"""Unpack bytes into a `Timestamp` object.
Used for pure-Python msgpack unpacking.
:param b: Payload from msgpack ext message with code -1
:type b: bytes
:returns: Timestamp object unpacked from msgpack ext payload
:rtype: Timestamp
"""
if len(b) == 4:
seconds = struct.unpack("!L", b)[0]
nanoseconds = 0
elif len(b) == 8:
data64 = struct.unpack("!Q", b)[0]
seconds = data64 & 0x00000003FFFFFFFF
nanoseconds = data64 >> 34
elif len(b) == 12:
nanoseconds, seconds = struct.unpack("!Iq", b)
else:
raise ValueError(
"Timestamp type can only be created from 32, 64, or 96-bit byte objects"
)
return Timestamp(seconds, nanoseconds)
def to_bytes(self):
"""Pack this Timestamp object into bytes.
Used for pure-Python msgpack packing.
:returns data: Payload for EXT message with code -1 (timestamp type)
:rtype: bytes
"""
if (self.seconds >> 34) == 0: # seconds is non-negative and fits in 34 bits
data64 = self.nanoseconds << 34 | self.seconds
if data64 & 0xFFFFFFFF00000000 == 0:
# nanoseconds is zero and seconds < 2**32, so timestamp 32
data = struct.pack("!L", data64)
else:
# timestamp 64
data = struct.pack("!Q", data64)
else:
# timestamp 96
data = struct.pack("!Iq", self.nanoseconds, self.seconds)
return data
@staticmethod
def from_unix(unix_sec):
"""Create a Timestamp from posix timestamp in seconds.
:param unix_float: Posix timestamp in seconds.
:type unix_float: int or float
"""
seconds = int(unix_sec // 1)
nanoseconds = int((unix_sec % 1) * 10**9)
return Timestamp(seconds, nanoseconds)
def to_unix(self):
"""Get the timestamp as a floating-point value.
:returns: posix timestamp
:rtype: float
"""
return self.seconds + self.nanoseconds / 1e9
@staticmethod
def from_unix_nano(unix_ns):
"""Create a Timestamp from posix timestamp in nanoseconds.
:param int unix_ns: Posix timestamp in nanoseconds.
:rtype: Timestamp
"""
return Timestamp(*divmod(unix_ns, 10**9))
def to_unix_nano(self):
"""Get the timestamp as a unixtime in nanoseconds.
:returns: posix timestamp in nanoseconds
:rtype: int
"""
return self.seconds * 10**9 + self.nanoseconds
def to_datetime(self):
"""Get the timestamp as a UTC datetime.
:rtype: `datetime.datetime`
"""
utc = datetime.timezone.utc
return datetime.datetime.fromtimestamp(0, utc) + datetime.timedelta(
seconds=self.seconds, microseconds=self.nanoseconds // 1000
)
@staticmethod
def from_datetime(dt):
"""Create a Timestamp from datetime with tzinfo.
:rtype: Timestamp
"""
return Timestamp(seconds=int(dt.timestamp()), nanoseconds=dt.microsecond * 1000)

File diff suppressed because it is too large Load diff

View file

@ -21,15 +21,12 @@
#include "sysdep.h"
#include <limits.h>
#include <string.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifdef _MSC_VER
#define inline __inline
#endif
typedef struct msgpack_packer {
char *buf;
size_t length;

View file

@ -37,18 +37,6 @@
* Integer
*/
#define msgpack_pack_real_uint8(x, d) \
do { \
if(d < (1<<7)) { \
/* fixnum */ \
msgpack_pack_append_buffer(x, &TAKE8_8(d), 1); \
} else { \
/* unsigned 8 */ \
unsigned char buf[2] = {0xcc, TAKE8_8(d)}; \
msgpack_pack_append_buffer(x, buf, 2); \
} \
} while(0)
#define msgpack_pack_real_uint16(x, d) \
do { \
if(d < (1<<7)) { \
@ -123,18 +111,6 @@ do { \
} \
} while(0)
#define msgpack_pack_real_int8(x, d) \
do { \
if(d < -(1<<5)) { \
/* signed 8 */ \
unsigned char buf[2] = {0xd0, TAKE8_8(d)}; \
msgpack_pack_append_buffer(x, buf, 2); \
} else { \
/* fixnum */ \
msgpack_pack_append_buffer(x, &TAKE8_8(d), 1); \
} \
} while(0)
#define msgpack_pack_real_int16(x, d) \
do { \
if(d < -(1<<5)) { \
@ -264,49 +240,6 @@ do { \
} while(0)
static inline int msgpack_pack_uint8(msgpack_packer* x, uint8_t d)
{
msgpack_pack_real_uint8(x, d);
}
static inline int msgpack_pack_uint16(msgpack_packer* x, uint16_t d)
{
msgpack_pack_real_uint16(x, d);
}
static inline int msgpack_pack_uint32(msgpack_packer* x, uint32_t d)
{
msgpack_pack_real_uint32(x, d);
}
static inline int msgpack_pack_uint64(msgpack_packer* x, uint64_t d)
{
msgpack_pack_real_uint64(x, d);
}
static inline int msgpack_pack_int8(msgpack_packer* x, int8_t d)
{
msgpack_pack_real_int8(x, d);
}
static inline int msgpack_pack_int16(msgpack_packer* x, int16_t d)
{
msgpack_pack_real_int16(x, d);
}
static inline int msgpack_pack_int32(msgpack_packer* x, int32_t d)
{
msgpack_pack_real_int32(x, d);
}
static inline int msgpack_pack_int64(msgpack_packer* x, int64_t d)
{
msgpack_pack_real_int64(x, d);
}
//#ifdef msgpack_pack_inline_func_cint
static inline int msgpack_pack_short(msgpack_packer* x, short d)
{
#if defined(SIZEOF_SHORT)
@ -372,192 +305,37 @@ if(sizeof(int) == 2) {
static inline int msgpack_pack_long(msgpack_packer* x, long d)
{
#if defined(SIZEOF_LONG)
#if SIZEOF_LONG == 2
msgpack_pack_real_int16(x, d);
#elif SIZEOF_LONG == 4
#if SIZEOF_LONG == 4
msgpack_pack_real_int32(x, d);
#else
msgpack_pack_real_int64(x, d);
#endif
#elif defined(LONG_MAX)
#if LONG_MAX == 0x7fffL
msgpack_pack_real_int16(x, d);
#elif LONG_MAX == 0x7fffffffL
#if LONG_MAX == 0x7fffffffL
msgpack_pack_real_int32(x, d);
#else
msgpack_pack_real_int64(x, d);
#endif
#else
if(sizeof(long) == 2) {
msgpack_pack_real_int16(x, d);
} else if(sizeof(long) == 4) {
msgpack_pack_real_int32(x, d);
} else {
msgpack_pack_real_int64(x, d);
}
if (sizeof(long) == 4) {
msgpack_pack_real_int32(x, d);
} else {
msgpack_pack_real_int64(x, d);
}
#endif
}
static inline int msgpack_pack_long_long(msgpack_packer* x, long long d)
{
#if defined(SIZEOF_LONG_LONG)
#if SIZEOF_LONG_LONG == 2
msgpack_pack_real_int16(x, d);
#elif SIZEOF_LONG_LONG == 4
msgpack_pack_real_int32(x, d);
#else
msgpack_pack_real_int64(x, d);
#endif
#elif defined(LLONG_MAX)
#if LLONG_MAX == 0x7fffL
msgpack_pack_real_int16(x, d);
#elif LLONG_MAX == 0x7fffffffL
msgpack_pack_real_int32(x, d);
#else
msgpack_pack_real_int64(x, d);
#endif
#else
if(sizeof(long long) == 2) {
msgpack_pack_real_int16(x, d);
} else if(sizeof(long long) == 4) {
msgpack_pack_real_int32(x, d);
} else {
msgpack_pack_real_int64(x, d);
}
#endif
}
static inline int msgpack_pack_unsigned_short(msgpack_packer* x, unsigned short d)
{
#if defined(SIZEOF_SHORT)
#if SIZEOF_SHORT == 2
msgpack_pack_real_uint16(x, d);
#elif SIZEOF_SHORT == 4
msgpack_pack_real_uint32(x, d);
#else
msgpack_pack_real_uint64(x, d);
#endif
#elif defined(USHRT_MAX)
#if USHRT_MAX == 0xffffU
msgpack_pack_real_uint16(x, d);
#elif USHRT_MAX == 0xffffffffU
msgpack_pack_real_uint32(x, d);
#else
msgpack_pack_real_uint64(x, d);
#endif
#else
if(sizeof(unsigned short) == 2) {
msgpack_pack_real_uint16(x, d);
} else if(sizeof(unsigned short) == 4) {
msgpack_pack_real_uint32(x, d);
} else {
msgpack_pack_real_uint64(x, d);
}
#endif
}
static inline int msgpack_pack_unsigned_int(msgpack_packer* x, unsigned int d)
{
#if defined(SIZEOF_INT)
#if SIZEOF_INT == 2
msgpack_pack_real_uint16(x, d);
#elif SIZEOF_INT == 4
msgpack_pack_real_uint32(x, d);
#else
msgpack_pack_real_uint64(x, d);
#endif
#elif defined(UINT_MAX)
#if UINT_MAX == 0xffffU
msgpack_pack_real_uint16(x, d);
#elif UINT_MAX == 0xffffffffU
msgpack_pack_real_uint32(x, d);
#else
msgpack_pack_real_uint64(x, d);
#endif
#else
if(sizeof(unsigned int) == 2) {
msgpack_pack_real_uint16(x, d);
} else if(sizeof(unsigned int) == 4) {
msgpack_pack_real_uint32(x, d);
} else {
msgpack_pack_real_uint64(x, d);
}
#endif
}
static inline int msgpack_pack_unsigned_long(msgpack_packer* x, unsigned long d)
{
#if defined(SIZEOF_LONG)
#if SIZEOF_LONG == 2
msgpack_pack_real_uint16(x, d);
#elif SIZEOF_LONG == 4
msgpack_pack_real_uint32(x, d);
#else
msgpack_pack_real_uint64(x, d);
#endif
#elif defined(ULONG_MAX)
#if ULONG_MAX == 0xffffUL
msgpack_pack_real_uint16(x, d);
#elif ULONG_MAX == 0xffffffffUL
msgpack_pack_real_uint32(x, d);
#else
msgpack_pack_real_uint64(x, d);
#endif
#else
if(sizeof(unsigned long) == 2) {
msgpack_pack_real_uint16(x, d);
} else if(sizeof(unsigned long) == 4) {
msgpack_pack_real_uint32(x, d);
} else {
msgpack_pack_real_uint64(x, d);
}
#endif
}
static inline int msgpack_pack_unsigned_long_long(msgpack_packer* x, unsigned long long d)
{
#if defined(SIZEOF_LONG_LONG)
#if SIZEOF_LONG_LONG == 2
msgpack_pack_real_uint16(x, d);
#elif SIZEOF_LONG_LONG == 4
msgpack_pack_real_uint32(x, d);
#else
msgpack_pack_real_uint64(x, d);
#endif
#elif defined(ULLONG_MAX)
#if ULLONG_MAX == 0xffffUL
msgpack_pack_real_uint16(x, d);
#elif ULLONG_MAX == 0xffffffffUL
msgpack_pack_real_uint32(x, d);
#else
msgpack_pack_real_uint64(x, d);
#endif
#else
if(sizeof(unsigned long long) == 2) {
msgpack_pack_real_uint16(x, d);
} else if(sizeof(unsigned long long) == 4) {
msgpack_pack_real_uint32(x, d);
} else {
msgpack_pack_real_uint64(x, d);
}
#endif
}
//#undef msgpack_pack_inline_func_cint
//#endif
/*
@ -566,24 +344,26 @@ if(sizeof(unsigned long long) == 2) {
static inline int msgpack_pack_float(msgpack_packer* x, float d)
{
union { float f; uint32_t i; } mem;
mem.f = d;
unsigned char buf[5];
buf[0] = 0xca; _msgpack_store32(&buf[1], mem.i);
buf[0] = 0xca;
#if PY_VERSION_HEX >= 0x030B00A7
PyFloat_Pack4(d, (char *)&buf[1], 0);
#else
_PyFloat_Pack4(d, &buf[1], 0);
#endif
msgpack_pack_append_buffer(x, buf, 5);
}
static inline int msgpack_pack_double(msgpack_packer* x, double d)
{
union { double f; uint64_t i; } mem;
mem.f = d;
unsigned char buf[9];
buf[0] = 0xcb;
#if defined(__arm__) && !(__ARM_EABI__) // arm-oabi
// https://github.com/msgpack/msgpack-perl/pull/1
mem.i = (mem.i & 0xFFFFFFFFUL) << 32UL | (mem.i >> 32UL);
#if PY_VERSION_HEX >= 0x030B00A7
PyFloat_Pack8(d, (char *)&buf[1], 0);
#else
_PyFloat_Pack8(d, &buf[1], 0);
#endif
_msgpack_store64(&buf[1], mem.i);
msgpack_pack_append_buffer(x, buf, 9);
}
@ -766,6 +546,39 @@ static inline int msgpack_pack_ext(msgpack_packer* x, char typecode, size_t l)
}
/*
* Pack Timestamp extension type. Follows msgpack-c pack_template.h.
*/
static inline int msgpack_pack_timestamp(msgpack_packer* x, int64_t seconds, uint32_t nanoseconds)
{
if ((seconds >> 34) == 0) {
/* seconds is unsigned and fits in 34 bits */
uint64_t data64 = ((uint64_t)nanoseconds << 34) | (uint64_t)seconds;
if ((data64 & 0xffffffff00000000L) == 0) {
/* no nanoseconds and seconds is 32bits or smaller. timestamp32. */
unsigned char buf[4];
uint32_t data32 = (uint32_t)data64;
msgpack_pack_ext(x, -1, 4);
_msgpack_store32(buf, data32);
msgpack_pack_raw_body(x, buf, 4);
} else {
/* timestamp64 */
unsigned char buf[8];
msgpack_pack_ext(x, -1, 8);
_msgpack_store64(buf, data64);
msgpack_pack_raw_body(x, buf, 8);
}
} else {
/* seconds is signed or >34bits */
unsigned char buf[12];
_msgpack_store32(&buf[0], nanoseconds);
_msgpack_store64(&buf[4], seconds);
msgpack_pack_ext(x, -1, 12);
msgpack_pack_raw_body(x, buf, 12);
}
return 0;
}
#undef msgpack_pack_append_buffer
@ -775,11 +588,9 @@ static inline int msgpack_pack_ext(msgpack_packer* x, char typecode, size_t l)
#undef TAKE8_32
#undef TAKE8_64
#undef msgpack_pack_real_uint8
#undef msgpack_pack_real_uint16
#undef msgpack_pack_real_uint32
#undef msgpack_pack_real_uint64
#undef msgpack_pack_real_int8
#undef msgpack_pack_real_int16
#undef msgpack_pack_real_int32
#undef msgpack_pack_real_int64

View file

@ -61,14 +61,14 @@ typedef unsigned int _msgpack_atomic_counter_t;
#endif
#endif
#else
#include <arpa/inet.h> /* __BYTE_ORDER */
#else /* _WIN32 */
#include <arpa/inet.h> /* ntohs, ntohl */
#endif
#if !defined(__LITTLE_ENDIAN__) && !defined(__BIG_ENDIAN__)
#if __BYTE_ORDER == __LITTLE_ENDIAN
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#define __LITTLE_ENDIAN__
#elif __BYTE_ORDER == __BIG_ENDIAN
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
#define __BIG_ENDIAN__
#elif _WIN32
#define __LITTLE_ENDIAN__
@ -95,7 +95,7 @@ typedef unsigned int _msgpack_atomic_counter_t;
#ifdef _WIN32
# if defined(ntohl)
# define _msgpack_be32(x) ntohl(x)
# elif defined(_byteswap_ulong) || (defined(_MSC_VER) && _MSC_VER >= 1400)
# elif defined(_byteswap_ulong) || defined(_MSC_VER)
# define _msgpack_be32(x) ((uint32_t)_byteswap_ulong((unsigned long)x))
# else
# define _msgpack_be32(x) \
@ -108,7 +108,7 @@ typedef unsigned int _msgpack_atomic_counter_t;
# define _msgpack_be32(x) ntohl(x)
#endif
#if defined(_byteswap_uint64) || (defined(_MSC_VER) && _MSC_VER >= 1400)
#if defined(_byteswap_uint64) || defined(_MSC_VER)
# define _msgpack_be64(x) (_byteswap_uint64(x))
#elif defined(bswap_64)
# define _msgpack_be64(x) bswap_64(x)

View file

@ -20,12 +20,17 @@
#include "unpack_define.h"
typedef struct unpack_user {
int use_list;
PyObject *object_hook;
bool use_list;
bool raw;
bool has_pairs_hook;
bool strict_map_key;
int timestamp;
PyObject *object_hook;
PyObject *list_hook;
PyObject *ext_hook;
const char *encoding;
PyObject *timestamp_t;
PyObject *giga;
PyObject *utc;
const char *unicode_errors;
Py_ssize_t max_str_len, max_bin_len, max_array_len, max_map_len, max_ext_len;
} unpack_user;
@ -42,7 +47,7 @@ static inline msgpack_unpack_object unpack_callback_root(unpack_user* u)
static inline int unpack_callback_uint16(unpack_user* u, uint16_t d, msgpack_unpack_object* o)
{
PyObject *p = PyInt_FromLong((long)d);
PyObject *p = PyLong_FromLong((long)d);
if (!p)
return -1;
*o = p;
@ -56,7 +61,7 @@ static inline int unpack_callback_uint8(unpack_user* u, uint8_t d, msgpack_unpac
static inline int unpack_callback_uint32(unpack_user* u, uint32_t d, msgpack_unpack_object* o)
{
PyObject *p = PyInt_FromSize_t((size_t)d);
PyObject *p = PyLong_FromSize_t((size_t)d);
if (!p)
return -1;
*o = p;
@ -69,7 +74,7 @@ static inline int unpack_callback_uint64(unpack_user* u, uint64_t d, msgpack_unp
if (d > LONG_MAX) {
p = PyLong_FromUnsignedLongLong((unsigned PY_LONG_LONG)d);
} else {
p = PyInt_FromLong((long)d);
p = PyLong_FromLong((long)d);
}
if (!p)
return -1;
@ -79,7 +84,7 @@ static inline int unpack_callback_uint64(unpack_user* u, uint64_t d, msgpack_unp
static inline int unpack_callback_int32(unpack_user* u, int32_t d, msgpack_unpack_object* o)
{
PyObject *p = PyInt_FromLong(d);
PyObject *p = PyLong_FromLong(d);
if (!p)
return -1;
*o = p;
@ -102,7 +107,7 @@ static inline int unpack_callback_int64(unpack_user* u, int64_t d, msgpack_unpac
if (d > LONG_MAX || d < LONG_MIN) {
p = PyLong_FromLongLong((PY_LONG_LONG)d);
} else {
p = PyInt_FromLong((long)d);
p = PyLong_FromLong((long)d);
}
*o = p;
return 0;
@ -187,6 +192,13 @@ static inline int unpack_callback_map(unpack_user* u, unsigned int n, msgpack_un
static inline int unpack_callback_map_item(unpack_user* u, unsigned int current, msgpack_unpack_object* c, msgpack_unpack_object k, msgpack_unpack_object v)
{
if (u->strict_map_key && !PyUnicode_CheckExact(k) && !PyBytes_CheckExact(k)) {
PyErr_Format(PyExc_ValueError, "%.100s is not allowed for map key when strict_map_key=True", Py_TYPE(k)->tp_name);
return -1;
}
if (PyUnicode_CheckExact(k)) {
PyUnicode_InternInPlace(&k);
}
if (u->has_pairs_hook) {
msgpack_unpack_object item = PyTuple_Pack(2, k, v);
if (!item)
@ -225,10 +237,11 @@ static inline int unpack_callback_raw(unpack_user* u, const char* b, const char*
}
PyObject *py;
if(u->encoding) {
py = PyUnicode_Decode(p, l, u->encoding, u->unicode_errors);
} else {
if (u->raw) {
py = PyBytes_FromStringAndSize(p, l);
} else {
py = PyUnicode_DecodeUTF8(p, l, u->unicode_errors);
}
if (!py)
return -1;
@ -250,10 +263,43 @@ static inline int unpack_callback_bin(unpack_user* u, const char* b, const char*
return 0;
}
static inline int unpack_callback_ext(unpack_user* u, const char* base, const char* pos,
unsigned int length, msgpack_unpack_object* o)
typedef struct msgpack_timestamp {
int64_t tv_sec;
uint32_t tv_nsec;
} msgpack_timestamp;
/*
* Unpack ext buffer to a timestamp. Pulled from msgpack-c timestamp.h.
*/
static int unpack_timestamp(const char* buf, unsigned int buflen, msgpack_timestamp* ts) {
switch (buflen) {
case 4:
ts->tv_nsec = 0;
{
uint32_t v = _msgpack_load32(uint32_t, buf);
ts->tv_sec = (int64_t)v;
}
return 0;
case 8: {
uint64_t value =_msgpack_load64(uint64_t, buf);
ts->tv_nsec = (uint32_t)(value >> 34);
ts->tv_sec = value & 0x00000003ffffffffLL;
return 0;
}
case 12:
ts->tv_nsec = _msgpack_load32(uint32_t, buf);
ts->tv_sec = _msgpack_load64(int64_t, buf + 4);
return 0;
default:
return -1;
}
}
#include "datetime.h"
static int unpack_callback_ext(unpack_user* u, const char* base, const char* pos,
unsigned int length, msgpack_unpack_object* o)
{
PyObject *py;
int8_t typecode = (int8_t)*pos++;
if (!u->ext_hook) {
PyErr_SetString(PyExc_AssertionError, "u->ext_hook cannot be NULL");
@ -263,12 +309,79 @@ static inline int unpack_callback_ext(unpack_user* u, const char* base, const ch
PyErr_Format(PyExc_ValueError, "%u exceeds max_ext_len(%zd)", length, u->max_ext_len);
return -1;
}
PyObject *py = NULL;
// length also includes the typecode, so the actual data is length-1
#if PY_MAJOR_VERSION == 2
py = PyObject_CallFunction(u->ext_hook, "(is#)", (int)typecode, pos, (Py_ssize_t)length-1);
#else
py = PyObject_CallFunction(u->ext_hook, "(iy#)", (int)typecode, pos, (Py_ssize_t)length-1);
#endif
if (typecode == -1) {
msgpack_timestamp ts;
if (unpack_timestamp(pos, length-1, &ts) < 0) {
return -1;
}
if (u->timestamp == 2) { // int
PyObject *a = PyLong_FromLongLong(ts.tv_sec);
if (a == NULL) return -1;
PyObject *c = PyNumber_Multiply(a, u->giga);
Py_DECREF(a);
if (c == NULL) {
return -1;
}
PyObject *b = PyLong_FromUnsignedLong(ts.tv_nsec);
if (b == NULL) {
Py_DECREF(c);
return -1;
}
py = PyNumber_Add(c, b);
Py_DECREF(c);
Py_DECREF(b);
}
else if (u->timestamp == 0) { // Timestamp
py = PyObject_CallFunction(u->timestamp_t, "(Lk)", ts.tv_sec, ts.tv_nsec);
}
else if (u->timestamp == 3) { // datetime
// Calculate datetime using epoch + delta
// due to limitations PyDateTime_FromTimestamp on Windows with negative timestamps
PyObject *epoch = PyDateTimeAPI->DateTime_FromDateAndTime(1970, 1, 1, 0, 0, 0, 0, u->utc, PyDateTimeAPI->DateTimeType);
if (epoch == NULL) {
return -1;
}
PyObject* d = PyDelta_FromDSU(ts.tv_sec/(24*3600), ts.tv_sec%(24*3600), ts.tv_nsec / 1000);
if (d == NULL) {
Py_DECREF(epoch);
return -1;
}
py = PyNumber_Add(epoch, d);
Py_DECREF(epoch);
Py_DECREF(d);
}
else { // float
PyObject *a = PyFloat_FromDouble((double)ts.tv_nsec);
if (a == NULL) return -1;
PyObject *b = PyNumber_TrueDivide(a, u->giga);
Py_DECREF(a);
if (b == NULL) return -1;
PyObject *c = PyLong_FromLongLong(ts.tv_sec);
if (c == NULL) {
Py_DECREF(b);
return -1;
}
a = PyNumber_Add(b, c);
Py_DECREF(b);
Py_DECREF(c);
py = a;
}
} else {
py = PyObject_CallFunction(u->ext_hook, "(iy#)", (int)typecode, pos, (Py_ssize_t)length-1);
}
if (!py)
return -1;
*o = py;

View file

@ -0,0 +1,51 @@
static inline int unpack_container_header(unpack_context* ctx, const char* data, Py_ssize_t len, Py_ssize_t* off)
{
assert(len >= *off);
uint32_t size;
const unsigned char *const p = (unsigned char*)data + *off;
#define inc_offset(inc) \
if (len - *off < inc) \
return 0; \
*off += inc;
switch (*p) {
case var_offset:
inc_offset(3);
size = _msgpack_load16(uint16_t, p + 1);
break;
case var_offset + 1:
inc_offset(5);
size = _msgpack_load32(uint32_t, p + 1);
break;
#ifdef USE_CASE_RANGE
case fixed_offset + 0x0 ... fixed_offset + 0xf:
#else
case fixed_offset + 0x0:
case fixed_offset + 0x1:
case fixed_offset + 0x2:
case fixed_offset + 0x3:
case fixed_offset + 0x4:
case fixed_offset + 0x5:
case fixed_offset + 0x6:
case fixed_offset + 0x7:
case fixed_offset + 0x8:
case fixed_offset + 0x9:
case fixed_offset + 0xa:
case fixed_offset + 0xb:
case fixed_offset + 0xc:
case fixed_offset + 0xd:
case fixed_offset + 0xe:
case fixed_offset + 0xf:
#endif
++*off;
size = ((unsigned int)*p) & 0x0f;
break;
default:
PyErr_SetString(PyExc_ValueError, "Unexpected type header on stream");
return -1;
}
unpack_callback_uint32(&ctx->user, size, &ctx->stack[0].obj);
return 1;
}

View file

@ -75,8 +75,7 @@ static inline void unpack_clear(unpack_context *ctx)
Py_CLEAR(ctx->stack[0].obj);
}
template <bool construct>
static inline int unpack_execute(unpack_context* ctx, const char* data, Py_ssize_t len, Py_ssize_t* off)
static inline int unpack_execute(bool construct, unpack_context* ctx, const char* data, Py_ssize_t len, Py_ssize_t* off)
{
assert(len >= *off);
@ -123,7 +122,7 @@ static inline int unpack_execute(unpack_context* ctx, const char* data, Py_ssize
goto _fixed_trail_again
#define start_container(func, count_, ct_) \
if(top >= MSGPACK_EMBED_STACK_SIZE) { goto _failed; } /* FIXME */ \
if(top >= MSGPACK_EMBED_STACK_SIZE) { ret = -3; goto _end; } \
if(construct_cb(func)(user, count_, &stack[top].obj) < 0) { goto _failed; } \
if((count_) == 0) { obj = stack[top].obj; \
if (construct_cb(func##_end)(user, &obj) < 0) { goto _failed; } \
@ -132,27 +131,6 @@ static inline int unpack_execute(unpack_context* ctx, const char* data, Py_ssize
stack[top].size = count_; \
stack[top].count = 0; \
++top; \
/*printf("container %d count %d stack %d\n",stack[top].obj,count_,top);*/ \
/*printf("stack push %d\n", top);*/ \
/* FIXME \
if(top >= stack_size) { \
if(stack_size == MSGPACK_EMBED_STACK_SIZE) { \
size_t csize = sizeof(unpack_stack) * MSGPACK_EMBED_STACK_SIZE; \
size_t nsize = csize * 2; \
unpack_stack* tmp = (unpack_stack*)malloc(nsize); \
if(tmp == NULL) { goto _failed; } \
memcpy(tmp, ctx->stack, csize); \
ctx->stack = stack = tmp; \
ctx->stack_size = stack_size = MSGPACK_EMBED_STACK_SIZE * 2; \
} else { \
size_t nsize = sizeof(unpack_stack) * ctx->stack_size * 2; \
unpack_stack* tmp = (unpack_stack*)realloc(ctx->stack, nsize); \
if(tmp == NULL) { goto _failed; } \
ctx->stack = stack = tmp; \
ctx->stack_size = stack_size = stack_size * 2; \
} \
} \
*/ \
goto _header_again
#define NEXT_CS(p) ((unsigned int)*p & 0x1f)
@ -229,7 +207,8 @@ static inline int unpack_execute(unpack_context* ctx, const char* data, Py_ssize
case 0xdf: // map 32
again_fixed_trail(NEXT_CS(p), 2 << (((unsigned int)*p) & 0x01));
default:
goto _failed;
ret = -2;
goto _end;
}
SWITCH_RANGE(0xa0, 0xbf) // FixRaw
again_fixed_trail_if_zero(ACS_RAW_VALUE, ((unsigned int)*p & 0x1f), _raw_zero);
@ -239,7 +218,8 @@ static inline int unpack_execute(unpack_context* ctx, const char* data, Py_ssize
start_container(_map, ((unsigned int)*p) & 0x0f, CT_MAP_KEY);
SWITCH_RANGE_DEFAULT
goto _failed;
ret = -2;
goto _end;
SWITCH_RANGE_END
// end CS_HEADER
@ -262,17 +242,21 @@ static inline int unpack_execute(unpack_context* ctx, const char* data, Py_ssize
_msgpack_load32(uint32_t,n)+1,
_ext_zero);
case CS_FLOAT: {
union { uint32_t i; float f; } mem;
mem.i = _msgpack_load32(uint32_t,n);
push_fixed_value(_float, mem.f); }
case CS_DOUBLE: {
union { uint64_t i; double f; } mem;
mem.i = _msgpack_load64(uint64_t,n);
#if defined(__arm__) && !(__ARM_EABI__) // arm-oabi
// https://github.com/msgpack/msgpack-perl/pull/1
mem.i = (mem.i & 0xFFFFFFFFUL) << 32UL | (mem.i >> 32UL);
double f;
#if PY_VERSION_HEX >= 0x030B00A7
f = PyFloat_Unpack4((const char*)n, 0);
#else
f = _PyFloat_Unpack4((unsigned char*)n, 0);
#endif
push_fixed_value(_double, mem.f); }
push_fixed_value(_float, f); }
case CS_DOUBLE: {
double f;
#if PY_VERSION_HEX >= 0x030B00A7
f = PyFloat_Unpack8((const char*)n, 0);
#else
f = _PyFloat_Unpack8((unsigned char*)n, 0);
#endif
push_fixed_value(_double, f); }
case CS_UINT_8:
push_fixed_value(_uint8, *(uint8_t*)n);
case CS_UINT_16:
@ -401,6 +385,7 @@ _end:
#undef construct_cb
}
#undef NEXT_CS
#undef SWITCH_RANGE_BEGIN
#undef SWITCH_RANGE
#undef SWITCH_RANGE_DEFAULT
@ -412,68 +397,27 @@ _end:
#undef again_fixed_trail_if_zero
#undef start_container
template <unsigned int fixed_offset, unsigned int var_offset>
static inline int unpack_container_header(unpack_context* ctx, const char* data, Py_ssize_t len, Py_ssize_t* off)
{
assert(len >= *off);
uint32_t size;
const unsigned char *const p = (unsigned char*)data + *off;
#define inc_offset(inc) \
if (len - *off < inc) \
return 0; \
*off += inc;
switch (*p) {
case var_offset:
inc_offset(3);
size = _msgpack_load16(uint16_t, p + 1);
break;
case var_offset + 1:
inc_offset(5);
size = _msgpack_load32(uint32_t, p + 1);
break;
#ifdef USE_CASE_RANGE
case fixed_offset + 0x0 ... fixed_offset + 0xf:
#else
case fixed_offset + 0x0:
case fixed_offset + 0x1:
case fixed_offset + 0x2:
case fixed_offset + 0x3:
case fixed_offset + 0x4:
case fixed_offset + 0x5:
case fixed_offset + 0x6:
case fixed_offset + 0x7:
case fixed_offset + 0x8:
case fixed_offset + 0x9:
case fixed_offset + 0xa:
case fixed_offset + 0xb:
case fixed_offset + 0xc:
case fixed_offset + 0xd:
case fixed_offset + 0xe:
case fixed_offset + 0xf:
#endif
++*off;
size = ((unsigned int)*p) & 0x0f;
break;
default:
PyErr_SetString(PyExc_ValueError, "Unexpected type header on stream");
return -1;
}
unpack_callback_uint32(&ctx->user, size, &ctx->stack[0].obj);
return 1;
static int unpack_construct(unpack_context *ctx, const char *data, Py_ssize_t len, Py_ssize_t *off) {
return unpack_execute(1, ctx, data, len, off);
}
static int unpack_skip(unpack_context *ctx, const char *data, Py_ssize_t len, Py_ssize_t *off) {
return unpack_execute(0, ctx, data, len, off);
}
#undef SWITCH_RANGE_BEGIN
#undef SWITCH_RANGE
#undef SWITCH_RANGE_DEFAULT
#undef SWITCH_RANGE_END
#define unpack_container_header read_array_header
#define fixed_offset 0x90
#define var_offset 0xdc
#include "unpack_container_header.h"
#undef unpack_container_header
#undef fixed_offset
#undef var_offset
static const execute_fn unpack_construct = &unpack_execute<true>;
static const execute_fn unpack_skip = &unpack_execute<false>;
static const execute_fn read_array_header = &unpack_container_header<0x90, 0xdc>;
static const execute_fn read_map_header = &unpack_container_header<0x80, 0xde>;
#undef NEXT_CS
#define unpack_container_header read_map_header
#define fixed_offset 0x80
#define var_offset 0xde
#include "unpack_container_header.h"
#undef unpack_container_header
#undef fixed_offset
#undef var_offset
/* vim: set ts=4 sw=4 sts=4 expandtab */

45
pyproject.toml Normal file
View file

@ -0,0 +1,45 @@
[build-system]
requires = ["setuptools >= 78.1.1"]
build-backend = "setuptools.build_meta"
[project]
name = "msgpack"
dynamic = ["version"]
license = "Apache-2.0"
authors = [{name="Inada Naoki", email="songofacandy@gmail.com"}]
description = "MessagePack serializer"
readme = "README.md"
keywords = ["msgpack", "messagepack", "serializer", "serialization", "binary"]
requires-python = ">=3.10"
classifiers = [
"Development Status :: 5 - Production/Stable",
"Operating System :: OS Independent",
"Topic :: File Formats",
"Intended Audience :: Developers",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy",
]
[project.urls]
Homepage = "https://msgpack.org/"
Documentation = "https://msgpack-python.readthedocs.io/"
Repository = "https://github.com/msgpack/msgpack-python/"
Tracker = "https://github.com/msgpack/msgpack-python/issues"
Changelog = "https://github.com/msgpack/msgpack-python/blob/main/ChangeLog.rst"
[tool.setuptools]
# Do not install C/C++/Cython source files
include-package-data = false
[tool.setuptools.dynamic]
version = {attr = "msgpack.__version__"}
[tool.ruff]
line-length = 100
target-version = "py310"
lint.select = [
"E", # pycodestyle
"F", # Pyflakes
"I", # isort
#"UP", pyupgrade
]

View file

@ -1 +1,3 @@
Cython==0.27.3
Cython==3.2.1
setuptools==78.1.1
build

147
setup.py Executable file → Normal file
View file

@ -1,137 +1,32 @@
#!/usr/bin/env python
# coding: utf-8
import io
import os
import sys
from glob import glob
from distutils.command.sdist import sdist
from setuptools import setup, Extension
from distutils.command.build_ext import build_ext
from setuptools import Extension, setup
# for building transitional package.
TRANSITIONAL = False
class NoCython(Exception):
pass
try:
import Cython.Compiler.Main as cython_compiler
have_cython = True
except ImportError:
have_cython = False
def cythonize(src):
sys.stderr.write("cythonize: %r\n" % (src,))
cython_compiler.compile([src], cplus=True)
def ensure_source(src):
pyx = os.path.splitext(src)[0] + '.pyx'
if not os.path.exists(src):
if not have_cython:
raise NoCython
cythonize(pyx)
elif (os.path.exists(pyx) and
os.stat(src).st_mtime < os.stat(pyx).st_mtime and
have_cython):
cythonize(pyx)
return src
class BuildExt(build_ext):
def build_extension(self, ext):
try:
ext.sources = list(map(ensure_source, ext.sources))
except NoCython:
print("WARNING")
print("Cython is required for building extension from checkout.")
print("Install Cython >= 0.16 or install msgpack from PyPI.")
print("Falling back to pure Python implementation.")
return
try:
return build_ext.build_extension(self, ext)
except Exception as e:
print("WARNING: Failed to compile extension modules.")
print("msgpack uses fallback pure python implementation.")
print(e)
exec(open('msgpack/_version.py').read())
version_str = '.'.join(str(x) for x in version[:3])
if len(version) > 3 and version[3] != 'final':
version_str += version[3]
# take care of extension modules.
if have_cython:
class Sdist(sdist):
def __init__(self, *args, **kwargs):
for src in glob('msgpack/*.pyx'):
cythonize(src)
sdist.__init__(self, *args, **kwargs)
else:
Sdist = sdist
PYPY = hasattr(sys, "pypy_version_info")
libraries = []
if sys.platform == 'win32':
libraries.append('ws2_32')
if sys.byteorder == 'big':
macros = [('__BIG_ENDIAN__', '1')]
else:
macros = [('__LITTLE_ENDIAN__', '1')]
macros = []
ext_modules = []
if not hasattr(sys, 'pypy_version_info'):
ext_modules.append(Extension('msgpack._packer',
sources=['msgpack/_packer.cpp'],
libraries=libraries,
include_dirs=['.'],
define_macros=macros,
))
ext_modules.append(Extension('msgpack._unpacker',
sources=['msgpack/_unpacker.cpp'],
libraries=libraries,
include_dirs=['.'],
define_macros=macros,
))
if sys.platform == "win32":
libraries.append("ws2_32")
macros = [("__LITTLE_ENDIAN__", "1")]
if not PYPY and not os.environ.get("MSGPACK_PUREPYTHON"):
ext_modules.append(
Extension(
"msgpack._cmsgpack",
sources=["msgpack/_cmsgpack.c"],
libraries=libraries,
include_dirs=["."],
define_macros=macros,
)
)
del libraries, macros
desc = 'MessagePack (de)serializer.'
with io.open('README.rst', encoding='utf-8') as f:
long_desc = f.read()
del f
name = 'msgpack'
if TRANSITIONAL:
name = 'msgpack-python'
long_desc = "This package is deprecated. Install msgpack instead."
setup(name=name,
author='INADA Naoki',
author_email='songofacandy@gmail.com',
version=version_str,
cmdclass={'build_ext': BuildExt, 'sdist': Sdist},
ext_modules=ext_modules,
packages=['msgpack'],
description=desc,
long_description=long_desc,
url='http://msgpack.org/',
license='Apache 2.0',
classifiers=[
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: Implementation :: CPython',
'Programming Language :: Python :: Implementation :: PyPy',
'Intended Audience :: Developers',
'License :: OSI Approved :: Apache Software License',
],
setup(
ext_modules=ext_modules,
packages=["msgpack"],
)

View file

@ -1,29 +1,49 @@
#!/usr/bin/env python
# coding: utf-8
from pytest import raises
from msgpack import packb, unpackb
from msgpack import Packer, packb, unpackb
def test_unpack_buffer():
from array import array
buf = array('b')
buf.fromstring(packb((b'foo', b'bar')))
buf = array("b")
buf.frombytes(packb((b"foo", b"bar")))
obj = unpackb(buf, use_list=1)
assert [b'foo', b'bar'] == obj
assert [b"foo", b"bar"] == obj
def test_unpack_bytearray():
buf = bytearray(packb(('foo', 'bar')))
buf = bytearray(packb((b"foo", b"bar")))
obj = unpackb(buf, use_list=1)
assert [b'foo', b'bar'] == obj
assert [b"foo", b"bar"] == obj
expected_type = bytes
assert all(type(s) == expected_type for s in obj)
assert all(type(s) is expected_type for s in obj)
def test_unpack_memoryview():
buf = bytearray(packb(('foo', 'bar')))
buf = bytearray(packb((b"foo", b"bar")))
view = memoryview(buf)
obj = unpackb(view, use_list=1)
assert [b'foo', b'bar'] == obj
assert [b"foo", b"bar"] == obj
expected_type = bytes
assert all(type(s) == expected_type for s in obj)
assert all(type(s) is expected_type for s in obj)
def test_packer_getbuffer():
packer = Packer(autoreset=False)
packer.pack_array_header(2)
packer.pack(42)
packer.pack("hello")
buffer = packer.getbuffer()
assert isinstance(buffer, memoryview)
assert bytes(buffer) == b"\x92*\xa5hello"
if Packer.__module__ == "msgpack._cmsgpack": # only for Cython
# cython Packer supports buffer protocol directly
assert bytes(packer) == b"\x92*\xa5hello"
with raises(BufferError):
packer.pack(42)
buffer.release()
packer.pack(42)
assert bytes(packer) == b"\x92*\xa5hello*"

View file

@ -1,102 +1,136 @@
#!/usr/bin/env python
# coding: utf-8
from msgpack import packb, unpackb
def check(length, obj):
v = packb(obj)
assert len(v) == length, \
"%r length should be %r but get %r" % (obj, length, len(v))
assert unpackb(v, use_list=0) == obj
def check(length, obj, use_bin_type=True):
v = packb(obj, use_bin_type=use_bin_type)
assert len(v) == length, f"{obj!r} length should be {length!r} but get {len(v)!r}"
assert unpackb(v, use_list=0, raw=not use_bin_type) == obj
def test_1():
for o in [None, True, False, 0, 1, (1 << 6), (1 << 7) - 1, -1,
-((1<<5)-1), -(1<<5)]:
for o in [
None,
True,
False,
0,
1,
(1 << 6),
(1 << 7) - 1,
-1,
-((1 << 5) - 1),
-(1 << 5),
]:
check(1, o)
def test_2():
for o in [1 << 7, (1 << 8) - 1,
-((1<<5)+1), -(1<<7)
]:
for o in [1 << 7, (1 << 8) - 1, -((1 << 5) + 1), -(1 << 7)]:
check(2, o)
def test_3():
for o in [1 << 8, (1 << 16) - 1,
-((1<<7)+1), -(1<<15)]:
for o in [1 << 8, (1 << 16) - 1, -((1 << 7) + 1), -(1 << 15)]:
check(3, o)
def test_5():
for o in [1 << 16, (1 << 32) - 1,
-((1<<15)+1), -(1<<31)]:
for o in [1 << 16, (1 << 32) - 1, -((1 << 15) + 1), -(1 << 31)]:
check(5, o)
def test_9():
for o in [1 << 32, (1 << 64) - 1,
-((1<<31)+1), -(1<<63),
1.0, 0.1, -0.1, -1.0]:
for o in [
1 << 32,
(1 << 64) - 1,
-((1 << 31) + 1),
-(1 << 63),
1.0,
0.1,
-0.1,
-1.0,
]:
check(9, o)
def check_raw(overhead, num):
check(num + overhead, b" " * num)
check(num + overhead, b" " * num, use_bin_type=False)
def test_fixraw():
check_raw(1, 0)
check_raw(1, (1<<5) - 1)
check_raw(1, (1 << 5) - 1)
def test_raw16():
check_raw(3, 1<<5)
check_raw(3, (1<<16) - 1)
check_raw(3, 1 << 5)
check_raw(3, (1 << 16) - 1)
def test_raw32():
check_raw(5, 1<<16)
check_raw(5, 1 << 16)
def check_array(overhead, num):
check(num + overhead, (None,) * num)
def test_fixarray():
check_array(1, 0)
check_array(1, (1 << 4) - 1)
def test_array16():
check_array(3, 1 << 4)
check_array(3, (1<<16)-1)
check_array(3, (1 << 16) - 1)
def test_array32():
check_array(5, (1<<16))
check_array(5, (1 << 16))
def match(obj, buf):
assert packb(obj) == buf
assert unpackb(buf, use_list=0) == obj
assert unpackb(buf, use_list=0, strict_map_key=False) == obj
def test_match():
cases = [
(None, b'\xc0'),
(False, b'\xc2'),
(True, b'\xc3'),
(0, b'\x00'),
(127, b'\x7f'),
(128, b'\xcc\x80'),
(256, b'\xcd\x01\x00'),
(-1, b'\xff'),
(-33, b'\xd0\xdf'),
(-129, b'\xd1\xff\x7f'),
({1:1}, b'\x81\x01\x01'),
(None, b"\xc0"),
(False, b"\xc2"),
(True, b"\xc3"),
(0, b"\x00"),
(127, b"\x7f"),
(128, b"\xcc\x80"),
(256, b"\xcd\x01\x00"),
(-1, b"\xff"),
(-33, b"\xd0\xdf"),
(-129, b"\xd1\xff\x7f"),
({1: 1}, b"\x81\x01\x01"),
(1.0, b"\xcb\x3f\xf0\x00\x00\x00\x00\x00\x00"),
((), b'\x90'),
(tuple(range(15)),b"\x9f\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e"),
(tuple(range(16)),b"\xdc\x00\x10\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"),
({}, b'\x80'),
(dict([(x,x) for x in range(15)]), b'\x8f\x00\x00\x01\x01\x02\x02\x03\x03\x04\x04\x05\x05\x06\x06\x07\x07\x08\x08\t\t\n\n\x0b\x0b\x0c\x0c\r\r\x0e\x0e'),
(dict([(x,x) for x in range(16)]), b'\xde\x00\x10\x00\x00\x01\x01\x02\x02\x03\x03\x04\x04\x05\x05\x06\x06\x07\x07\x08\x08\t\t\n\n\x0b\x0b\x0c\x0c\r\r\x0e\x0e\x0f\x0f'),
]
((), b"\x90"),
(
tuple(range(15)),
b"\x9f\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e",
),
(
tuple(range(16)),
b"\xdc\x00\x10\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
),
({}, b"\x80"),
(
{x: x for x in range(15)},
b"\x8f\x00\x00\x01\x01\x02\x02\x03\x03\x04\x04\x05\x05\x06\x06\x07\x07\x08\x08\t\t\n\n\x0b\x0b\x0c\x0c\r\r\x0e\x0e",
),
(
{x: x for x in range(16)},
b"\xde\x00\x10\x00\x00\x01\x01\x02\x02\x03\x03\x04\x04\x05\x05\x06\x06\x07\x07\x08\x08\t\t\n\n\x0b\x0b\x0c\x0c\r\r\x0e\x0e\x0f\x0f",
),
]
for v, p in cases:
match(v, p)
def test_unicode():
assert unpackb(packb('foobar'), use_list=1) == b'foobar'
def test_unicode():
assert unpackb(packb("foobar"), use_list=1) == "foobar"

View file

@ -1,11 +1,11 @@
#!/usr/bin/env python
# coding: utf-8
from pytest import raises
from msgpack import packb, unpackb
import datetime
from pytest import raises
from msgpack import FormatError, OutOfData, StackError, Unpacker, packb, unpackb
class DummyException(Exception):
pass
@ -19,13 +19,45 @@ def test_raise_on_find_unsupported_value():
def test_raise_from_object_hook():
def hook(obj):
raise DummyException
raises(DummyException, unpackb, packb({}), object_hook=hook)
raises(DummyException, unpackb, packb({'fizz': 'buzz'}), object_hook=hook)
raises(DummyException, unpackb, packb({'fizz': 'buzz'}), object_pairs_hook=hook)
raises(DummyException, unpackb, packb({'fizz': {'buzz': 'spam'}}), object_hook=hook)
raises(DummyException, unpackb, packb({'fizz': {'buzz': 'spam'}}), object_pairs_hook=hook)
raises(DummyException, unpackb, packb({"fizz": "buzz"}), object_hook=hook)
raises(DummyException, unpackb, packb({"fizz": "buzz"}), object_pairs_hook=hook)
raises(DummyException, unpackb, packb({"fizz": {"buzz": "spam"}}), object_hook=hook)
raises(
DummyException,
unpackb,
packb({"fizz": {"buzz": "spam"}}),
object_pairs_hook=hook,
)
def test_invalidvalue():
incomplete = b"\xd9\x97#DL_" # raw8 - length=0x97
with raises(ValueError):
unpackb(b'\xd9\x97#DL_')
unpackb(incomplete)
with raises(OutOfData):
unpacker = Unpacker()
unpacker.feed(incomplete)
unpacker.unpack()
with raises(FormatError):
unpackb(b"\xc1") # (undefined tag)
with raises(FormatError):
unpackb(b"\x91\xc1") # fixarray(len=1) [ (undefined tag) ]
with raises(StackError):
unpackb(b"\x91" * 3000) # nested fixarray(len=1)
def test_strict_map_key():
valid = {"unicode": 1, b"bytes": 2}
packed = packb(valid, use_bin_type=True)
assert valid == unpackb(packed, raw=False, strict_map_key=True)
invalid = {42: 1}
packed = packb(invalid, use_bin_type=True)
with raises(ValueError):
unpackb(packed, raw=False, strict_map_key=True)

View file

@ -1,5 +1,5 @@
from __future__ import print_function
import array
import msgpack
from msgpack import ExtType
@ -9,65 +9,67 @@ def test_pack_ext_type():
packer = msgpack.Packer()
packer.pack_ext_type(0x42, s)
return packer.bytes()
assert p(b'A') == b'\xd4\x42A' # fixext 1
assert p(b'AB') == b'\xd5\x42AB' # fixext 2
assert p(b'ABCD') == b'\xd6\x42ABCD' # fixext 4
assert p(b'ABCDEFGH') == b'\xd7\x42ABCDEFGH' # fixext 8
assert p(b'A'*16) == b'\xd8\x42' + b'A'*16 # fixext 16
assert p(b'ABC') == b'\xc7\x03\x42ABC' # ext 8
assert p(b'A'*0x0123) == b'\xc8\x01\x23\x42' + b'A'*0x0123 # ext 16
assert p(b'A'*0x00012345) == b'\xc9\x00\x01\x23\x45\x42' + b'A'*0x00012345 # ext 32
assert p(b"A") == b"\xd4\x42A" # fixext 1
assert p(b"AB") == b"\xd5\x42AB" # fixext 2
assert p(b"ABCD") == b"\xd6\x42ABCD" # fixext 4
assert p(b"ABCDEFGH") == b"\xd7\x42ABCDEFGH" # fixext 8
assert p(b"A" * 16) == b"\xd8\x42" + b"A" * 16 # fixext 16
assert p(b"ABC") == b"\xc7\x03\x42ABC" # ext 8
assert p(b"A" * 0x0123) == b"\xc8\x01\x23\x42" + b"A" * 0x0123 # ext 16
assert p(b"A" * 0x00012345) == b"\xc9\x00\x01\x23\x45\x42" + b"A" * 0x00012345 # ext 32
def test_unpack_ext_type():
def check(b, expected):
assert msgpack.unpackb(b) == expected
check(b'\xd4\x42A', ExtType(0x42, b'A')) # fixext 1
check(b'\xd5\x42AB', ExtType(0x42, b'AB')) # fixext 2
check(b'\xd6\x42ABCD', ExtType(0x42, b'ABCD')) # fixext 4
check(b'\xd7\x42ABCDEFGH', ExtType(0x42, b'ABCDEFGH')) # fixext 8
check(b'\xd8\x42' + b'A'*16, ExtType(0x42, b'A'*16)) # fixext 16
check(b'\xc7\x03\x42ABC', ExtType(0x42, b'ABC')) # ext 8
check(b'\xc8\x01\x23\x42' + b'A'*0x0123,
ExtType(0x42, b'A'*0x0123)) # ext 16
check(b'\xc9\x00\x01\x23\x45\x42' + b'A'*0x00012345,
ExtType(0x42, b'A'*0x00012345)) # ext 32
check(b"\xd4\x42A", ExtType(0x42, b"A")) # fixext 1
check(b"\xd5\x42AB", ExtType(0x42, b"AB")) # fixext 2
check(b"\xd6\x42ABCD", ExtType(0x42, b"ABCD")) # fixext 4
check(b"\xd7\x42ABCDEFGH", ExtType(0x42, b"ABCDEFGH")) # fixext 8
check(b"\xd8\x42" + b"A" * 16, ExtType(0x42, b"A" * 16)) # fixext 16
check(b"\xc7\x03\x42ABC", ExtType(0x42, b"ABC")) # ext 8
check(b"\xc8\x01\x23\x42" + b"A" * 0x0123, ExtType(0x42, b"A" * 0x0123)) # ext 16
check(
b"\xc9\x00\x01\x23\x45\x42" + b"A" * 0x00012345,
ExtType(0x42, b"A" * 0x00012345),
) # ext 32
def test_extension_type():
def default(obj):
print('default called', obj)
print("default called", obj)
if isinstance(obj, array.array):
typecode = 123 # application specific typecode
data = obj.tostring()
typecode = 123 # application specific typecode
try:
data = obj.tobytes()
except AttributeError:
data = obj.tostring()
return ExtType(typecode, data)
raise TypeError("Unknown type object %r" % (obj,))
raise TypeError(f"Unknown type object {obj!r}")
def ext_hook(code, data):
print('ext_hook called', code, data)
print("ext_hook called", code, data)
assert code == 123
obj = array.array('d')
obj.fromstring(data)
obj = array.array("d")
obj.frombytes(data)
return obj
obj = [42, b'hello', array.array('d', [1.1, 2.2, 3.3])]
obj = [42, b"hello", array.array("d", [1.1, 2.2, 3.3])]
s = msgpack.packb(obj, default=default)
obj2 = msgpack.unpackb(s, ext_hook=ext_hook)
assert obj == obj2
import sys
if sys.version > '3':
long = int
def test_overriding_hooks():
def default(obj):
if isinstance(obj, long):
if isinstance(obj, int):
return {"__type__": "long", "__data__": str(obj)}
else:
return obj
obj = {"testval": long(1823746192837461928374619)}
obj = {"testval": 1823746192837461928374619}
refobj = {"testval": default(obj["testval"])}
refout = msgpack.packb(refobj)
assert isinstance(refout, (str, bytes))

View file

@ -1,70 +1,88 @@
#!/usr/bin/env python
# coding: utf-8
from msgpack import unpackb
def check(src, should, use_list=0):
assert unpackb(src, use_list=use_list) == should
def check(src, should, use_list=0, raw=True):
assert unpackb(src, use_list=use_list, raw=raw, strict_map_key=False) == should
def testSimpleValue():
check(b"\x93\xc0\xc2\xc3",
(None, False, True,))
check(b"\x93\xc0\xc2\xc3", (None, False, True))
def testFixnum():
check(b"\x92\x93\x00\x40\x7f\x93\xe0\xf0\xff",
((0,64,127,), (-32,-16,-1,),)
)
check(b"\x92\x93\x00\x40\x7f\x93\xe0\xf0\xff", ((0, 64, 127), (-32, -16, -1)))
def testFixArray():
check(b"\x92\x90\x91\x91\xc0",
((),((None,),),),
)
check(b"\x92\x90\x91\x91\xc0", ((), ((None,),)))
def testFixRaw():
check(b"\x94\xa0\xa1a\xa2bc\xa3def",
(b"", b"a", b"bc", b"def",),
)
check(b"\x94\xa0\xa1a\xa2bc\xa3def", (b"", b"a", b"bc", b"def"))
def testFixMap():
check(
b"\x82\xc2\x81\xc0\xc0\xc3\x81\xc0\x80",
{False: {None: None}, True:{None:{}}},
)
check(b"\x82\xc2\x81\xc0\xc0\xc3\x81\xc0\x80", {False: {None: None}, True: {None: {}}})
def testUnsignedInt():
check(
b"\x99\xcc\x00\xcc\x80\xcc\xff\xcd\x00\x00\xcd\x80\x00"
b"\xcd\xff\xff\xce\x00\x00\x00\x00\xce\x80\x00\x00\x00"
b"\xce\xff\xff\xff\xff",
(0, 128, 255, 0, 32768, 65535, 0, 2147483648, 4294967295,),
)
b"\x99\xcc\x00\xcc\x80\xcc\xff\xcd\x00\x00\xcd\x80\x00"
b"\xcd\xff\xff\xce\x00\x00\x00\x00\xce\x80\x00\x00\x00"
b"\xce\xff\xff\xff\xff",
(0, 128, 255, 0, 32768, 65535, 0, 2147483648, 4294967295),
)
def testSignedInt():
check(b"\x99\xd0\x00\xd0\x80\xd0\xff\xd1\x00\x00\xd1\x80\x00"
b"\xd1\xff\xff\xd2\x00\x00\x00\x00\xd2\x80\x00\x00\x00"
b"\xd2\xff\xff\xff\xff",
(0, -128, -1, 0, -32768, -1, 0, -2147483648, -1,))
check(
b"\x99\xd0\x00\xd0\x80\xd0\xff\xd1\x00\x00\xd1\x80\x00"
b"\xd1\xff\xff\xd2\x00\x00\x00\x00\xd2\x80\x00\x00\x00"
b"\xd2\xff\xff\xff\xff",
(0, -128, -1, 0, -32768, -1, 0, -2147483648, -1),
)
def testRaw():
check(b"\x96\xda\x00\x00\xda\x00\x01a\xda\x00\x02ab\xdb\x00\x00"
check(
b"\x96\xda\x00\x00\xda\x00\x01a\xda\x00\x02ab\xdb\x00\x00"
b"\x00\x00\xdb\x00\x00\x00\x01a\xdb\x00\x00\x00\x02ab",
(b"", b"a", b"ab", b"", b"a", b"ab"))
(b"", b"a", b"ab", b"", b"a", b"ab"),
)
check(
b"\x96\xda\x00\x00\xda\x00\x01a\xda\x00\x02ab\xdb\x00\x00"
b"\x00\x00\xdb\x00\x00\x00\x01a\xdb\x00\x00\x00\x02ab",
("", "a", "ab", "", "a", "ab"),
raw=False,
)
def testArray():
check(b"\x96\xdc\x00\x00\xdc\x00\x01\xc0\xdc\x00\x02\xc2\xc3\xdd\x00"
check(
b"\x96\xdc\x00\x00\xdc\x00\x01\xc0\xdc\x00\x02\xc2\xc3\xdd\x00"
b"\x00\x00\x00\xdd\x00\x00\x00\x01\xc0\xdd\x00\x00\x00\x02"
b"\xc2\xc3",
((), (None,), (False,True), (), (None,), (False,True))
)
((), (None,), (False, True), (), (None,), (False, True)),
)
def testMap():
check(
b"\x96"
b"\xde\x00\x00"
b"\xde\x00\x01\xc0\xc2"
b"\xde\x00\x02\xc0\xc2\xc3\xc2"
b"\xdf\x00\x00\x00\x00"
b"\xdf\x00\x00\x00\x01\xc0\xc2"
b"\xdf\x00\x00\x00\x02\xc0\xc2\xc3\xc2",
({}, {None: False}, {True: False, None: False}, {},
{None: False}, {True: False, None: False}))
b"\xde\x00\x00"
b"\xde\x00\x01\xc0\xc2"
b"\xde\x00\x02\xc0\xc2\xc3\xc2"
b"\xdf\x00\x00\x00\x00"
b"\xdf\x00\x00\x00\x01\xc0\xc2"
b"\xdf\x00\x00\x00\x02\xc0\xc2\xc3\xc2",
(
{},
{None: False},
{True: False, None: False},
{},
{None: False},
{True: False, None: False},
),
)

View file

@ -1,56 +1,60 @@
#!/usr/bin/env python
# coding: utf-8
from __future__ import absolute_import, division, print_function, unicode_literals
import pytest
from msgpack import (
packb, unpackb, Packer, Unpacker, ExtType,
PackOverflowError, PackValueError, UnpackValueError,
ExtType,
Packer,
PackOverflowError,
PackValueError,
Unpacker,
UnpackValueError,
packb,
unpackb,
)
def test_integer():
x = -(2 ** 63)
x = -(2**63)
assert unpackb(packb(x)) == x
with pytest.raises(PackOverflowError):
packb(x-1)
packb(x - 1)
x = 2 ** 64 - 1
x = 2**64 - 1
assert unpackb(packb(x)) == x
with pytest.raises(PackOverflowError):
packb(x+1)
packb(x + 1)
def test_array_header():
packer = Packer()
packer.pack_array_header(2**32-1)
packer.pack_array_header(2**32 - 1)
with pytest.raises(PackValueError):
packer.pack_array_header(2**32)
def test_map_header():
packer = Packer()
packer.pack_map_header(2**32-1)
packer.pack_map_header(2**32 - 1)
with pytest.raises(PackValueError):
packer.pack_array_header(2**32)
def test_max_str_len():
d = 'x' * 3
d = "x" * 3
packed = packb(d)
unpacker = Unpacker(max_str_len=3, encoding='utf-8')
unpacker = Unpacker(max_str_len=3, raw=False)
unpacker.feed(packed)
assert unpacker.unpack() == d
unpacker = Unpacker(max_str_len=2, encoding='utf-8')
unpacker = Unpacker(max_str_len=2, raw=False)
with pytest.raises(UnpackValueError):
unpacker.feed(packed)
unpacker.unpack()
def test_max_bin_len():
d = b'x' * 3
d = b"x" * 3
packed = packb(d, use_bin_type=True)
unpacker = Unpacker(max_bin_len=3)
@ -64,7 +68,7 @@ def test_max_bin_len():
def test_max_array_len():
d = [1,2,3]
d = [1, 2, 3]
packed = packb(d)
unpacker = Unpacker(max_array_len=3)
@ -81,11 +85,11 @@ def test_max_map_len():
d = {1: 2, 3: 4, 5: 6}
packed = packb(d)
unpacker = Unpacker(max_map_len=3)
unpacker = Unpacker(max_map_len=3, strict_map_key=False)
unpacker.feed(packed)
assert unpacker.unpack() == d
unpacker = Unpacker(max_map_len=2)
unpacker = Unpacker(max_map_len=2, strict_map_key=False)
with pytest.raises(UnpackValueError):
unpacker.feed(packed)
unpacker.unpack()
@ -105,11 +109,10 @@ def test_max_ext_len():
unpacker.unpack()
# PyPy fails following tests because of constant folding?
# https://bugs.pypy.org/issue1721
#@pytest.mark.skipif(True, reason="Requires very large memory.")
#def test_binary():
# @pytest.mark.skipif(True, reason="Requires very large memory.")
# def test_binary():
# x = b'x' * (2**32 - 1)
# assert unpackb(packb(x)) == x
# del x
@ -118,8 +121,8 @@ def test_max_ext_len():
# packb(x)
#
#
#@pytest.mark.skipif(True, reason="Requires very large memory.")
#def test_string():
# @pytest.mark.skipif(True, reason="Requires very large memory.")
# def test_string():
# x = 'x' * (2**32 - 1)
# assert unpackb(packb(x)) == x
# x += 'y'
@ -127,10 +130,36 @@ def test_max_ext_len():
# packb(x)
#
#
#@pytest.mark.skipif(True, reason="Requires very large memory.")
#def test_array():
# @pytest.mark.skipif(True, reason="Requires very large memory.")
# def test_array():
# x = [0] * (2**32 - 1)
# assert unpackb(packb(x)) == x
# x.append(0)
# with pytest.raises(ValueError):
# packb(x)
# auto max len
def test_auto_max_array_len():
packed = b"\xde\x00\x06zz"
with pytest.raises(UnpackValueError):
unpackb(packed, raw=False)
unpacker = Unpacker(max_buffer_size=5, raw=False)
unpacker.feed(packed)
with pytest.raises(UnpackValueError):
unpacker.unpack()
def test_auto_max_map_len():
# len(packed) == 6 -> max_map_len == 3
packed = b"\xde\x00\x04zzz"
with pytest.raises(UnpackValueError):
unpackb(packed, raw=False)
unpacker = Unpacker(max_buffer_size=6, raw=False)
unpacker.feed(packed)
with pytest.raises(UnpackValueError):
unpacker.unpack()

View file

@ -1,47 +1,26 @@
#!/usr/bin/env python
# coding: utf-8
from array import array
from msgpack import packb, unpackb
import sys
# For Python < 3:
# - array type only supports old buffer interface
# - array.frombytes is not available, must use deprecated array.fromstring
if sys.version_info[0] < 3:
def make_memoryview(obj):
return memoryview(buffer(obj))
def make_array(f, data):
a = array(f)
a.fromstring(data)
return a
def get_data(a):
return a.tostring()
else:
make_memoryview = memoryview
def make_array(f, data):
a = array(f)
a.frombytes(data)
return a
def get_data(a):
return a.tobytes()
def make_array(f, data):
a = array(f)
a.frombytes(data)
return a
def _runtest(format, nbytes, expected_header, expected_prefix, use_bin_type):
# create a new array
original_array = array(format)
original_array.fromlist([255] * (nbytes // original_array.itemsize))
original_data = get_data(original_array)
view = make_memoryview(original_array)
original_data = original_array.tobytes()
view = memoryview(original_array)
# pack, unpack, and reconstruct array
packed = packb(view, use_bin_type=use_bin_type)
unpacked = unpackb(packed)
unpacked = unpackb(packed, raw=(not use_bin_type))
reconstructed_array = make_array(format, unpacked)
# check that we got the right amount of data
@ -49,64 +28,72 @@ def _runtest(format, nbytes, expected_header, expected_prefix, use_bin_type):
# check packed header
assert packed[:1] == expected_header
# check packed length prefix, if any
assert packed[1:1+len(expected_prefix)] == expected_prefix
assert packed[1 : 1 + len(expected_prefix)] == expected_prefix
# check packed data
assert packed[1+len(expected_prefix):] == original_data
assert packed[1 + len(expected_prefix) :] == original_data
# check array unpacked correctly
assert original_array == reconstructed_array
def test_fixstr_from_byte():
_runtest('B', 1, b'\xa1', b'', False)
_runtest('B', 31, b'\xbf', b'', False)
_runtest("B", 1, b"\xa1", b"", False)
_runtest("B", 31, b"\xbf", b"", False)
def test_fixstr_from_float():
_runtest('f', 4, b'\xa4', b'', False)
_runtest('f', 28, b'\xbc', b'', False)
_runtest("f", 4, b"\xa4", b"", False)
_runtest("f", 28, b"\xbc", b"", False)
def test_str16_from_byte():
_runtest('B', 2**8, b'\xda', b'\x01\x00', False)
_runtest('B', 2**16-1, b'\xda', b'\xff\xff', False)
_runtest("B", 2**8, b"\xda", b"\x01\x00", False)
_runtest("B", 2**16 - 1, b"\xda", b"\xff\xff", False)
def test_str16_from_float():
_runtest('f', 2**8, b'\xda', b'\x01\x00', False)
_runtest('f', 2**16-4, b'\xda', b'\xff\xfc', False)
_runtest("f", 2**8, b"\xda", b"\x01\x00", False)
_runtest("f", 2**16 - 4, b"\xda", b"\xff\xfc", False)
def test_str32_from_byte():
_runtest('B', 2**16, b'\xdb', b'\x00\x01\x00\x00', False)
_runtest("B", 2**16, b"\xdb", b"\x00\x01\x00\x00", False)
def test_str32_from_float():
_runtest('f', 2**16, b'\xdb', b'\x00\x01\x00\x00', False)
_runtest("f", 2**16, b"\xdb", b"\x00\x01\x00\x00", False)
def test_bin8_from_byte():
_runtest('B', 1, b'\xc4', b'\x01', True)
_runtest('B', 2**8-1, b'\xc4', b'\xff', True)
_runtest("B", 1, b"\xc4", b"\x01", True)
_runtest("B", 2**8 - 1, b"\xc4", b"\xff", True)
def test_bin8_from_float():
_runtest('f', 4, b'\xc4', b'\x04', True)
_runtest('f', 2**8-4, b'\xc4', b'\xfc', True)
_runtest("f", 4, b"\xc4", b"\x04", True)
_runtest("f", 2**8 - 4, b"\xc4", b"\xfc", True)
def test_bin16_from_byte():
_runtest('B', 2**8, b'\xc5', b'\x01\x00', True)
_runtest('B', 2**16-1, b'\xc5', b'\xff\xff', True)
_runtest("B", 2**8, b"\xc5", b"\x01\x00", True)
_runtest("B", 2**16 - 1, b"\xc5", b"\xff\xff", True)
def test_bin16_from_float():
_runtest('f', 2**8, b'\xc5', b'\x01\x00', True)
_runtest('f', 2**16-4, b'\xc5', b'\xff\xfc', True)
_runtest("f", 2**8, b"\xc5", b"\x01\x00", True)
_runtest("f", 2**16 - 4, b"\xc5", b"\xff\xfc", True)
def test_bin32_from_byte():
_runtest('B', 2**16, b'\xc6', b'\x00\x01\x00\x00', True)
_runtest("B", 2**16, b"\xc6", b"\x00\x01\x00\x00", True)
def test_bin32_from_float():
_runtest('f', 2**16, b'\xc6', b'\x00\x01\x00\x00', True)
_runtest("f", 2**16, b"\xc6", b"\x00\x01\x00\x00", True)
def test_multidim_memoryview():
# See https://github.com/msgpack/msgpack-python/issues/526
view = memoryview(b"\00" * 6)
data = view.cast(view.format, (3, 2))
packed = packb(data)
assert packed == b"\xc4\x06\x00\x00\x00\x00\x00\x00"

View file

@ -1,88 +1,90 @@
# coding: utf-8
from msgpack import packb, unpackb, ExtType
from msgpack import ExtType, packb, unpackb
def test_str8():
header = b'\xd9'
data = b'x' * 32
header = b"\xd9"
data = b"x" * 32
b = packb(data.decode(), use_bin_type=True)
assert len(b) == len(data) + 2
assert b[0:2] == header + b'\x20'
assert b[0:2] == header + b"\x20"
assert b[2:] == data
assert unpackb(b) == data
assert unpackb(b, raw=True) == data
assert unpackb(b, raw=False) == data.decode()
data = b'x' * 255
data = b"x" * 255
b = packb(data.decode(), use_bin_type=True)
assert len(b) == len(data) + 2
assert b[0:2] == header + b'\xff'
assert b[0:2] == header + b"\xff"
assert b[2:] == data
assert unpackb(b) == data
assert unpackb(b, raw=True) == data
assert unpackb(b, raw=False) == data.decode()
def test_bin8():
header = b'\xc4'
data = b''
header = b"\xc4"
data = b""
b = packb(data, use_bin_type=True)
assert len(b) == len(data) + 2
assert b[0:2] == header + b'\x00'
assert b[0:2] == header + b"\x00"
assert b[2:] == data
assert unpackb(b) == data
data = b'x' * 255
data = b"x" * 255
b = packb(data, use_bin_type=True)
assert len(b) == len(data) + 2
assert b[0:2] == header + b'\xff'
assert b[0:2] == header + b"\xff"
assert b[2:] == data
assert unpackb(b) == data
def test_bin16():
header = b'\xc5'
data = b'x' * 256
header = b"\xc5"
data = b"x" * 256
b = packb(data, use_bin_type=True)
assert len(b) == len(data) + 3
assert b[0:1] == header
assert b[1:3] == b'\x01\x00'
assert b[1:3] == b"\x01\x00"
assert b[3:] == data
assert unpackb(b) == data
data = b'x' * 65535
data = b"x" * 65535
b = packb(data, use_bin_type=True)
assert len(b) == len(data) + 3
assert b[0:1] == header
assert b[1:3] == b'\xff\xff'
assert b[1:3] == b"\xff\xff"
assert b[3:] == data
assert unpackb(b) == data
def test_bin32():
header = b'\xc6'
data = b'x' * 65536
header = b"\xc6"
data = b"x" * 65536
b = packb(data, use_bin_type=True)
assert len(b) == len(data) + 5
assert b[0:1] == header
assert b[1:5] == b'\x00\x01\x00\x00'
assert b[1:5] == b"\x00\x01\x00\x00"
assert b[5:] == data
assert unpackb(b) == data
def test_ext():
def check(ext, packed):
assert packb(ext) == packed
assert unpackb(packed) == ext
check(ExtType(0x42, b'Z'), b'\xd4\x42Z') # fixext 1
check(ExtType(0x42, b'ZZ'), b'\xd5\x42ZZ') # fixext 2
check(ExtType(0x42, b'Z'*4), b'\xd6\x42' + b'Z'*4) # fixext 4
check(ExtType(0x42, b'Z'*8), b'\xd7\x42' + b'Z'*8) # fixext 8
check(ExtType(0x42, b'Z'*16), b'\xd8\x42' + b'Z'*16) # fixext 16
check(ExtType(0x42, b"Z"), b"\xd4\x42Z") # fixext 1
check(ExtType(0x42, b"ZZ"), b"\xd5\x42ZZ") # fixext 2
check(ExtType(0x42, b"Z" * 4), b"\xd6\x42" + b"Z" * 4) # fixext 4
check(ExtType(0x42, b"Z" * 8), b"\xd7\x42" + b"Z" * 8) # fixext 8
check(ExtType(0x42, b"Z" * 16), b"\xd8\x42" + b"Z" * 16) # fixext 16
# ext 8
check(ExtType(0x42, b''), b'\xc7\x00\x42')
check(ExtType(0x42, b'Z'*255), b'\xc7\xff\x42' + b'Z'*255)
check(ExtType(0x42, b""), b"\xc7\x00\x42")
check(ExtType(0x42, b"Z" * 255), b"\xc7\xff\x42" + b"Z" * 255)
# ext 16
check(ExtType(0x42, b'Z'*256), b'\xc8\x01\x00\x42' + b'Z'*256)
check(ExtType(0x42, b'Z'*0xffff), b'\xc8\xff\xff\x42' + b'Z'*0xffff)
check(ExtType(0x42, b"Z" * 256), b"\xc8\x01\x00\x42" + b"Z" * 256)
check(ExtType(0x42, b"Z" * 0xFFFF), b"\xc8\xff\xff\x42" + b"Z" * 0xFFFF)
# ext 32
check(ExtType(0x42, b'Z'*0x10000), b'\xc9\x00\x01\x00\x00\x42' + b'Z'*0x10000)
check(ExtType(0x42, b"Z" * 0x10000), b"\xc9\x00\x01\x00\x00\x42" + b"Z" * 0x10000)
# needs large memory
#check(ExtType(0x42, b'Z'*0xffffffff),
# check(ExtType(0x42, b'Z'*0xffffffff),
# b'\xc9\xff\xff\xff\xff\x42' + b'Z'*0xffffffff)

View file

@ -1,67 +1,82 @@
#!/usr/bin/env python
# coding: utf-8
from pytest import raises
from msgpack import packb, unpackb
def _decode_complex(obj):
if b'__complex__' in obj:
return complex(obj[b'real'], obj[b'imag'])
if b"__complex__" in obj:
return complex(obj[b"real"], obj[b"imag"])
return obj
def _encode_complex(obj):
if isinstance(obj, complex):
return {b'__complex__': True, b'real': 1, b'imag': 2}
return {b"__complex__": True, b"real": 1, b"imag": 2}
return obj
def test_encode_hook():
packed = packb([3, 1+2j], default=_encode_complex)
packed = packb([3, 1 + 2j], default=_encode_complex)
unpacked = unpackb(packed, use_list=1)
assert unpacked[1] == {b'__complex__': True, b'real': 1, b'imag': 2}
assert unpacked[1] == {b"__complex__": True, b"real": 1, b"imag": 2}
def test_decode_hook():
packed = packb([3, {b'__complex__': True, b'real': 1, b'imag': 2}])
packed = packb([3, {b"__complex__": True, b"real": 1, b"imag": 2}])
unpacked = unpackb(packed, object_hook=_decode_complex, use_list=1)
assert unpacked[1] == 1+2j
assert unpacked[1] == 1 + 2j
def test_decode_pairs_hook():
packed = packb([3, {1: 2, 3: 4}])
prod_sum = 1 * 2 + 3 * 4
unpacked = unpackb(packed, object_pairs_hook=lambda l: sum(k * v for k, v in l), use_list=1)
unpacked = unpackb(
packed,
object_pairs_hook=lambda lst: sum(k * v for k, v in lst),
use_list=1,
strict_map_key=False,
)
assert unpacked[1] == prod_sum
def test_only_one_obj_hook():
with raises(TypeError):
unpackb(b'', object_hook=lambda x: x, object_pairs_hook=lambda x: x)
unpackb(b"", object_hook=lambda x: x, object_pairs_hook=lambda x: x)
def test_bad_hook():
with raises(TypeError):
packed = packb([3, 1+2j], default=lambda o: o)
unpacked = unpackb(packed, use_list=1)
packed = packb([3, 1 + 2j], default=lambda o: o)
unpackb(packed, use_list=1)
def _arr_to_str(arr):
return ''.join(str(c) for c in arr)
return "".join(str(c) for c in arr)
def test_array_hook():
packed = packb([1,2,3])
packed = packb([1, 2, 3])
unpacked = unpackb(packed, list_hook=_arr_to_str, use_list=1)
assert unpacked == '123'
assert unpacked == "123"
class DecodeError(Exception):
pass
def bad_complex_decoder(o):
raise DecodeError("Ooops!")
def test_an_exception_in_objecthook1():
with raises(DecodeError):
packed = packb({1: {'__complex__': True, 'real': 1, 'imag': 2}})
unpackb(packed, object_hook=bad_complex_decoder)
packed = packb({1: {"__complex__": True, "real": 1, "imag": 2}})
unpackb(packed, object_hook=bad_complex_decoder, strict_map_key=False)
def test_an_exception_in_objecthook2():
with raises(DecodeError):
packed = packb({1: [{'__complex__': True, 'real': 1, 'imag': 2}]})
unpackb(packed, list_hook=bad_complex_decoder, use_list=1)
packed = packb({1: [{"__complex__": True, "real": 1, "imag": 2}]})
unpackb(packed, list_hook=bad_complex_decoder, use_list=1, strict_map_key=False)

View file

@ -1,98 +1,110 @@
#!/usr/bin/env python
# coding: utf-8
from __future__ import absolute_import, division, print_function, unicode_literals
import struct
from pytest import raises, xfail
from msgpack import packb, unpackb, Unpacker, Packer
from collections import OrderedDict
from io import BytesIO
import pytest
from msgpack import Packer, Unpacker, packb, unpackb
def check(data, use_list=False):
re = unpackb(packb(data), use_list=use_list)
re = unpackb(packb(data), use_list=use_list, strict_map_key=False)
assert re == data
def testPack():
test_data = [
0, 1, 127, 128, 255, 256, 65535, 65536, 4294967295, 4294967296,
-1, -32, -33, -128, -129, -32768, -32769, -4294967296, -4294967297,
1.0,
b"", b"a", b"a"*31, b"a"*32,
None, True, False,
(), ((),), ((), None,),
0,
1,
127,
128,
255,
256,
65535,
65536,
4294967295,
4294967296,
-1,
-32,
-33,
-128,
-129,
-32768,
-32769,
-4294967296,
-4294967297,
1.0,
b"",
b"a",
b"a" * 31,
b"a" * 32,
None,
True,
False,
(),
((),),
((), None),
{None: 0},
(1<<23),
]
(1 << 23),
]
for td in test_data:
check(td)
def testPackUnicode():
test_data = ["", "abcd", ["defgh"], "Русский текст"]
for td in test_data:
re = unpackb(packb(td, encoding='utf-8'), use_list=1, encoding='utf-8')
re = unpackb(packb(td), use_list=1, raw=False)
assert re == td
packer = Packer(encoding='utf-8')
packer = Packer()
data = packer.pack(td)
re = Unpacker(BytesIO(data), encoding=str('utf-8'), use_list=1).unpack()
re = Unpacker(BytesIO(data), raw=False, use_list=1).unpack()
assert re == td
def testPackUTF32():
try:
test_data = [
"",
"abcd",
["defgh"],
"Русский текст",
]
for td in test_data:
re = unpackb(packb(td, encoding='utf-32'), use_list=1, encoding='utf-32')
assert re == td
except LookupError as e:
xfail(e)
def testPackBytes():
test_data = [
b"", b"abcd", (b"defgh",),
]
test_data = [b"", b"abcd", (b"defgh",)]
for td in test_data:
check(td)
def testPackByteArrays():
test_data = [
bytearray(b""), bytearray(b"abcd"), (bytearray(b"defgh"),),
]
test_data = [bytearray(b""), bytearray(b"abcd"), (bytearray(b"defgh"),)]
for td in test_data:
check(td)
def testIgnoreUnicodeErrors():
re = unpackb(packb(b'abc\xeddef'), encoding='utf-8', unicode_errors='ignore', use_list=1)
re = unpackb(packb(b"abc\xeddef", use_bin_type=False), raw=False, unicode_errors="ignore")
assert re == "abcdef"
def testStrictUnicodeUnpack():
with raises(UnicodeDecodeError):
unpackb(packb(b'abc\xeddef'), encoding='utf-8', use_list=1)
packed = packb(b"abc\xeddef", use_bin_type=False)
with pytest.raises(UnicodeDecodeError):
unpackb(packed, raw=False, use_list=1)
def testStrictUnicodePack():
with raises(UnicodeEncodeError):
packb("abc\xeddef", encoding='ascii', unicode_errors='strict')
def testIgnoreErrorsPack():
re = unpackb(packb("abcФФФdef", encoding='ascii', unicode_errors='ignore'), encoding='utf-8', use_list=1)
re = unpackb(
packb("abc\udc80\udcffdef", use_bin_type=True, unicode_errors="ignore"),
raw=False,
use_list=1,
)
assert re == "abcdef"
def testNoEncoding():
with raises(TypeError):
packb("abc", encoding=None)
def testDecodeBinary():
re = unpackb(packb(b"abc"), encoding=None, use_list=1)
re = unpackb(packb(b"abc"), use_list=1)
assert re == b"abc"
def testPackFloat():
assert packb(1.0, use_single_float=True) == b'\xca' + struct.pack(str('>f'), 1.0)
assert packb(1.0, use_single_float=False) == b'\xcb' + struct.pack(str('>d'), 1.0)
assert packb(1.0, use_single_float=True) == b"\xca" + struct.pack(">f", 1.0)
assert packb(1.0, use_single_float=False) == b"\xcb" + struct.pack(">d", 1.0)
def testArraySize(sizes=[0, 5, 50, 1000]):
bio = BytesIO()
@ -107,6 +119,7 @@ def testArraySize(sizes=[0, 5, 50, 1000]):
for size in sizes:
assert unpacker.unpack() == list(range(size))
def test_manualreset(sizes=[0, 5, 50, 1000]):
packer = Packer(autoreset=False)
for size in sizes:
@ -120,7 +133,8 @@ def test_manualreset(sizes=[0, 5, 50, 1000]):
assert unpacker.unpack() == list(range(size))
packer.reset()
assert packer.bytes() == b''
assert packer.bytes() == b""
def testMapSize(sizes=[0, 5, 50, 1000]):
bio = BytesIO()
@ -128,27 +142,40 @@ def testMapSize(sizes=[0, 5, 50, 1000]):
for size in sizes:
bio.write(packer.pack_map_header(size))
for i in range(size):
bio.write(packer.pack(i)) # key
bio.write(packer.pack(i * 2)) # value
bio.write(packer.pack(i)) # key
bio.write(packer.pack(i * 2)) # value
bio.seek(0)
unpacker = Unpacker(bio)
unpacker = Unpacker(bio, strict_map_key=False)
for size in sizes:
assert unpacker.unpack() == dict((i, i * 2) for i in range(size))
assert unpacker.unpack() == {i: i * 2 for i in range(size)}
def test_odict():
seq = [(b'one', 1), (b'two', 2), (b'three', 3), (b'four', 4)]
seq = [(b"one", 1), (b"two", 2), (b"three", 3), (b"four", 4)]
od = OrderedDict(seq)
assert unpackb(packb(od), use_list=1) == dict(seq)
def pair_hook(seq):
return list(seq)
assert unpackb(packb(od), object_pairs_hook=pair_hook, use_list=1) == seq
def test_pairlist():
pairlist = [(b'a', 1), (2, b'b'), (b'foo', b'bar')]
pairlist = [(b"a", 1), (2, b"b"), (b"foo", b"bar")]
packer = Packer()
packed = packer.pack_map_pairs(pairlist)
unpacked = unpackb(packed, object_pairs_hook=list)
unpacked = unpackb(packed, object_pairs_hook=list, strict_map_key=False)
assert pairlist == unpacked
def test_get_buffer():
packer = Packer(autoreset=0, use_bin_type=True)
packer.pack([1, 2])
strm = BytesIO()
strm.write(packer.getbuffer())
written = strm.getvalue()
expected = packb([1, 2], use_bin_type=True)
assert written == expected

View file

@ -1,66 +1,72 @@
"""Test Unpacker's read_array_header and read_map_header methods"""
from msgpack import packb, Unpacker, OutOfData
from msgpack import OutOfData, Unpacker, packb
UnexpectedTypeException = ValueError
def test_read_array_header():
unpacker = Unpacker()
unpacker.feed(packb(['a', 'b', 'c']))
unpacker.feed(packb(["a", "b", "c"]))
assert unpacker.read_array_header() == 3
assert unpacker.unpack() == b'a'
assert unpacker.unpack() == b'b'
assert unpacker.unpack() == b'c'
assert unpacker.unpack() == "a"
assert unpacker.unpack() == "b"
assert unpacker.unpack() == "c"
try:
unpacker.unpack()
assert 0, 'should raise exception'
assert 0, "should raise exception"
except OutOfData:
assert 1, 'okay'
assert 1, "okay"
def test_read_map_header():
unpacker = Unpacker()
unpacker.feed(packb({'a': 'A'}))
unpacker.feed(packb({"a": "A"}))
assert unpacker.read_map_header() == 1
assert unpacker.unpack() == B'a'
assert unpacker.unpack() == B'A'
assert unpacker.unpack() == "a"
assert unpacker.unpack() == "A"
try:
unpacker.unpack()
assert 0, 'should raise exception'
assert 0, "should raise exception"
except OutOfData:
assert 1, 'okay'
assert 1, "okay"
def test_incorrect_type_array():
unpacker = Unpacker()
unpacker.feed(packb(1))
try:
unpacker.read_array_header()
assert 0, 'should raise exception'
assert 0, "should raise exception"
except UnexpectedTypeException:
assert 1, 'okay'
assert 1, "okay"
def test_incorrect_type_map():
unpacker = Unpacker()
unpacker.feed(packb(1))
try:
unpacker.read_map_header()
assert 0, 'should raise exception'
assert 0, "should raise exception"
except UnexpectedTypeException:
assert 1, 'okay'
assert 1, "okay"
def test_correct_type_nested_array():
unpacker = Unpacker()
unpacker.feed(packb({'a': ['b', 'c', 'd']}))
unpacker.feed(packb({"a": ["b", "c", "d"]}))
try:
unpacker.read_array_header()
assert 0, 'should raise exception'
assert 0, "should raise exception"
except UnexpectedTypeException:
assert 1, 'okay'
assert 1, "okay"
def test_incorrect_type_nested_map():
unpacker = Unpacker()
unpacker.feed(packb([{'a': 'b'}]))
unpacker.feed(packb([{"a": "b"}]))
try:
unpacker.read_map_header()
assert 0, 'should raise exception'
assert 0, "should raise exception"
except UnexpectedTypeException:
assert 1, 'okay'
assert 1, "okay"

View file

@ -1,14 +1,14 @@
#!/usr/bin/env python
# coding: utf-8
# ruff: noqa: E501
# ignore line length limit for long comments
import io
import msgpack
import msgpack
binarydata = bytes(bytearray(range(256)))
def gen_binary_data(idx):
return binarydata[:idx % 300]
return binarydata[: idx % 300]
def test_exceeding_unpacker_read_size():
@ -18,10 +18,10 @@ def test_exceeding_unpacker_read_size():
NUMBER_OF_STRINGS = 6
read_size = 16
# 5 ok for read_size=16, while 6 glibc detected *** python: double free or corruption (fasttop):
# 20 ok for read_size=256, while 25 segfaults / glibc detected *** python: double free or corruption (!prev)
# 40 ok for read_size=1024, while 50 introduces errors
# 7000 ok for read_size=1024*1024, while 8000 leads to glibc detected *** python: double free or corruption (!prev):
# 5 ok for read_size=16, while 6 glibc detected *** python: double free or corruption (fasttop):
# 20 ok for read_size=256, while 25 segfaults / glibc detected *** python: double free or corruption (!prev)
# 40 ok for read_size=1024, while 50 introduces errors
# 7000 ok for read_size=1024*1024, while 8000 leads to glibc detected *** python: double free or corruption (!prev):
for idx in range(NUMBER_OF_STRINGS):
data = gen_binary_data(idx)
@ -34,7 +34,7 @@ def test_exceeding_unpacker_read_size():
read_count = 0
for idx, o in enumerate(unpacker):
assert type(o) == bytes
assert isinstance(o, bytes)
assert o == gen_binary_data(idx)
read_count += 1

View file

@ -1,117 +1,147 @@
#!/usr/bin/env python
# coding: utf-8
import io
from msgpack import Unpacker, BufferFull
from msgpack import pack
from msgpack.exceptions import OutOfData
from pytest import raises
from msgpack import BufferFull, Unpacker, pack, packb
from msgpack.exceptions import OutOfData
def test_partialdata():
unpacker = Unpacker()
unpacker.feed(b'\xa5')
with raises(StopIteration): next(iter(unpacker))
unpacker.feed(b'h')
with raises(StopIteration): next(iter(unpacker))
unpacker.feed(b'a')
with raises(StopIteration): next(iter(unpacker))
unpacker.feed(b'l')
with raises(StopIteration): next(iter(unpacker))
unpacker.feed(b'l')
with raises(StopIteration): next(iter(unpacker))
unpacker.feed(b'o')
assert next(iter(unpacker)) == b'hallo'
unpacker.feed(b"\xa5")
with raises(StopIteration):
next(iter(unpacker))
unpacker.feed(b"h")
with raises(StopIteration):
next(iter(unpacker))
unpacker.feed(b"a")
with raises(StopIteration):
next(iter(unpacker))
unpacker.feed(b"l")
with raises(StopIteration):
next(iter(unpacker))
unpacker.feed(b"l")
with raises(StopIteration):
next(iter(unpacker))
unpacker.feed(b"o")
assert next(iter(unpacker)) == "hallo"
def test_foobar():
unpacker = Unpacker(read_size=3, use_list=1)
unpacker.feed(b'foobar')
assert unpacker.unpack() == ord(b'f')
assert unpacker.unpack() == ord(b'o')
assert unpacker.unpack() == ord(b'o')
assert unpacker.unpack() == ord(b'b')
assert unpacker.unpack() == ord(b'a')
assert unpacker.unpack() == ord(b'r')
unpacker.feed(b"foobar")
assert unpacker.unpack() == ord(b"f")
assert unpacker.unpack() == ord(b"o")
assert unpacker.unpack() == ord(b"o")
assert unpacker.unpack() == ord(b"b")
assert unpacker.unpack() == ord(b"a")
assert unpacker.unpack() == ord(b"r")
with raises(OutOfData):
unpacker.unpack()
unpacker.feed(b'foo')
unpacker.feed(b'bar')
unpacker.feed(b"foo")
unpacker.feed(b"bar")
k = 0
for o, e in zip(unpacker, 'foobarbaz'):
for o, e in zip(unpacker, "foobarbaz"):
assert o == ord(e)
k += 1
assert k == len(b'foobar')
assert k == len(b"foobar")
def test_foobar_skip():
unpacker = Unpacker(read_size=3, use_list=1)
unpacker.feed(b'foobar')
assert unpacker.unpack() == ord(b'f')
unpacker.feed(b"foobar")
assert unpacker.unpack() == ord(b"f")
unpacker.skip()
assert unpacker.unpack() == ord(b'o')
assert unpacker.unpack() == ord(b"o")
unpacker.skip()
assert unpacker.unpack() == ord(b'a')
assert unpacker.unpack() == ord(b"a")
unpacker.skip()
with raises(OutOfData):
unpacker.unpack()
def test_maxbuffersize():
with raises(ValueError):
Unpacker(read_size=5, max_buffer_size=3)
unpacker = Unpacker(read_size=3, max_buffer_size=3, use_list=1)
unpacker.feed(b'fo')
unpacker.feed(b"fo")
with raises(BufferFull):
unpacker.feed(b'ob')
unpacker.feed(b'o')
assert ord('f') == next(unpacker)
unpacker.feed(b'b')
assert ord('o') == next(unpacker)
assert ord('o') == next(unpacker)
assert ord('b') == next(unpacker)
unpacker.feed(b"ob")
unpacker.feed(b"o")
assert ord("f") == next(unpacker)
unpacker.feed(b"b")
assert ord("o") == next(unpacker)
assert ord("o") == next(unpacker)
assert ord("b") == next(unpacker)
def test_maxbuffersize_file():
buff = io.BytesIO(packb(b"a" * 10) + packb([b"a" * 20] * 2))
unpacker = Unpacker(buff, read_size=1, max_buffer_size=19, max_bin_len=20)
assert unpacker.unpack() == b"a" * 10
# assert unpacker.unpack() == [b"a" * 20]*2
with raises(BufferFull):
print(unpacker.unpack())
def test_readbytes():
unpacker = Unpacker(read_size=3)
unpacker.feed(b'foobar')
assert unpacker.unpack() == ord(b'f')
assert unpacker.read_bytes(3) == b'oob'
assert unpacker.unpack() == ord(b'a')
assert unpacker.unpack() == ord(b'r')
unpacker.feed(b"foobar")
assert unpacker.unpack() == ord(b"f")
assert unpacker.read_bytes(3) == b"oob"
assert unpacker.unpack() == ord(b"a")
assert unpacker.unpack() == ord(b"r")
# Test buffer refill
unpacker = Unpacker(io.BytesIO(b'foobar'), read_size=3)
assert unpacker.unpack() == ord(b'f')
assert unpacker.read_bytes(3) == b'oob'
assert unpacker.unpack() == ord(b'a')
assert unpacker.unpack() == ord(b'r')
unpacker = Unpacker(io.BytesIO(b"foobar"), read_size=3)
assert unpacker.unpack() == ord(b"f")
assert unpacker.read_bytes(3) == b"oob"
assert unpacker.unpack() == ord(b"a")
assert unpacker.unpack() == ord(b"r")
# Issue 352
u = Unpacker()
u.feed(b"x")
assert bytes(u.read_bytes(1)) == b"x"
with raises(StopIteration):
next(u)
u.feed(b"\1")
assert next(u) == 1
def test_issue124():
unpacker = Unpacker()
unpacker.feed(b'\xa1?\xa1!')
assert tuple(unpacker) == (b'?', b'!')
unpacker.feed(b"\xa1?\xa1!")
assert tuple(unpacker) == ("?", "!")
assert tuple(unpacker) == ()
unpacker.feed(b"\xa1?\xa1")
assert tuple(unpacker) == (b'?',)
assert tuple(unpacker) == ("?",)
assert tuple(unpacker) == ()
unpacker.feed(b"!")
assert tuple(unpacker) == (b'!',)
assert tuple(unpacker) == ("!",)
assert tuple(unpacker) == ()
def test_unpack_tell():
stream = io.BytesIO()
messages = [2**i-1 for i in range(65)]
messages = [2**i - 1 for i in range(65)]
messages += [-(2**i) for i in range(1, 64)]
messages += [b'hello', b'hello'*1000, list(range(20)),
{i: bytes(i)*i for i in range(10)},
{i: bytes(i)*i for i in range(32)}]
messages += [
b"hello",
b"hello" * 1000,
list(range(20)),
{i: bytes(i) * i for i in range(10)},
{i: bytes(i) * i for i in range(32)},
]
offsets = []
for m in messages:
pack(m, stream)
offsets.append(stream.tell())
stream.seek(0)
unpacker = Unpacker(stream)
unpacker = Unpacker(stream, strict_map_key=False)
for m, o in zip(messages, offsets):
m2 = next(unpacker)
assert m == m2

View file

@ -1,62 +1,59 @@
# coding: utf-8
from collections import namedtuple
from msgpack import packb, unpackb, ExtType
from msgpack import ExtType, packb, unpackb
def test_namedtuple():
T = namedtuple('T', "foo bar")
T = namedtuple("T", "foo bar")
def default(o):
if isinstance(o, T):
return dict(o._asdict())
raise TypeError('Unsupported type %s' % (type(o),))
raise TypeError(f"Unsupported type {type(o)}")
packed = packb(T(1, 42), strict_types=True, use_bin_type=True, default=default)
unpacked = unpackb(packed, encoding='utf-8')
assert unpacked == {'foo': 1, 'bar': 42}
unpacked = unpackb(packed, raw=False)
assert unpacked == {"foo": 1, "bar": 42}
def test_tuple():
t = ('one', 2, b'three', (4, ))
t = ("one", 2, b"three", (4,))
def default(o):
if isinstance(o, tuple):
return {
'__type__': 'tuple',
'value': list(o),
}
raise TypeError('Unsupported type %s' % (type(o),))
return {"__type__": "tuple", "value": list(o)}
raise TypeError(f"Unsupported type {type(o)}")
def convert(o):
if o.get('__type__') == 'tuple':
return tuple(o['value'])
if o.get("__type__") == "tuple":
return tuple(o["value"])
return o
data = packb(t, strict_types=True, use_bin_type=True, default=default)
expected = unpackb(data, encoding='utf-8', object_hook=convert)
expected = unpackb(data, raw=False, object_hook=convert)
assert expected == t
def test_tuple_ext():
t = ('one', 2, b'three', (4, ))
t = ("one", 2, b"three", (4,))
MSGPACK_EXT_TYPE_TUPLE = 0
def default(o):
if isinstance(o, tuple):
# Convert to list and pack
payload = packb(
list(o), strict_types=True, use_bin_type=True, default=default)
payload = packb(list(o), strict_types=True, use_bin_type=True, default=default)
return ExtType(MSGPACK_EXT_TYPE_TUPLE, payload)
raise TypeError(repr(o))
def convert(code, payload):
if code == MSGPACK_EXT_TYPE_TUPLE:
# Unpack and convert to tuple
return tuple(unpackb(payload, encoding='utf-8', ext_hook=convert))
raise ValueError('Unknown Ext code {}'.format(code))
return tuple(unpackb(payload, raw=False, ext_hook=convert))
raise ValueError(f"Unknown Ext code {code}")
data = packb(t, strict_types=True, use_bin_type=True, default=default)
expected = unpackb(data, encoding='utf-8', ext_hook=convert)
expected = unpackb(data, raw=False, ext_hook=convert)
assert expected == t

View file

@ -1,19 +1,24 @@
#!/usr/bin/env python
# coding: utf-8
from msgpack import packb, unpackb
from collections import namedtuple
from msgpack import packb
class MyList(list):
pass
class MyDict(dict):
pass
class MyTuple(tuple):
pass
MyNamedTuple = namedtuple('MyNamedTuple', 'x y')
MyNamedTuple = namedtuple("MyNamedTuple", "x y")
def test_types():
assert packb(MyDict()) == packb(dict())

171
test/test_timestamp.py Normal file
View file

@ -0,0 +1,171 @@
import datetime
import pytest
import msgpack
from msgpack.ext import Timestamp
def test_timestamp():
# timestamp32
ts = Timestamp(2**32 - 1)
assert ts.to_bytes() == b"\xff\xff\xff\xff"
packed = msgpack.packb(ts)
assert packed == b"\xd6\xff" + ts.to_bytes()
unpacked = msgpack.unpackb(packed)
assert ts == unpacked
assert ts.seconds == 2**32 - 1 and ts.nanoseconds == 0
# timestamp64
ts = Timestamp(2**34 - 1, 999999999)
assert ts.to_bytes() == b"\xee\x6b\x27\xff\xff\xff\xff\xff"
packed = msgpack.packb(ts)
assert packed == b"\xd7\xff" + ts.to_bytes()
unpacked = msgpack.unpackb(packed)
assert ts == unpacked
assert ts.seconds == 2**34 - 1 and ts.nanoseconds == 999999999
# timestamp96
ts = Timestamp(2**63 - 1, 999999999)
assert ts.to_bytes() == b"\x3b\x9a\xc9\xff\x7f\xff\xff\xff\xff\xff\xff\xff"
packed = msgpack.packb(ts)
assert packed == b"\xc7\x0c\xff" + ts.to_bytes()
unpacked = msgpack.unpackb(packed)
assert ts == unpacked
assert ts.seconds == 2**63 - 1 and ts.nanoseconds == 999999999
# negative fractional
ts = Timestamp.from_unix(-2.3) # s: -3, ns: 700000000
assert ts.seconds == -3 and ts.nanoseconds == 700000000
assert ts.to_bytes() == b"\x29\xb9\x27\x00\xff\xff\xff\xff\xff\xff\xff\xfd"
packed = msgpack.packb(ts)
assert packed == b"\xc7\x0c\xff" + ts.to_bytes()
unpacked = msgpack.unpackb(packed)
assert ts == unpacked
def test_unpack_timestamp():
# timestamp 32
assert msgpack.unpackb(b"\xd6\xff\x00\x00\x00\x00") == Timestamp(0)
# timestamp 64
assert msgpack.unpackb(b"\xd7\xff" + b"\x00" * 8) == Timestamp(0)
with pytest.raises(ValueError):
msgpack.unpackb(b"\xd7\xff" + b"\xff" * 8)
# timestamp 96
assert msgpack.unpackb(b"\xc7\x0c\xff" + b"\x00" * 12) == Timestamp(0)
with pytest.raises(ValueError):
msgpack.unpackb(b"\xc7\x0c\xff" + b"\xff" * 12) == Timestamp(0)
# Undefined
with pytest.raises(ValueError):
msgpack.unpackb(b"\xd4\xff\x00") # fixext 1
with pytest.raises(ValueError):
msgpack.unpackb(b"\xd5\xff\x00\x00") # fixext 2
with pytest.raises(ValueError):
msgpack.unpackb(b"\xc7\x00\xff") # ext8 (len=0)
with pytest.raises(ValueError):
msgpack.unpackb(b"\xc7\x03\xff\0\0\0") # ext8 (len=3)
with pytest.raises(ValueError):
msgpack.unpackb(b"\xc7\x05\xff\0\0\0\0\0") # ext8 (len=5)
def test_timestamp_from():
t = Timestamp(42, 14000)
assert Timestamp.from_unix(42.000014) == t
assert Timestamp.from_unix_nano(42000014000) == t
def test_timestamp_to():
t = Timestamp(42, 14000)
assert t.to_unix() == 42.000014
assert t.to_unix_nano() == 42000014000
def test_timestamp_datetime():
t = Timestamp(42, 14)
utc = datetime.timezone.utc
assert t.to_datetime() == datetime.datetime(1970, 1, 1, 0, 0, 42, 0, tzinfo=utc)
ts = datetime.datetime(2024, 4, 16, 8, 43, 9, 420317, tzinfo=utc)
ts2 = datetime.datetime(2024, 4, 16, 8, 43, 9, 420318, tzinfo=utc)
assert (
Timestamp.from_datetime(ts2).nanoseconds - Timestamp.from_datetime(ts).nanoseconds == 1000
)
ts3 = datetime.datetime(2024, 4, 16, 8, 43, 9, 4256)
ts4 = datetime.datetime(2024, 4, 16, 8, 43, 9, 4257)
assert (
Timestamp.from_datetime(ts4).nanoseconds - Timestamp.from_datetime(ts3).nanoseconds == 1000
)
assert Timestamp.from_datetime(ts).to_datetime() == ts
def test_unpack_datetime():
t = Timestamp(42, 14)
utc = datetime.timezone.utc
packed = msgpack.packb(t)
unpacked = msgpack.unpackb(packed, timestamp=3)
assert unpacked == datetime.datetime(1970, 1, 1, 0, 0, 42, 0, tzinfo=utc)
def test_pack_unpack_before_epoch():
utc = datetime.timezone.utc
t_in = datetime.datetime(1960, 1, 1, tzinfo=utc)
packed = msgpack.packb(t_in, datetime=True)
unpacked = msgpack.unpackb(packed, timestamp=3)
assert unpacked == t_in
def test_pack_datetime():
t = Timestamp(42, 14000)
dt = t.to_datetime()
utc = datetime.timezone.utc
assert dt == datetime.datetime(1970, 1, 1, 0, 0, 42, 14, tzinfo=utc)
packed = msgpack.packb(dt, datetime=True)
packed2 = msgpack.packb(t)
assert packed == packed2
unpacked = msgpack.unpackb(packed)
print(packed, unpacked)
assert unpacked == t
unpacked = msgpack.unpackb(packed, timestamp=3)
assert unpacked == dt
x = []
packed = msgpack.packb(dt, datetime=False, default=x.append)
assert x
assert x[0] == dt
assert msgpack.unpackb(packed) is None
def test_issue451():
# https://github.com/msgpack/msgpack-python/issues/451
utc = datetime.timezone.utc
dt = datetime.datetime(2100, 1, 1, 1, 1, tzinfo=utc)
packed = msgpack.packb(dt, datetime=True)
assert packed == b"\xd6\xff\xf4\x86eL"
unpacked = msgpack.unpackb(packed, timestamp=3)
assert dt == unpacked
def test_pack_datetime_without_tzinfo():
dt = datetime.datetime(1970, 1, 1, 0, 0, 42, 14)
with pytest.raises(ValueError, match="where tzinfo=None"):
packed = msgpack.packb(dt, datetime=True)
dt = datetime.datetime(1970, 1, 1, 0, 0, 42, 14)
packed = msgpack.packb(dt, datetime=True, default=lambda x: None)
assert packed == msgpack.packb(None)
utc = datetime.timezone.utc
dt = datetime.datetime(1970, 1, 1, 0, 0, 42, 14, tzinfo=utc)
packed = msgpack.packb(dt, datetime=True)
unpacked = msgpack.unpackb(packed, timestamp=3)
assert unpacked == dt

View file

@ -1,11 +1,13 @@
from io import BytesIO
import sys
from msgpack import Unpacker, packb, OutOfData, ExtType
from pytest import raises, mark
from io import BytesIO
from pytest import mark, raises
from msgpack import ExtType, OutOfData, Unpacker, packb
def test_unpack_array_header_from_file():
f = BytesIO(packb([1,2,3,4]))
f = BytesIO(packb([1, 2, 3, 4]))
unpacker = Unpacker(f)
assert unpacker.read_array_header() == 4
assert unpacker.unpack() == 1
@ -16,8 +18,10 @@ def test_unpack_array_header_from_file():
unpacker.unpack()
@mark.skipif("not hasattr(sys, 'getrefcount') == True",
reason='sys.getrefcount() is needed to pass this test')
@mark.skipif(
"not hasattr(sys, 'getrefcount') == True",
reason="sys.getrefcount() is needed to pass this test",
)
def test_unpacker_hook_refcnt():
result = []
@ -43,12 +47,9 @@ def test_unpacker_hook_refcnt():
def test_unpacker_ext_hook():
class MyUnpacker(Unpacker):
def __init__(self):
super(MyUnpacker, self).__init__(ext_hook=self._hook,
encoding='utf-8')
super().__init__(ext_hook=self._hook, raw=False)
def _hook(self, code, data):
if code == 1:
@ -57,15 +58,32 @@ def test_unpacker_ext_hook():
return ExtType(code, data)
unpacker = MyUnpacker()
unpacker.feed(packb({'a': 1}, encoding='utf-8'))
assert unpacker.unpack() == {'a': 1}
unpacker.feed(packb({'a': ExtType(1, b'123')}, encoding='utf-8'))
assert unpacker.unpack() == {'a': 123}
unpacker.feed(packb({'a': ExtType(2, b'321')}, encoding='utf-8'))
assert unpacker.unpack() == {'a': ExtType(2, b'321')}
unpacker.feed(packb({"a": 1}))
assert unpacker.unpack() == {"a": 1}
unpacker.feed(packb({"a": ExtType(1, b"123")}))
assert unpacker.unpack() == {"a": 123}
unpacker.feed(packb({"a": ExtType(2, b"321")}))
assert unpacker.unpack() == {"a": ExtType(2, b"321")}
if __name__ == '__main__':
test_unpack_array_header_from_file()
test_unpacker_hook_refcnt()
test_unpacker_ext_hook()
def test_unpacker_tell():
objects = 1, 2, "abc", "def", "ghi"
packed = b"\x01\x02\xa3abc\xa3def\xa3ghi"
positions = 1, 2, 6, 10, 14
unpacker = Unpacker(BytesIO(packed))
for obj, unp, pos in zip(objects, unpacker, positions):
assert obj == unp
assert pos == unpacker.tell()
def test_unpacker_tell_read_bytes():
objects = 1, "abc", "ghi"
packed = b"\x01\x02\xa3abc\xa3def\xa3ghi"
raw_data = b"\x02", b"\xa3def", b""
lenghts = 1, 4, 999
positions = 1, 6, 14
unpacker = Unpacker(BytesIO(packed))
for obj, unp, pos, n, raw in zip(objects, unpacker, positions, lenghts, raw_data):
assert obj == unp
assert pos == unpacker.tell()
assert unpacker.read_bytes(n) == raw

View file

@ -1,29 +0,0 @@
"""Tests for cases where the user seeks to obtain packed msgpack objects"""
import io
from msgpack import Unpacker, packb
def test_write_bytes():
unpacker = Unpacker()
unpacker.feed(b'abc')
f = io.BytesIO()
assert unpacker.unpack(f.write) == ord('a')
assert f.getvalue() == b'a'
f = io.BytesIO()
assert unpacker.skip(f.write) is None
assert f.getvalue() == b'b'
f = io.BytesIO()
assert unpacker.skip() is None
assert f.getvalue() == b''
def test_write_bytes_multi_buffer():
long_val = (5) * 100
expected = packb(long_val)
unpacker = Unpacker(io.BytesIO(expected), read_size=3, max_buffer_size=3)
f = io.BytesIO()
unpacked = unpacker.unpack(f.write)
assert unpacked == long_val
assert f.getvalue() == expected

38
tox.ini
View file

@ -1,38 +0,0 @@
[tox]
envlist = {py27,py35,py36}-{c,pure},{pypy,pypy3}-pure,py27-x86,py34-x86
[variants:pure]
setenv=
MSGPACK_PUREPYTHON=x
[testenv]
deps=
pytest
changedir=test
commands=
c,x86: python -c 'from msgpack import _packer, _unpacker'
c,x86: py.test
pure: py.test
[testenv:py27-x86]
basepython=python2.7-x86
deps=
pytest
changedir=test
commands=
python -c 'import sys; print(hex(sys.maxsize))'
python -c 'from msgpack import _packer, _unpacker'
py.test
[testenv:py34-x86]
basepython=python3.4-x86
deps=
pytest
changedir=test
commands=
python -c 'import sys; print(hex(sys.maxsize))'
python -c 'from msgpack import _packer, _unpacker'
py.test