Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
88 commits
Select commit Hold shift + click to select a range
c26f06e
Add __init__.pxd
Vizonex Jun 17, 2025
bac1e1d
Create capsule.h
Vizonex Jun 17, 2025
2f43fb6
Update _multidict.c
Vizonex Jun 17, 2025
3ddfc53
Update __init__.py
Vizonex Jun 17, 2025
35b7406
forgot to put get_include in `__all__`
Vizonex Jun 17, 2025
86e55a7
Update __init__.py
Vizonex Jun 17, 2025
7461cf7
Create 1178.feature.rst
Vizonex Jun 17, 2025
8fa2b7f
run autopep8
Vizonex Jun 18, 2025
b44fee9
Update __init__.py
Vizonex Jun 18, 2025
cc8ed9f
run black
Vizonex Jun 18, 2025
dd09781
fix linting
Vizonex Jun 18, 2025
2e64455
add cython
Vizonex Jun 18, 2025
6af7486
Merge branch 'aio-libs:master' into Cython-Compatability
Vizonex Jun 18, 2025
24dba8c
Update CHANGES/1178.feature.rst
Vizonex Jun 18, 2025
aa40975
Update multidict/__init__.pxd
Vizonex Jun 18, 2025
5b92228
Move pathlib to the top instead of being inside the get_include func.
Vizonex Jun 18, 2025
036d674
thank you for catching my typo webknjaz :)
Vizonex Jun 18, 2025
387cf48
sneaky human mistake, this is not supposed to be a vectorcall
Vizonex Jun 18, 2025
e4757c5
revert for now so that typechecking doesn't degrade performance I'll …
Vizonex Jun 18, 2025
5ce8633
These are supposed to be CIMultiDict objects
Vizonex Jun 18, 2025
267899e
Lets organize these a little bit so that things don't get out of hand
Vizonex Jun 18, 2025
43171cf
Not sure who had the idea of using a type here, it's unused so I thin…
Vizonex Jun 18, 2025
fc358b4
Add the rest of the macro definitions only one left is IStr now
Vizonex Jun 18, 2025
dca5a77
make get_include() PEP-0257 Compliant
Vizonex Jun 18, 2025
c8df470
add istr to cython
Vizonex Jun 18, 2025
3200962
Add istr with an IStr_FromUnicode Method to be a fast unicode converter
Vizonex Jun 18, 2025
e7c12dd
fix whitespace problem
Vizonex Jun 18, 2025
9a8e0a6
run isort
Vizonex Jun 18, 2025
6888d9c
Add multidict/multilib header files to source distribution
Vizonex Jun 18, 2025
7852607
Add the rest of the functions we need to send to cython we should be …
Vizonex Jun 18, 2025
d380215
add the cherry on top
Vizonex Jun 18, 2025
6d697c8
CIMultidicts can be binded via fused keywords
Vizonex Jun 18, 2025
726b325
make Copy Functions return fused types
Vizonex Jun 18, 2025
e96eace
Fused types didn't work & add cython test stuff and compile tool
Vizonex Jun 18, 2025
c9fc85c
Lesson Learned: you have to give the ull name of the Capsule or else…
Vizonex Jun 19, 2025
e4abb6e
instead of reverting this let's optimize this...
Vizonex Jun 19, 2025
a9d760f
that will be all I do for today, more tomorrow
Vizonex Jun 19, 2025
8ee6698
let me make sure these were updated correclty
Vizonex Jun 19, 2025
3e777e1
run isort on stubfile
Vizonex Jun 19, 2025
e7711d2
Update multidict/_multilib/capsule.h
Vizonex Jun 19, 2025
9607da7
Update multidict/__init__.pxd
Vizonex Jun 19, 2025
9443cf0
Update multidict/_multilib/capsule.h
Vizonex Jun 19, 2025
5e1911f
Update multidict/_multilib/capsule.h
Vizonex Jun 19, 2025
a71dd22
Update multidict/__init__.pxd
Vizonex Jun 19, 2025
fa9a01a
Update multidict/__init__.pxd
Vizonex Jun 19, 2025
f4f5f83
Update multidict/__init__.pxd
Vizonex Jun 19, 2025
493cafd
Update multidict/__init__.pxd
Vizonex Jun 19, 2025
608b709
add cython-test to makefile for workflow-related tests
Vizonex Jun 19, 2025
37d82bc
add wrapper to prevent issues with non-c-extension-tests
Vizonex Jun 19, 2025
8af77cd
set reason
Vizonex Jun 19, 2025
cd35076
IStr_FromUnicode did not go as planned
Vizonex Jun 19, 2025
6df3f53
istr tests now covered
Vizonex Jun 19, 2025
3ec2fda
ran mypy on test_cython_import and added new istr functions in cython
Vizonex Jun 19, 2025
cf1a58a
Branch conflicts were screwing with this pr I will fix shortly
Vizonex Jun 19, 2025
a455930
Merge branch 'master' into Cython-Compatability
Vizonex Jun 19, 2025
0cf3a3e
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jun 19, 2025
e1775f6
thats better
Vizonex Jun 19, 2025
d0db07e
Merge branch 'Cython-Compatability' of https://github.com/Vizonex/mul…
Vizonex Jun 19, 2025
637f874
final changes for tests unless someone says otherwise
Vizonex Jun 19, 2025
c57e34a
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jun 19, 2025
dbc3a82
mypy fixes
Vizonex Jun 19, 2025
1f2b1a1
Merge branch 'Cython-Compatability' of https://github.com/Vizonex/mul…
Vizonex Jun 19, 2025
083a2af
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jun 19, 2025
b13bb5f
reformat so that linter is happy
Vizonex Jun 19, 2025
fceb11e
Merge branch 'Cython-Compatability' of https://github.com/Vizonex/mul…
Vizonex Jun 19, 2025
fdb402e
thanks asvetlov, I downloaded clang-format just to get this passed :)
Vizonex Jun 19, 2025
e6023f4
clang-formatted _multidict.c
Vizonex Jun 19, 2025
8451958
this should fix it
Vizonex Jun 19, 2025
c07080c
update capsule.h
Vizonex Jun 19, 2025
51b0f3f
remember to compile cython test
Vizonex Jun 19, 2025
54351be
fix lint, cython already installs itself if I remember correctly
Vizonex Jun 19, 2025
9886334
Maybe this will work???
Vizonex Jun 19, 2025
a775b83
removing trailing spaces
Vizonex Jun 19, 2025
0652d27
Update ci-cd.yml
Vizonex Jun 19, 2025
c0cc056
Update Makefile
Vizonex Jun 19, 2025
fc3e1be
Update .github/workflows/ci-cd.yml
Vizonex Jun 19, 2025
d426c89
Update .gitignore
Vizonex Jun 19, 2025
dd88a6c
add documentation
Vizonex Jun 20, 2025
fbac986
move cython requirement to ci.txt as suggested
Vizonex Jun 20, 2025
69f7c43
add cython istr api workaround
Vizonex Jun 20, 2025
76b63a2
if --c-extensions is enabled allow workflow to compile it in pre-exec…
Vizonex Jun 20, 2025
16701b6
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jun 20, 2025
c04c2f8
satisfy mypy
Vizonex Jun 20, 2025
2279860
statisfy mypy
Vizonex Jun 20, 2025
78a90cc
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jun 20, 2025
f50697a
better venv check
Vizonex Jun 20, 2025
faa13ac
Merge branch 'Cython-Compatability' of https://github.com/Vizonex/mul…
Vizonex Jun 20, 2025
cd5cbfb
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jun 20, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES/1178.feature.rst

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is where you can see a preview of how this change note is rendered: https://multidict--1178.org.readthedocs.build/en/1178/changes/. Alternatively, if you want to build the docs locally, it'll also include draft/unreleased notes.

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Started exposing an interface for importing the :mod:`multidict` C-extension from downstream Cython libraries -- by :user:`Vizonex`.
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ graft requirements
graft tests
global-exclude *.pyc
include multidict/*.c
include multidict/_multilib/*.h

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This adds the file to sdist. You may have to include it into wheels too.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I will make sure I do so. Thanks for reminding me about wheels. I will get that done after I write some new documentation for using multidict with cython.

exclude multidict/_multidict.html
exclude multidict/*.so
exclude multidict/*.pyd
Expand Down
101 changes: 101 additions & 0 deletions docs/in-cython.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
.. _cython-api:

==========
Cython API
==========

Multidicts implements a cython api that is used for speeding up the aiohttp http parser
but this feature can be used elsewhere in your own projects.


Introduction
------------
Multidict can be used with cython to speedup performance of other tools or
scripts you may think about programming. Those who are familliar with the way
`numpy <https://cython.readthedocs.io/en/latest/src/userguide/numpy_tutorial.html>`_
works should know that this library works the exact same way. If your not familliar with this don't worry.

An example might be combining the node-js `llhttp <https://llhttp.org>`_ library
and Multidict together for example (which is something aiohttp already
does) . By using llhttp's callback functions on items such as HTTP headers and URL
query arguments you can build some extremely fast parsers and more
with extra performance benefits included.


Functions for using MultiDict in cython
should have very simillar feel and format to the way CPython was written. e.g.:

.. code-block:: python

from multidict cimport import_multidict, MultiDict, MultiDict_Add
# always remeber to call import_multidict before anything else
# otherwise your compilation will fail
import_multidict()

cdef MultiDict create_with_user_agent_header():
cdef MultiDict md = MultiDict()
MultiDict_Add(md, "user-agent", "Multidict-Made-User-Agent")
return md






Compiling
---------
Compiling multidict with cython works the exact same way as *numpy* with the only
requirement being to link where the headers needed to compile the library are kept
luckily multidict includes a function to get where the headers are stored called
*get_include* and it is no different from the way `numpy works <https://cython.readthedocs.io/en/latest/src/userguide/numpy_tutorial.html#compilation-using-setuptools>`_.
e.g.:

.. code-block:: python

from Cython.Build import cythonize
from setuptools import Extension, setup
import multidict

if __name__ == "__main__":
setup(
ext_modules=cythonize(
Extension(
"your_module.pyx", sources=["your_module.pyx"]
)
),
# in here is where you could but down your include directories
include_dirs=[multidict.get_include()],
)


Know that your are not limited to just one *include_dirs* directory in fact you could combine
*numpy* and *multidict* together if you really wanted to along with other C Libraries that you would
like to compile alongside it. e.g.:

.. code-block:: python

from setuptools import Extension, setup
from Cython.Build import cythonize
import numpy
import multidict

extensions = [
Extension("*", ["*.pyx"],
include_dirs=[
numpy.get_include(),
multidict.get_include(),
"my-other-clibraries/path/etc"
]
),
]
setup(
name="My hello app",
ext_modules=cythonize(extensions),
)

There's





1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ Contents
multidict
benchmark
changes
in-cython

Indices and tables
==================
Expand Down
247 changes: 247 additions & 0 deletions docs/multidict.rst
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,253 @@ The type of multidict keys is always :class:`str` or a class derived from a stri
.. versionadded:: 3.7


Cython API Reference
====================

This library is also shipped with a Cython API

.. function:: int MultiDict_GetAll(MultiDict self, object key, PyObject **ret) except -1

functions the same way as the :class:`MultiDict` *getall* function but it is binded to the C function directly under the
hood. Returns -1 if the key does not exist, and the return value will be *NULL*. Know that this doesn't raise a *KeyError*
And it is excepted the that the end programmer would take care of that.

e.g::

from cpython.object cimport PyObject

def get_all_of(MultiDict md):
cdef PyObject* ret
# This will raise KeyError if we didn't handle the exception in a different way.
if MultiDict_GetAll(md, "key", &ret) < 0:
raise KeyError("key")


.. function:: int MultiDict_GetOne(MultiDict self, object key, PyObject **ret) except -1

functions the same way as :class:`MultiDict` *getone* function but is binded directly to C.
Returns -1 if Key was not found. This function does not raise *KeyError* and it's excepted
for the end developer to take care of raising that exception.

.. function:: object MultiDict_Keys(MultiDict self)

function works the same as :class:`MultiDict` *keys* function.

e.g::

cdef iterate_md(MultiDict md):
cdef object key
for key in MultiDict_Keys(md):
...


.. function:: object MultiDict_Items(MultiDict self)

function works the same as :class:`MultiDict` *items* function.

e.g::

cdef items_md(MultiDict md):
cdef object key, value
for key, value in MultiDict_Items(md):
...


.. function:: object MultiDict_Values(MultiDict self)

function works the same as :class:`MultiDict` *values* function.

e.g::

cdef iterate_md(MultiDict md):
cdef object value
for value in MultiDict_Values(md):
...

.. function:: int MultiDict_Add(MultiDict self, object key, object value) except -1

function adds a key and value to a :class:`MultiDict` returns -1 on failure

e.g::

if MultiDict_Add(md, "foo", "spam") < 0:
raise RuntimeError("Failed to add (foo, spam)")


.. function:: PyObject* MultiDict_Extend(MultiDict self, tuple args, dict kwargs) except NULL

function extends a multidict with both args or kwargs. Ignoring one or the other should work.
returns a *NULL* pointer if all else fails.

e.g::

if MultiDict_Extend(md, [("a", 1)], {"k": 1}) == NULL:
...


.. function:: MultiDict MultiDict_Copy(MultiDict self)

copies a multidict and returns a new one with the copied items.

.. function:: PyObject* MultiDict_SetDefault(MultiDict self, object key, object value) except NULL

works the same as :class:`MultiDict` *setdefault*, returns `NULL` if something fails.

.. function:: int MultiDict_PopOne(MultiDict self, object key, PyObject** ret) except -1

works the same as :class:`MultiDict` *popone*, returns -1 if something fails.

.. function:: int MultiDict_PopAll(MultiDict self, object key, PyObject** ret) except -1

works the same as :class:`MultiDict` *popall*, returns -1 if something fails.

.. function:: object MultiDict_PopItem(MultiDict self)

works the same as :class:`MultiDict` *popitem*, raises :exc:`KeyError` if dictionary is empty

.. function:: PyObject* MultiDict_Update(MultiDict self, tuple args, dict kwds) except NULL

works the same as :class:`MultiDict` *update*, returns NULL if the function fails to update something

e.g::

MultiDict_Update(md, (), {"key":"value"})


.. function:: int CIMultiDict_GetAll(CIMultiDict self, object key, PyObject **ret) except -1

functions the same way as the :class:`CIMultiDict` *getall* function but it is binded to the C function directly under the
hood. Returns -1 if the key does not exist, and the return value will be *NULL*. Know that this doesn't raise a *KeyError*
And it is excepted the that the end programmer would take care of that.

e.g::

from cpython.object cimport PyObject

def get_all_of(CIMultiDict md):
cdef PyObject* ret
# This will raise KeyError if we didn't handle the exception in a different way.
if CIMultiDict_GetAll(md, "key", &ret) < 0:
raise KeyError("key")


.. function:: int CIMultiDict_GetOne(CIMultiDict self, object key, PyObject **ret) except -1

functions the same way as :class:`CIMultiDict` *getone* function but is binded directly to C.
Returns -1 if Key was not found. This function does not raise *KeyError* and it's excepted
for the end developer to take care of raising that exception.

.. function:: object CIMultiDict_Keys(CIMultiDict self)

function works the same as :class:`CIMultiDict` *keys* function.

e.g::

cdef iterate_md(CIMultiDict md):
cdef object key
for key in CIMultiDict_Keys(md):
...


.. function:: object CIMultiDict_Items(CIMultiDict self)

function works the same as :class:`CIMultiDict` *items* function.

e.g::

cdef items_md(CIMultiDict md):
cdef object key, value
for key, value in CIMultiDict_Items(md):
...


.. function:: object CIMultiDict_Values(CIMultiDict self)

function works the same as :class:`CIMultiDict` *values* function.

e.g::

cdef iterate_md(CIMultiDict md):
cdef object value
for value in CIMultiDict_Values(md):
...

.. function:: int CIMultiDict_Add(CIMultiDict self, object key, object value) except -1

function adds a key and value to a :class:`CIMultiDict` returns -1 on failure

e.g::

if CIMultiDict_Add(md, "foo", "spam") < 0:
raise RuntimeError("Failed to add (foo, spam)")


.. function:: PyObject* CIMultiDict_Extend(CIMultiDict self, tuple args, dict kwargs) except NULL

function extends a CImultidict with both args or kwargs. Ignoring one or the other should work.
returns a *NULL* pointer if all else fails.

e.g::

if CIMultiDict_Extend(md, [("a", 1)], {"k": 1}) == NULL:
...


.. function:: CIMultiDict CIMultiDict_Copy(CIMultiDict self)

copies a CImultidict and returns a new one with the copied items.

.. function:: PyObject* CIMultiDict_SetDefault(CIMultiDict self, object key, object value) except NULL

works the same as :class:`CIMultiDict` *setdefault*, returns `NULL` if something fails.

.. function:: int CIMultiDict_PopOne(CIMultiDict self, object key, PyObject** ret) except -1

works the same as :class:`CIMultiDict` *popone*, returns -1 if something fails.

.. function:: int CIMultiDict_PopAll(CIMultiDict self, object key, PyObject** ret) except -1

works the same as :class:`CIMultiDict` *popall*, returns -1 if something fails.

.. function:: object CIMultiDict_PopItem(CIMultiDict self)

works the same as :class:`CIMultiDict` *popitem*, raises :exc:`KeyError` if dictionary is empty

.. function:: PyObject* CIMultiDict_Update(CIMultiDict self, tuple args, dict kwds) except NULL

works the same as :class:`CIMultiDict` *update*, returns NULL if the function fails to update something

e.g::

CIMultiDict_Update(md, (), {"key":"value"})

.. function:: int MultiDictProxy_GetAll(MultiDictProxy self, object key, PyObject **ret)

todo...

.. function:: int MultiDictProxy_GetOne(MultiDictProxy self, object key, PyObject **ret)

todo...

.. function:: object MultiDictProxy_Keys(MultiDictProxy self)

todo...

.. function:: object MultiDictProxy_Values(MultiDictProxy self)

todo...

.. funciton:: object MultiDictProxy_Items(MultiDictProxy self)

todo...

.. funciton:: MultiDictProxy MultiDictProxy_Copy(MultiDictProxy self)

todo...




Environment variables
=====================

Expand Down
1 change: 1 addition & 0 deletions docs/spelling_wordlist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ multipart
Multipart
mypy
Nikolay
numpy
param
params
performant
Expand Down
Loading
Loading