{"id":15497732,"url":"https://github.com/techouse/qs_codec","last_synced_at":"2026-02-22T12:17:30.873Z","repository":{"id":236163432,"uuid":"792073198","full_name":"techouse/qs_codec","owner":"techouse","description":"A query string encoding and decoding library for Python. Ported from qs for JavaScript.","archived":false,"fork":false,"pushed_at":"2025-04-20T20:16:59.000Z","size":449,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-20T21:24:06.525Z","etag":null,"topics":["python","qs","query-encoding","query-parser","query-string","url-parsing","url-query"],"latest_commit_sha":null,"homepage":"https://techouse.github.io/qs_codec/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/techouse.png","metadata":{"files":{"readme":"README.rst","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":"CODE-OF-CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"techouse","custom":["https://paypal.me/ktusar"]}},"created_at":"2024-04-25T23:29:55.000Z","updated_at":"2025-04-20T20:17:02.000Z","dependencies_parsed_at":"2024-04-25T23:48:33.960Z","dependency_job_id":"4311d733-75cf-46d0-9b0f-ecd7ea9a08d7","html_url":"https://github.com/techouse/qs_codec","commit_stats":{"total_commits":85,"total_committers":2,"mean_commits":42.5,"dds":0.04705882352941182,"last_synced_commit":"d5b23a54460edd1e222a0e501af22062d2760395"},"previous_names":["techouse/qs_codec"],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/techouse%2Fqs_codec","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/techouse%2Fqs_codec/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/techouse%2Fqs_codec/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/techouse%2Fqs_codec/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/techouse","download_url":"https://codeload.github.com/techouse/qs_codec/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249964550,"owners_count":21352729,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["python","qs","query-encoding","query-parser","query-string","url-parsing","url-query"],"created_at":"2024-10-02T08:40:37.367Z","updated_at":"2026-02-22T12:17:30.856Z","avatar_url":"https://github.com/techouse.png","language":"Python","readme":"qs-codec\n========\n\n\n.. image:: https://raw.githubusercontent.com/techouse/qs_codec/main/logo.png\n   :alt: qs-codec\n   :width: 256\n   :align: center\n\nA query string encoding and decoding library for Python.\n\nPorted from `qs \u003chttps://www.npmjs.com/package/qs\u003e`__ for JavaScript.\n\n|PyPI - Version| |PyPI - Downloads| |PyPI - Status| |PyPI - Python Version| |PyPy Support| |PyPI - Format|\n|Test| |CodeQL| |Publish| |Docs| |codecov| |Codacy| |OpenSSF| |Black| |flake8| |mypy| |pylint| |isort| |bandit|\n|License| |Contributor Covenant| |GitHub Sponsors| |GitHub Repo stars|\n\nHighlights\n----------\n\n- Nested dictionaries \u0026 lists: ``foo[bar][baz]=qux`` ⇄ ``{'foo': {'bar': {'baz': 'qux'}}}``.\n- Multiple list formats: INDICES (``a[0]=x``), BRACKETS (``a[]=x``), REPEAT (``a=x\u0026a=y``), COMMA (``a=x,y``) with optional comma round-trip.\n- Dot-notation: parse/encode keys like ``a.b=c`` as nested; option to **encode dots in keys** when using dot notation.\n- Charset handling: UTF-8 (default) and Latin-1; optional **charset sentinel** (``utf8=✓``) to auto-detect encoding.\n- Pluggable hooks: custom ``encoder``/``decoder`` callables; options to sort keys, filter output, and control percent-encoding (keys-only, values-only).\n- Nulls \u0026 empties: ``strict_null_handling`` and ``skip_nulls``; support for empty lists/arrays when desired.\n- Dates: ``serialize_date`` for ISO 8601 or custom (e.g., UNIX timestamp).\n- Safety limits: configurable decode depth and encode max depth, parameter limit, and list index limit; optional strict-depth errors; duplicate-key strategies (combine/first/last).\n- Extras: numeric entity decoding (e.g. ``\u0026#9786;`` → ☺), alternate delimiters/regex, and query-prefix helpers.\n\nCompatibility\n-------------\n\n- CPython 3.8–3.14 (default tox envs).\n- PyPy 3.8–3.11 (run ``tox -e pypy3.8`` through ``tox -e pypy3.11`` locally; CI mirrors this matrix).\n\nUsage\n-----\n\nA simple usage example:\n\n.. code:: python\n\n   import qs_codec as qs\n\n   # Encoding\n   assert qs.encode({'a': 'b'}) == 'a=b'\n\n   # Decoding\n   assert qs.decode('a=b') == {'a': 'b'}\n\nDecoding\n~~~~~~~~\n\ndictionaries\n^^^^^^^^^^^^\n\n`decode \u003chttps://techouse.github.io/qs_codec/qs_codec.html#module-qs_codec.decode\u003e`__ allows you to create nested ``dict``\\ s within your query\nstrings, by surrounding the name of sub-keys with square brackets\n``[]``. For example, the string ``'foo[bar]=baz'`` converts to:\n\n.. code:: python\n\n   import qs_codec as qs\n\n   assert qs.decode('foo[bar]=baz') == {'foo': {'bar': 'baz'}}\n\nURI encoded strings work too:\n\n.. code:: python\n\n   import qs_codec as qs\n\n   assert qs.decode('a%5Bb%5D=c') == {'a': {'b': 'c'}}\n\nYou can also nest your ``dict``\\ s, like ``'foo[bar][baz]=foobarbaz'``:\n\n.. code:: python\n\n   import qs_codec as qs\n\n   assert qs.decode('foo[bar][baz]=foobarbaz') == {'foo': {'bar': {'baz': 'foobarbaz'}}}\n\nBy default, when nesting ``dict``\\ s qs will only decode up to 5\nchildren deep. This means if you attempt to decode a string like\n``'a[b][c][d][e][f][g][h][i]=j'`` your resulting ``dict`` will be:\n\n.. code:: python\n\n   import qs_codec as qs\n\n   assert qs.decode(\"a[b][c][d][e][f][g][h][i]=j\") == {\n       \"a\": {\"b\": {\"c\": {\"d\": {\"e\": {\"f\": {\"[g][h][i]\": \"j\"}}}}}}\n   }\n\nThis depth can be overridden by setting the `depth \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.models.decode_options.DecodeOptions.depth\u003e`_:\n\n.. code:: python\n\n   import qs_codec as qs\n\n   assert qs.decode(\n       'a[b][c][d][e][f][g][h][i]=j',\n       qs.DecodeOptions(depth=1),\n   ) == {'a': {'b': {'[c][d][e][f][g][h][i]': 'j'}}}\n\nYou can configure `decode \u003chttps://techouse.github.io/qs_codec/qs_codec.html#module-qs_codec.decode\u003e`__ to throw an error\nwhen parsing nested input beyond this depth using `strict_depth \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.models.decode_options.DecodeOptions.strict_depth\u003e`__ (defaults to ``False``):\n\n.. code:: python\n\n   import qs_codec as qs\n\n   try:\n       qs.decode(\n           'a[b][c][d][e][f][g][h][i]=j',\n           qs.DecodeOptions(depth=1, strict_depth=True),\n       )\n   except IndexError as e:\n       assert str(e) == 'Input depth exceeded depth option of 1 and strict_depth is True'\n\nThe depth limit helps mitigate abuse when `decode \u003chttps://techouse.github.io/qs_codec/qs_codec.html#module-qs_codec.decode\u003e`__ is used to parse user\ninput, and it is recommended to keep it a reasonably small number. `strict_depth \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.models.decode_options.DecodeOptions.strict_depth\u003e`__\nadds a layer of protection by throwing an ``IndexError`` when the limit is exceeded, allowing you to catch and handle such cases.\n\nFor similar reasons, by default `decode \u003chttps://techouse.github.io/qs_codec/qs_codec.html#module-qs_codec.decode\u003e`__ will only parse up to 1000 parameters. This can be overridden by passing a\n`parameter_limit \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.models.decode_options.DecodeOptions.parameter_limit\u003e`__ option:\n\n.. code:: python\n\n   import qs_codec as qs\n\n   assert qs.decode(\n       'a=b\u0026c=d',\n       qs.DecodeOptions(parameter_limit=1),\n   ) == {'a': 'b'}\n\nTo bypass the leading question mark, use `ignore_query_prefix \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.models.decode_options.DecodeOptions.ignore_query_prefix\u003e`__:\n\n.. code:: python\n\n   import qs_codec as qs\n\n   assert qs.decode(\n       '?a=b\u0026c=d',\n       qs.DecodeOptions(ignore_query_prefix=True),\n   ) == {'a': 'b', 'c': 'd'}\n\nAn optional `delimiter \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.models.decode_options.DecodeOptions.delimiter\u003e`__ can also be passed:\n\n.. code:: python\n\n   import qs_codec as qs\n\n   assert qs.decode(\n       'a=b;c=d',\n       qs.DecodeOptions(delimiter=';'),\n   ) == {'a': 'b', 'c': 'd'}\n\n`delimiter \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.models.decode_options.DecodeOptions.delimiter\u003e`__ can be a regular expression too:\n\n.. code:: python\n\n   import qs_codec as qs\n   import re\n\n   assert qs.decode(\n       'a=b;c=d',\n       qs.DecodeOptions(delimiter=re.compile(r'[;,]')),\n   ) == {'a': 'b', 'c': 'd'}\n\nOption `allow_dots \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.models.decode_options.DecodeOptions.allow_dots\u003e`__\ncan be used to enable dot notation:\n\n.. code:: python\n\n   import qs_codec as qs\n\n   assert qs.decode(\n       'a.b=c',\n       qs.DecodeOptions(allow_dots=True),\n   ) == {'a': {'b': 'c'}}\n\nOption `decode_dot_in_keys \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.models.decode_options.DecodeOptions.decode_dot_in_keys\u003e`__\ncan be used to decode dots in keys.\n\n**Note:** it implies `allow_dots \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.models.decode_options.DecodeOptions.allow_dots\u003e`__, so\n`decode \u003chttps://techouse.github.io/qs_codec/qs_codec.html#module-qs_codec.decode\u003e`__ will error if you set `decode_dot_in_keys \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.models.decode_options.DecodeOptions.decode_dot_in_keys\u003e`__\nto ``True``, and `allow_dots \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.models.decode_options.DecodeOptions.allow_dots\u003e`__ to ``False``.\n\n.. code:: python\n\n   import qs_codec as qs\n\n   assert qs.decode(\n       'name%252Eobj.first=John\u0026name%252Eobj.last=Doe',\n       qs.DecodeOptions(decode_dot_in_keys=True),\n   ) == {'name.obj': {'first': 'John', 'last': 'Doe'}}\n\nOption `allow_empty_lists \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.models.decode_options.DecodeOptions.allow_empty_lists\u003e`__ can\nbe used to allowing empty ``list`` values in a ``dict``\n\n.. code:: python\n\n   import qs_codec as qs\n\n   assert qs.decode(\n       'foo[]\u0026bar=baz',\n       qs.DecodeOptions(allow_empty_lists=True),\n   ) == {'foo': [], 'bar': 'baz'}\n\nOption `duplicates \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.models.decode_options.DecodeOptions.duplicates\u003e`__ can be used to\nchange the behavior when duplicate keys are encountered\n\n.. code:: python\n\n   import qs_codec as qs\n\n   assert qs.decode('foo=bar\u0026foo=baz') == {'foo': ['bar', 'baz']}\n\n   assert qs.decode(\n       'foo=bar\u0026foo=baz',\n       qs.DecodeOptions(duplicates=qs.Duplicates.COMBINE),\n   ) == {'foo': ['bar', 'baz']}\n\n   assert qs.decode(\n       'foo=bar\u0026foo=baz',\n       qs.DecodeOptions(duplicates=qs.Duplicates.FIRST),\n   ) == {'foo': 'bar'}\n\n   assert qs.decode(\n       'foo=bar\u0026foo=baz',\n       qs.DecodeOptions(duplicates=qs.Duplicates.LAST),\n   ) == {'foo': 'baz'}\n\nIf you have to deal with legacy browsers or services, there’s also\nsupport for decoding percent-encoded octets as `LATIN1 \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.enums.charset.Charset.LATIN1\u003e`__:\n\n.. code:: python\n\n   import qs_codec as qs\n\n   assert qs.decode(\n       'a=%A7',\n       qs.DecodeOptions(charset=qs.Charset.LATIN1),\n   ) == {'a': '§'}\n\nSome services add an initial ``utf8=✓`` value to forms so that old\nInternet Explorer versions are more likely to submit the form as utf-8.\nAdditionally, the server can check the value against wrong encodings of\nthe checkmark character and detect that a query string or\n``application/x-www-form-urlencoded`` body was *not* sent as ``utf-8``,\ne.g. if the form had an ``accept-charset`` parameter or the containing\npage had a different character set.\n\n`decode \u003chttps://techouse.github.io/qs_codec/qs_codec.html#module-qs_codec.decode\u003e`__ supports this mechanism via the\n`charset_sentinel \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.models.decode_options.DecodeOptions.charset_sentinel\u003e`__ option.\nIf specified, the ``utf8`` parameter will be omitted from the returned\n``dict``. It will be used to switch to `LATIN1 \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.enums.charset.Charset.LATIN1\u003e`__ or\n`UTF8 \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.enums.charset.Charset.UTF8\u003e`__ mode depending on how the checkmark is encoded.\n\n**Important**: When you specify both the `charset \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.models.decode_options.DecodeOptions.charset\u003e`__\noption and the `charset_sentinel \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.models.decode_options.DecodeOptions.charset_sentinel\u003e`__ option, the\n`charset \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.models.decode_options.DecodeOptions.charset\u003e`__ will be overridden when the request contains a\n``utf8`` parameter from which the actual charset can be deduced. In that\nsense the `charset \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.models.decode_options.DecodeOptions.charset\u003e`__ will behave as the default charset\nrather than the authoritative charset.\n\n.. code:: python\n\n   import qs_codec as qs\n\n   assert qs.decode(\n       'utf8=%E2%9C%93\u0026a=%C3%B8',\n       qs.DecodeOptions(\n           charset=qs.Charset.LATIN1,\n           charset_sentinel=True,\n       ),\n   ) == {'a': 'ø'}\n\n   assert qs.decode(\n       'utf8=%26%2310003%3B\u0026a=%F8',\n       qs.DecodeOptions(\n           charset=qs.Charset.UTF8,\n           charset_sentinel=True,\n       ),\n   ) == {'a': 'ø'}\n\nIf you want to decode the `\u0026#...; \u003chttps://www.w3schools.com/html/html_entities.asp\u003e`__ syntax to the actual character, you can specify the\n`interpret_numeric_entities \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.models.decode_options.DecodeOptions.interpret_numeric_entities\u003e`__\noption as well:\n\n.. code:: python\n\n   import qs_codec as qs\n\n   assert qs.decode(\n       'a=%26%239786%3B',\n       qs.DecodeOptions(\n           charset=qs.Charset.LATIN1,\n           interpret_numeric_entities=True,\n       ),\n   ) == {'a': '☺'}\n\nIt also works when the charset has been detected in\n`charset_sentinel \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.models.decode_options.DecodeOptions.charset_sentinel\u003e`__ mode.\n\nlists\n^^^^^\n\n`decode \u003chttps://techouse.github.io/qs_codec/qs_codec.html#module-qs_codec.decode\u003e`__ can also decode ``list``\\ s using a similar ``[]`` notation:\n\n.. code:: python\n\n   import qs_codec as qs\n\n   assert qs.decode('a[]=b\u0026a[]=c') == {'a': ['b', 'c']}\n\nYou may specify an index as well:\n\n.. code:: python\n\n   import qs_codec as qs\n\n   assert qs.decode('a[1]=c\u0026a[0]=b') == {'a': ['b', 'c']}\n\nNote that the only difference between an index in a ``list`` and a key\nin a ``dict`` is that the value between the brackets must be a number to\ncreate a ``list``. When creating ``list``\\ s with specific indices,\n`decode \u003chttps://techouse.github.io/qs_codec/qs_codec.html#module-qs_codec.decode\u003e`__ will compact a sparse ``list`` to\nonly the existing values preserving their order:\n\n.. code:: python\n\n   import qs_codec as qs\n\n   assert qs.decode('a[1]=b\u0026a[15]=c') == {'a': ['b', 'c']}\n\nNote that an empty ``str``\\ing is also a value, and will be preserved:\n\n.. code:: python\n\n   import qs_codec as qs\n\n   assert qs.decode('a[]=\u0026a[]=b') == {'a': ['', 'b']}\n\n   assert qs.decode('a[0]=b\u0026a[1]=\u0026a[2]=c') == {'a': ['b', '', 'c']}\n\n`decode \u003chttps://techouse.github.io/qs_codec/qs_codec.html#module-qs_codec.decode\u003e`__ will also limit specifying indices\nin a ``list`` to a maximum index of ``20``. Any ``list`` members with an\nindex of greater than ``20`` will instead be converted to a ``dict`` with\nthe index as the key. This is needed to handle cases when someone sent,\nfor example, ``a[999999999]`` and it will take significant time to iterate\nover this huge ``list``.\n\n.. code:: python\n\n   import qs_codec as qs\n\n   assert qs.decode('a[100]=b') == {'a': {'100': 'b'}}\n\nThis limit can be overridden by passing a `list_limit \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.models.decode_options.DecodeOptions.list_limit\u003e`__\noption:\n\n.. code:: python\n\n   import qs_codec as qs\n\n   assert qs.decode(\n       'a[1]=b',\n       qs.DecodeOptions(list_limit=0),\n   ) == {'a': {'1': 'b'}}\n\nTo disable ``list`` parsing entirely, set `parse_lists \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.models.decode_options.DecodeOptions.parse_lists\u003e`__\nto ``False``.\n\n.. code:: python\n\n   import qs_codec as qs\n\n   assert qs.decode(\n       'a[]=b',\n       qs.DecodeOptions(parse_lists=False),\n   ) == {'a': {'0': 'b'}}\n\nIf you mix notations, `decode \u003chttps://techouse.github.io/qs_codec/qs_codec.html#module-qs_codec.decode\u003e`__ will merge the two items into a ``dict``:\n\n.. code:: python\n\n   import qs_codec as qs\n\n   assert qs.decode('a[0]=b\u0026a[b]=c') == {'a': {'0': 'b', 'b': 'c'}}\n\nYou can also create ``list``\\ s of ``dict``\\ s:\n\n.. code:: python\n\n   import qs_codec as qs\n\n   assert qs.decode('a[][b]=c') == {'a': [{'b': 'c'}]}\n\n(`decode \u003chttps://techouse.github.io/qs_codec/qs_codec.html#module-qs_codec.decode\u003e`__ *cannot convert nested ``dict``\\ s, such as ``'a={b:1},{c:d}'``*)\n\nprimitive values (``int``, ``bool``, ``None``, etc.)\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nBy default, all values are parsed as ``str``\\ings.\n\n.. code:: python\n\n   import qs_codec as qs\n\n   assert qs.decode(\n       'a=15\u0026b=true\u0026c=null',\n   ) == {'a': '15', 'b': 'true', 'c': 'null'}\n\nEncoding\n~~~~~~~~\n\nWhen encoding, `encode \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.encode\u003e`__ by default URI encodes output. ``dict``\\ s are\nencoded as you would expect:\n\n.. code:: python\n\n   import qs_codec as qs\n\n   assert qs.encode({'a': 'b'}) == 'a=b'\n   assert qs.encode({'a': {'b': 'c'}}) == 'a%5Bb%5D=c'\n\nThis encoding can be disabled by setting the `encode \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.models.encode_options.EncodeOptions.encode\u003e`__\noption to ``False``:\n\n.. code:: python\n\n   import qs_codec as qs\n\n   assert qs.encode(\n       {'a': {'b': 'c'}},\n       qs.EncodeOptions(encode=False),\n   ) == 'a[b]=c'\n\nEncoding can be disabled for keys by setting the\n`encode_values_only \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.models.encode_options.EncodeOptions.encode_values_only\u003e`__ option to ``True``:\n\n.. code:: python\n\n   import qs_codec as qs\n\n   assert qs.encode(\n       {\n           'a': 'b',\n           'c': ['d', 'e=f'],\n           'f': [\n               ['g'],\n               ['h']\n           ]\n       },\n       qs.EncodeOptions(encode_values_only=True)\n   ) == 'a=b\u0026c[0]=d\u0026c[1]=e%3Df\u0026f[0][0]=g\u0026f[1][0]=h'\n\nMaximum encoding depth\n^^^^^^^^^^^^^^^^^^^^^^\n\nYou can cap how deep the encoder will traverse by setting the\n`max_depth \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.models.encode_options.EncodeOptions.max_depth\u003e`__\noption. If unset, traversal is unbounded by this option. When set, the provided limit is enforced directly.\n\n.. code:: python\n\n   import qs_codec as qs\n\n   try:\n       qs.encode({'a': {'b': {'c': 'd'}}}, qs.EncodeOptions(max_depth=2))\n   except ValueError as e:\n       assert str(e) == 'Maximum encoding depth exceeded'\n\nThis encoding can also be replaced by a custom ``Callable`` in the\n`encoder \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.models.encode_options.EncodeOptions.encoder\u003e`__ option:\n\n.. code:: python\n\n   import qs_codec as qs\n   import typing as t\n\n\n   def custom_encoder(\n       value: str,\n       charset: t.Optional[qs.Charset],\n       format: t.Optional[qs.Format],\n   ) -\u003e str:\n       if value == 'č':\n           return 'c'\n       return value\n\n\n   assert qs.encode(\n       {'a': {'b': 'č'}},\n       qs.EncodeOptions(encoder=custom_encoder),\n   ) == 'a[b]=c'\n\n(Note: the `encoder \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.models.encode_options.EncodeOptions.encoder\u003e`__ option does not apply if\n`encode \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.models.encode_options.EncodeOptions.encode\u003e`__ is ``False``).\n\nSimilar to `encoder \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.models.encode_options.EncodeOptions.encoder\u003e`__ there is a\n`decoder \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.models.decode_options.DecodeOptions.decoder\u003e`__ option for `decode \u003chttps://techouse.github.io/qs_codec/qs_codec.html#module-qs_codec.decode\u003e`__\nto override decoding of properties and values:\n\n.. code:: python\n\n   import qs_codec as qs\n   import typing as t\n\n   def custom_decoder(\n       value: t.Any,\n       charset: t.Optional[qs.Charset],\n   ) -\u003e t.Union[int, str]:\n       try:\n           return int(value)\n       except ValueError:\n           return value\n\n   assert qs.decode(\n       'foo=123',\n       qs.DecodeOptions(decoder=custom_decoder),\n   ) == {'foo': 123}\n\nExamples beyond this point will be shown as though the output is not URI\nencoded for clarity. Please note that the return values in these cases\n*will* be URI encoded during real usage.\n\nWhen ``list``\\s are encoded, they follow the\n`list_format \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.models.encode_options.EncodeOptions.list_format\u003e`__ option, which defaults to\n`INDICES \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.enums.list_format.ListFormat.INDICES\u003e`__:\n\n.. code:: python\n\n   import qs_codec as qs\n\n   assert qs.encode(\n       {'a': ['b', 'c', 'd']},\n       qs.EncodeOptions(encode=False)\n   ) == 'a[0]=b\u0026a[1]=c\u0026a[2]=d'\n\nYou may override this by setting the `indices \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.models.encode_options.EncodeOptions.indices\u003e`__ option to\n``False``, or to be more explicit, the `list_format \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.models.encode_options.EncodeOptions.list_format\u003e`__\noption to `REPEAT \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.enums.list_format.ListFormat.REPEAT\u003e`__:\n\n.. code:: python\n\n   import qs_codec as qs\n\n   assert qs.encode(\n       {'a': ['b', 'c', 'd']},\n       qs.EncodeOptions(\n           encode=False,\n           indices=False,\n       ),\n   ) == 'a=b\u0026a=c\u0026a=d'\n\nYou may use the `list_format \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.models.encode_options.EncodeOptions.list_format\u003e`__ option to specify the\nformat of the output ``list``:\n\n.. code:: python\n\n   import qs_codec as qs\n\n   # ListFormat.INDICES\n   assert qs.encode(\n       {'a': ['b', 'c']},\n       qs.EncodeOptions(\n           encode=False,\n           list_format=qs.ListFormat.INDICES,\n       ),\n   ) == 'a[0]=b\u0026a[1]=c'\n\n   # ListFormat.BRACKETS\n   assert qs.encode(\n       {'a': ['b', 'c']},\n       qs.EncodeOptions(\n           encode=False,\n           list_format=qs.ListFormat.BRACKETS,\n       ),\n   ) == 'a[]=b\u0026a[]=c'\n\n   # ListFormat.REPEAT\n   assert qs.encode(\n       {'a': ['b', 'c']},\n       qs.EncodeOptions(\n           encode=False,\n           list_format=qs.ListFormat.REPEAT,\n       ),\n   ) == 'a=b\u0026a=c'\n\n   # ListFormat.COMMA\n   assert qs.encode(\n       {'a': ['b', 'c']},\n       qs.EncodeOptions(\n           encode=False,\n           list_format=qs.ListFormat.COMMA,\n       ),\n   ) == 'a=b,c'\n\n**Note:** When using `list_format \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.models.encode_options.EncodeOptions.list_format\u003e`__ set to\n`COMMA \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.enums.list_format.ListFormat.COMMA\u003e`_, you can also pass the\n`comma_round_trip \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.models.encode_options.EncodeOptions.comma_round_trip\u003e`__ option set to ``True`` or\n``False``, to append ``[]`` on single-item ``list``\\ s, so that they can round trip through a decoding.\nSet the `comma_compact_nulls \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.models.encode_options.EncodeOptions.comma_compact_nulls\u003e`__ option to ``True`` with the same\nformat when you'd like to drop ``None`` entries instead of keeping empty slots (e.g. ``[True, False, None, True]`` becomes\n``true,false,true``).\n\n`BRACKETS \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.enums.list_format.ListFormat.BRACKETS\u003e`__ notation is used for encoding ``dict``\\s by default:\n\n.. code:: python\n\n   import qs_codec as qs\n\n   assert qs.encode(\n       {'a': {'b': {'c': 'd', 'e': 'f'}}},\n       qs.EncodeOptions(encode=False),\n   ) == 'a[b][c]=d\u0026a[b][e]=f'\n\nYou may override this to use dot notation by setting the\n`allow_dots \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.models.encode_options.EncodeOptions.allow_dots\u003e`__ option to ``True``:\n\n.. code:: python\n\n   import qs_codec as qs\n\n   assert qs.encode(\n       {'a': {'b': {'c': 'd', 'e': 'f'}}},\n       qs.EncodeOptions(encode=False, allow_dots=True),\n   ) == 'a.b.c=d\u0026a.b.e=f'\n\nYou may encode dots in keys of ``dict``\\s by setting\n`encode_dot_in_keys \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.models.encode_options.EncodeOptions.encode_dot_in_keys\u003e`__ to ``True``:\n\n.. code:: python\n\n   import qs_codec as qs\n\n   assert qs.encode(\n       {'name.obj': {'first': 'John', 'last': 'Doe'}},\n       qs.EncodeOptions(\n           allow_dots=True,\n           encode_dot_in_keys=True,\n       ),\n   ) == 'name%252Eobj.first=John\u0026name%252Eobj.last=Doe'\n\n**Caveat:** When both `encode_values_only \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.models.encode_options.EncodeOptions.encode_values_only\u003e`__\nand `encode_dot_in_keys \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.models.encode_options.EncodeOptions.encode_dot_in_keys\u003e`__ are set to\n``True``, only dots in keys and nothing else will be encoded!\n\nYou may allow empty ``list`` values by setting the\n`allow_empty_lists \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.models.encode_options.EncodeOptions.allow_empty_lists\u003e`__ option to ``True``:\n\n.. code:: python\n\n   import qs_codec as qs\n\n   assert qs.encode(\n       {'foo': [], 'bar': 'baz', },\n       qs.EncodeOptions(\n           encode=False,\n           allow_empty_lists=True,\n       ),\n   ) == 'foo[]\u0026bar=baz'\n\nEmpty ``str``\\ings and ``None`` values will be omitted, but the equals sign (``=``) remains in place:\n\n.. code:: python\n\n   import qs_codec as qs\n\n   assert qs.encode({'a': ''}) == 'a='\n\nKeys with no values (such as an empty ``dict`` or ``list``) will return nothing:\n\n.. code:: python\n\n   import qs_codec as qs\n\n   assert qs.encode({'a': []}) == ''\n\n   assert qs.encode({'a': {}}) == ''\n\n   assert qs.encode({'a': [{}]}) == ''\n\n   assert qs.encode({'a': {'b': []}}) == ''\n\n   assert qs.encode({'a': {'b': {}}}) == ''\n\n`Undefined \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.models.undefined.Undefined\u003e`__ properties will be omitted entirely:\n\n.. code:: python\n\n   import qs_codec as qs\n\n   assert qs.encode({'a': None, 'b': qs.Undefined()}) == 'a='\n\nThe query string may optionally be prepended with a question mark (``?``) by setting\n`add_query_prefix \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.models.encode_options.EncodeOptions.add_query_prefix\u003e`__ to ``True``:\n\n.. code:: python\n\n   import qs_codec as qs\n\n   assert qs.encode(\n       {'a': 'b', 'c': 'd'},\n       qs.EncodeOptions(add_query_prefix=True),\n   ) == '?a=b\u0026c=d'\n\nThe `delimiter \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.models.encode_options.EncodeOptions.delimiter\u003e`__ may be overridden as well:\n\n.. code:: python\n\n   import qs_codec as qs\n\n   assert qs.encode(\n       {'a': 'b', 'c': 'd', },\n       qs.EncodeOptions(delimiter=';')\n   ) == 'a=b;c=d'\n\nIf you only want to override the serialization of `datetime \u003chttps://docs.python.org/3/library/datetime.html#datetime-objects\u003e`__\nobjects, you can provide a ``Callable`` in the\n`serialize_date \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.models.encode_options.EncodeOptions.serialize_date\u003e`__ option:\n\n.. code:: python\n\n   import qs_codec as qs\n   import datetime\n   import sys\n\n   # First case: encoding a datetime object to an ISO 8601 string\n   assert (\n       qs.encode(\n           {\n               \"a\": (\n                   datetime.datetime.fromtimestamp(7, datetime.UTC)\n                   if sys.version_info.major == 3 and sys.version_info.minor \u003e= 11\n                   else datetime.datetime.utcfromtimestamp(7)\n               )\n           },\n           qs.EncodeOptions(encode=False),\n       )\n       == \"a=1970-01-01T00:00:07+00:00\"\n       if sys.version_info.major == 3 and sys.version_info.minor \u003e= 11\n       else \"a=1970-01-01T00:00:07\"\n   )\n\n   # Second case: encoding a datetime object to a timestamp string\n   assert (\n       qs.encode(\n           {\n               \"a\": (\n                   datetime.datetime.fromtimestamp(7, datetime.UTC)\n                   if sys.version_info.major == 3 and sys.version_info.minor \u003e= 11\n                   else datetime.datetime.utcfromtimestamp(7)\n               )\n           },\n           qs.EncodeOptions(encode=False, serialize_date=lambda date: str(int(date.timestamp()))),\n       )\n       == \"a=7\"\n   )\n\nTo affect the order of parameter keys, you can set a ``Callable`` in the\n`sort \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.models.encode_options.EncodeOptions.sort\u003e`__ option:\n\n.. code:: python\n\n   import qs_codec as qs\n\n   assert qs.encode(\n       {'a': 'c', 'z': 'y', 'b': 'f'},\n       qs.EncodeOptions(\n           encode=False,\n           sort=lambda a, b: (a \u003e b) - (a \u003c b)\n       )\n   ) == 'a=c\u0026b=f\u0026z=y'\n\nFinally, you can use the `filter \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.models.encode_options.EncodeOptions.filter\u003e`__ option to restrict\nwhich keys will be included in the encoded output. If you pass a ``Callable``, it will be called for each key to obtain\nthe replacement value. Otherwise, if you pass a ``list``, it will be used to select properties and ``list`` indices to\nbe encoded:\n\n.. code:: python\n\n   import qs_codec as qs\n   import datetime\n   import sys\n\n   # First case: using a Callable as filter\n   assert (\n       qs.encode(\n           {\n               \"a\": \"b\",\n               \"c\": \"d\",\n               \"e\": {\n                   \"f\": (\n                       datetime.datetime.fromtimestamp(123, datetime.UTC)\n                       if sys.version_info.major == 3 and sys.version_info.minor \u003e= 11\n                       else datetime.datetime.utcfromtimestamp(123)\n                   ),\n                   \"g\": [2],\n               },\n           },\n           qs.EncodeOptions(\n               encode=False,\n               filter=lambda prefix, value: {\n                   \"b\": None,\n                   \"e[f]\": int(value.timestamp()) if isinstance(value, datetime.datetime) else value,\n                   \"e[g][0]\": value * 2 if isinstance(value, int) else value,\n               }.get(prefix, value),\n           ),\n       )\n       == \"a=b\u0026c=d\u0026e[f]=123\u0026e[g][0]=4\"\n   )\n\n   # Second case: using a list as filter\n   assert qs.encode(\n       {'a': 'b', 'c': 'd', 'e': 'f'},\n       qs.EncodeOptions(\n           encode=False,\n           filter=['a', 'e']\n       )\n   ) == 'a=b\u0026e=f'\n\n   # Third case: using a list as filter with indices\n   assert qs.encode(\n       {\n           'a': ['b', 'c', 'd'],\n           'e': 'f',\n       },\n       qs.EncodeOptions(\n           encode=False,\n           filter=['a', 0, 2]\n       )\n   ) == 'a[0]=b\u0026a[2]=d'\n\nHandling ``None`` values\n~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nBy default, ``None`` values are treated like empty ``str``\\ings:\n\n.. code:: python\n\n   import qs_codec as qs\n\n   assert qs.encode({'a': None, 'b': ''}) == 'a=\u0026b='\n\nTo distinguish between ``None`` values and empty ``str``\\s use the\n`strict_null_handling \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.models.encode_options.EncodeOptions.strict_null_handling\u003e`__ flag.\nIn the result string the ``None`` values have no ``=`` sign:\n\n.. code:: python\n\n   import qs_codec as qs\n\n   assert qs.encode(\n       {'a': None, 'b': ''},\n       qs.EncodeOptions(strict_null_handling=True),\n   ) == 'a\u0026b='\n\nTo decode values without ``=`` back to ``None`` use the\n`strict_null_handling \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.models.decode_options.DecodeOptions.strict_null_handling\u003e`__ flag:\n\n.. code:: python\n\n   import qs_codec as qs\n\n   assert qs.decode(\n       'a\u0026b=',\n       qs.DecodeOptions(strict_null_handling=True),\n   ) == {'a': None, 'b': ''}\n\nTo completely skip rendering keys with ``None`` values, use the\n`skip_nulls \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.models.encode_options.EncodeOptions.skip_nulls\u003e`__ flag:\n\n.. code:: python\n\n   import qs_codec as qs\n\n   assert qs.encode(\n       {'a': 'b', 'c': None},\n       qs.EncodeOptions(skip_nulls=True),\n   ) == 'a=b'\n\nIf you’re communicating with legacy systems, you can switch to\n`LATIN1 \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.enums.charset.Charset.LATIN1\u003e`__ using the\n`charset \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.models.encode_options.EncodeOptions.charset\u003e`__ option:\n\n.. code:: python\n\n   import qs_codec as qs\n\n   assert qs.encode(\n       {'æ': 'æ'},\n       qs.EncodeOptions(charset=qs.Charset.LATIN1)\n   ) == '%E6=%E6'\n\nCharacters that don’t exist in `LATIN1 \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.enums.charset.Charset.LATIN1\u003e`__\nwill be converted to numeric entities, similar to what browsers do:\n\n.. code:: python\n\n   import qs_codec as qs\n\n   assert qs.encode(\n       {'a': '☺'},\n       qs.EncodeOptions(charset=qs.Charset.LATIN1)\n   ) == 'a=%26%239786%3B'\n\nYou can use the `charset_sentinel \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.models.encode_options.EncodeOptions.charset_sentinel\u003e`__\noption to announce the character by including an ``utf8=✓`` parameter with the proper\nencoding of the checkmark, similar to what Ruby on Rails and others do when submitting forms.\n\n.. code:: python\n\n   import qs_codec as qs\n\n   assert qs.encode(\n       {'a': '☺'},\n       qs.EncodeOptions(charset_sentinel=True)\n   ) == 'utf8=%E2%9C%93\u0026a=%E2%98%BA'\n\n   assert qs.encode(\n       {'a': 'æ'},\n       qs.EncodeOptions(charset=qs.Charset.LATIN1, charset_sentinel=True)\n   ) == 'utf8=%26%2310003%3B\u0026a=%E6'\n\nDealing with special character sets\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nBy default, the encoding and decoding of characters is done in\n`UTF8 \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.enums.charset.Charset.UTF8\u003e`__, and\n`LATIN1 \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.enums.charset.Charset.LATIN1\u003e`__ support is also built in via\nthe `charset \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.models.encode_options.EncodeOptions.charset\u003e`__\nand `charset \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.models.decode_options.DecodeOptions.charset\u003e`__ parameter,\nrespectively.\n\nIf you wish to encode query strings to a different character set (i.e.\n`Shift JIS \u003chttps://en.wikipedia.org/wiki/Shift_JIS\u003e`__)\n\n.. code:: python\n\n   import qs_codec as qs\n   import codecs\n   import typing as t\n\n   def custom_encoder(\n       string: str,\n       charset: t.Optional[qs.Charset],\n       format: t.Optional[qs.Format],\n   ) -\u003e str:\n       if string:\n           buf: bytes = codecs.encode(string, 'shift_jis')\n           result: t.List[str] = ['{:02x}'.format(b) for b in buf]\n           return '%' + '%'.join(result)\n       return ''\n\n   assert qs.encode(\n       {'a': 'こんにちは！'},\n       qs.EncodeOptions(encoder=custom_encoder)\n   ) == '%61=%82%b1%82%f1%82%c9%82%bf%82%cd%81%49'\n\nThis also works for decoding of query strings:\n\n.. code:: python\n\n   import qs_codec as qs\n   import re\n   import codecs\n   import typing as t\n\n   def custom_decoder(\n       string: str,\n       charset: t.Optional[qs.Charset],\n   ) -\u003e t.Optional[str]:\n       if string:\n           result: t.List[int] = []\n           while string:\n               match: t.Optional[t.Match[str]] = re.search(r'%([0-9A-F]{2})', string, re.IGNORECASE)\n               if match:\n                   result.append(int(match.group(1), 16))\n                   string = string[match.end():]\n               else:\n                   break\n           buf: bytes = bytes(result)\n           return codecs.decode(buf, 'shift_jis')\n       return None\n\n   assert qs.decode(\n       '%61=%82%b1%82%f1%82%c9%82%bf%82%cd%81%49',\n       qs.DecodeOptions(decoder=custom_decoder)\n   ) == {'a': 'こんにちは！'}\n\nRFC 3986 and RFC 1738 space encoding\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThe default `format \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.models.encode_options.EncodeOptions.format\u003e`__ is\n`RFC3986 \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.enums.format.Format.RFC3986\u003e`__ which encodes\n``' '`` to ``%20`` which is backward compatible. You can also set the\n`format \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.models.encode_options.EncodeOptions.format\u003e`__ to\n`RFC1738 \u003chttps://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.enums.format.Format.RFC1738\u003e`__ which encodes ``' '`` to ``+``.\n\n.. code:: python\n\n   import qs_codec as qs\n\n   assert qs.encode({'a': 'b c'}) == 'a=b%20c'\n\n   assert qs.encode(\n       {'a': 'b c'},\n       qs.EncodeOptions(format=qs.Format.RFC3986)\n   ) == 'a=b%20c'\n\n   assert qs.encode(\n       {'a': 'b c'},\n       qs.EncodeOptions(format=qs.Format.RFC1738)\n   ) == 'a=b+c'\n\n--------------\n\nOther ports\n-----------\n\n+----------------------------+---------------------------------------------------------------+-----------------+\n| Port                       | Repository                                                    | Package         |\n+============================+===============================================================+=================+\n| Dart                       | `techouse/qs \u003chttps://github.com/techouse/qs\u003e`__              | |pubdev|        |\n+----------------------------+---------------------------------------------------------------+-----------------+\n| Kotlin / JVM + Android AAR | `techouse/qs-kotlin \u003chttps://github.com/techouse/qs-kotlin\u003e`__| |maven-central| |\n+----------------------------+---------------------------------------------------------------+-----------------+\n| Swift / Objective-C        | `techouse/qs-swift \u003chttps://github.com/techouse/qs-swift\u003e`__  | |spm|           |\n+----------------------------+---------------------------------------------------------------+-----------------+\n| .NET / C#                  | `techouse/qs-net \u003chttps://github.com/techouse/qs-net\u003e`__      | |nuget|         |\n+----------------------------+---------------------------------------------------------------+-----------------+\n| Node.js (original)         | `ljharb/qs \u003chttps://github.com/ljharb/qs\u003e`__                  | |npm|           |\n+----------------------------+---------------------------------------------------------------+-----------------+\n\n--------------\n\nSpecial thanks to the authors of\n`qs \u003chttps://www.npmjs.com/package/qs\u003e`__ for JavaScript: - `Jordan\nHarband \u003chttps://github.com/ljharb\u003e`__ - `TJ\nHolowaychuk \u003chttps://github.com/visionmedia/node-querystring\u003e`__\n\n.. |PyPI - Version| image:: https://img.shields.io/pypi/v/qs_codec?logo=pypi\u0026label=PyPI\n   :target: https://pypi.org/project/qs-codec/\n   :alt: PyPI version\n.. |PyPI - Downloads| image:: https://img.shields.io/pypi/dm/qs_codec?logo=pypi\u0026label=PyPI%20downloads\n   :target: https://pypistats.org/packages/qs-codec\n   :alt: PyPI downloads\n.. |PyPI - Status| image:: https://img.shields.io/pypi/status/qs_codec?logo=pypi\n   :target: https://pypi.org/project/qs-codec/\n   :alt: PyPI status\n.. |PyPI - Python Version| image:: https://img.shields.io/pypi/pyversions/qs_codec?logo=python\u0026label=Python\n   :target: https://pypi.org/project/qs-codec/\n   :alt: Python version support\n.. |PyPy Support| image:: https://img.shields.io/badge/PyPy-3.8%20%7C%203.9%20%7C%203.10%20%7C%203.11-6f42c1?logo=pypy\n   :target: https://www.pypy.org/\n   :alt: PyPy support status\n.. |PyPI - Format| image:: https://img.shields.io/pypi/format/qs_codec?logo=python\n   :target: https://pypi.org/project/qs-codec/\n   :alt: PyPI format\n.. |Test| image:: https://github.com/techouse/qs_codec/actions/workflows/test.yml/badge.svg\n   :target: https://github.com/techouse/qs_codec/actions/workflows/test.yml\n   :alt: Test Status\n.. |CodeQL| image:: https://github.com/techouse/qs_codec/actions/workflows/github-code-scanning/codeql/badge.svg\n   :target: https://github.com/techouse/qs_codec/actions/workflows/github-code-scanning/codeql\n   :alt: CodeQL Status\n.. |Publish| image:: https://github.com/techouse/qs_codec/actions/workflows/publish.yml/badge.svg\n   :target: https://github.com/techouse/qs_codec/actions/workflows/publish.yml\n   :alt: Publish Status\n.. |Docs| image:: https://github.com/techouse/qs_codec/actions/workflows/docs.yml/badge.svg\n   :target: https://github.com/techouse/qs_codec/actions/workflows/docs.yml\n   :alt: Docs Status\n.. |Black| image:: https://img.shields.io/badge/code%20style-black-000000.svg?logo=python\n   :target: https://github.com/psf/black\n   :alt: Code style Black\n.. |codecov| image:: https://codecov.io/gh/techouse/qs_codec/graph/badge.svg?token=Vp0z05yj2l\n   :target: https://codecov.io/gh/techouse/qs_codec\n   :alt: Code Coverage\n.. |Codacy| image:: https://app.codacy.com/project/badge/Grade/7ead208221ae4f6785631043064647e4\n   :target: https://app.codacy.com/gh/techouse/qs_codec/dashboard?utm_source=gh\u0026utm_medium=referral\u0026utm_content=\u0026utm_campaign=Badge_grade\n   :alt: Codacy Quality\n.. |License| image:: https://img.shields.io/github/license/techouse/qs_codec\n   :target: LICENSE\n   :alt: License\n.. |GitHub Sponsors| image:: https://img.shields.io/github/sponsors/techouse?logo=github\n   :target: https://github.com/sponsors/techouse\n   :alt: GitHub Sponsors\n.. |GitHub Repo stars| image:: https://img.shields.io/github/stars/techouse/qs_codec\n   :target: https://github.com/techouse/qs_codec/stargazers\n   :alt: GitHub Repo stars\n.. |Contributor Covenant| image:: https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg?logo=contributorcovenant\n   :target: CODE-OF-CONDUCT.md\n   :alt: Contributor Covenant\n.. |OpenSSF| image:: https://www.bestpractices.dev/projects/11863/badge\n   :target: https://www.bestpractices.dev/projects/11863\n   :alt: OpenSSF Best Practices\n.. |flake8| image:: https://img.shields.io/badge/flake8-checked-blueviolet.svg?logo=python\n   :target: https://flake8.pycqa.org/en/latest/\n   :alt: flake8 Status\n.. |mypy| image:: https://img.shields.io/badge/mypy-checked-blue.svg?logo=python\n   :target: https://mypy.readthedocs.io/en/stable/\n   :alt: typing mypy\n.. |pylint| image:: https://img.shields.io/badge/linting-pylint-yellowgreen.svg?logo=python\n   :target: https://github.com/pylint-dev/pylint\n   :alt: linting pylint\n.. |isort| image:: https://img.shields.io/badge/imports-isort-blue.svg?logo=python\n   :target: https://pycqa.github.io/isort/\n   :alt: imports isort\n.. |bandit| image:: https://img.shields.io/badge/security-bandit-blue.svg?logo=python\n   :target: https://github.com/PyCQA/bandit\n   :alt: Security Status\n.. |pubdev| image:: https://img.shields.io/pub/v/qs_dart?logo=dart\u0026label=pub.dev\n   :target: https://pub.dev/packages/qs_dart\n   :alt: pub.dev version\n.. |maven-central| image:: https://img.shields.io/maven-central/v/io.github.techouse/qs-kotlin?logo=kotlin\u0026label=Maven%20Central\n   :target: https://central.sonatype.com/artifact/io.github.techouse/qs-kotlin\n   :alt: Maven Central version\n.. |spm| image:: https://img.shields.io/github/v/release/techouse/qs-swift?logo=swift\u0026label=SwiftPM\n   :target: https://swiftpackageindex.com/techouse/qs-swift\n   :alt: Swift Package Manager version\n.. |nuget| image:: https://img.shields.io/nuget/v/QsNet?logo=dotnet\u0026label=NuGet\n   :target: https://www.nuget.org/packages/QsNet\n   :alt: NuGet version\n.. |npm| image:: https://img.shields.io/npm/v/qs?logo=javascript\u0026label=npm\n   :target: https://www.npmjs.com/package/qs\n   :alt: npm version\n","funding_links":["https://github.com/sponsors/techouse","https://paypal.me/ktusar"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftechouse%2Fqs_codec","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftechouse%2Fqs_codec","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftechouse%2Fqs_codec/lists"}