qs_codec.models package¶
Submodules¶
qs_codec.models.decode_options module¶
This module contains the DecodeOptions
class that configures the output of decode
.
Keys are decoded identically to values by the default decoder; whether a decoded .
splits
segments is controlled by parsing options (allow_dots
/ decode_dot_in_keys
) elsewhere.
- class qs_codec.models.decode_options.DecodeOptions(allow_dots: bool | None = None, decode_dot_in_keys: bool | None = None, allow_empty_lists: bool = False, list_limit: int = 20, charset: ~qs_codec.enums.charset.Charset = Charset.UTF8, charset_sentinel: bool = False, comma: bool = False, delimiter: str | ~typing.Pattern[str] = '&', depth: int = 5, parameter_limit: int | float = 1000, duplicates: ~qs_codec.enums.duplicates.Duplicates = Duplicates.COMBINE, ignore_query_prefix: bool = False, interpret_numeric_entities: bool = False, parse_lists: bool = True, strict_depth: bool = False, strict_null_handling: bool = False, raise_on_limit_exceeded: bool = False, decoder: ~typing.Callable[[...], str | None] | None = <bound method DecodeUtils.decode of <class 'qs_codec.utils.decode_utils.DecodeUtils'>>, legacy_decoder: ~typing.Callable[[...], str | None] | None = None)[source]¶
Bases:
object
Options that configure the output of
decode
.- allow_dots: bool | None = None¶
Set to
True
to decode dotdict
notation in the encoded input. WhenNone
(default), it inherits the value ofdecode_dot_in_keys
.
- allow_empty_lists: bool = False¶
Set to
True
to allow emptylist
values insidedict
s in the encoded input.
- charset: Charset = _CharsetDataMixin(encoding='utf-8')¶
The character encoding to use when decoding the input.
- charset_sentinel: bool = False¶
Some services add an initial
utf8=✓
value to forms so that old InternetExplorer versions are more likely to submit the form asutf-8
. Additionally, the server can check the value against wrong encodings of the checkmark character and detect that a query string orapplication/x-www-form-urlencoded
body was not sent asutf-8
, e.g. if the form had anaccept-charset
parameter or the containing page had a different character set.qs_codec
supports this mechanism via thecharset_sentinel
option. If specified, theutf-8
parameter will be omitted from the returneddict
. It will be used to switch toLATIN1
orUTF8
mode depending on how the checkmark is encoded.Important: When you specify both the
charset
option and thecharset_sentinel
option, thecharset
will be overridden when the request contains autf-8
parameter from which the actual charset can be deduced. In that sense thecharset
will behave as the default charset rather than the authoritative charset.
- comma: bool = False¶
Set to
True
to parse the input as a comma-separated value. Note: nesteddict
s, such as'a={b:1},{c:d}'
are not supported.
- decode(value: str | None, charset: Charset | None = None, *, kind: DecodeKind = DecodeKind.VALUE) Any | None [source]¶
Unified scalar decode with key/value context.
Uses the configured
decoder
(orlegacy_decoder
) when provided; otherwise falls back toDecodeUtils.decode()
. The default library behavior decodes keys identically to values; whether a.
participates in key splitting is decided later by the parser.
- decode_dot_in_keys: bool | None = None¶
Set to
True
to decode percent‑encoded dots in keys (e.g.,%2E
→.
). Note: it impliesallow_dots
, sodecode
will error if you setdecode_dot_in_keys
toTrue
, andallow_dots
toFalse
. WhenNone
(default), it defaults toFalse
.Inside bracket segments, percent-decoding naturally yields
.
from%2E/%2e
. This option controls whether top‑level encoded dots are treated as additional split points; it does not affect the literal.
produced by percent-decoding inside bracket segments.
- decode_key(value: str | None, charset: Charset | None = None) str | None [source]¶
Decode a key (or key segment). Always returns a string or
None
.Note: custom decoders returning non-strings for keys are coerced via
str()
.
- decode_value(value: str | None, charset: Charset | None = None) Any | None [source]¶
Decode a value token. Returns any scalar or
None
.
- classmethod decoder(string: str | None, charset: Charset | None = Charset.UTF8, kind: DecodeKind = DecodeKind.VALUE) str | None ¶
Custom scalar decoder invoked for each raw token prior to interpretation.
The built-in decoder supports
kind
and is invoked asdecoder(string, charset, kind=DecodeKind.KEY|VALUE)
. Custom decoders that omitkind
(orcharset
) are automatically adapted for compatibility. ReturningNone
from the decoder usesNone
as the scalar value.
- delimiter: str | Pattern[str] = '&'¶
The delimiter to use when splitting key-value pairs in the encoded input. Can be a
str
or aPattern
.
- depth: int = 5¶
By default, when nesting
dict
sqs_codec
will only decode up to 5 children deep. This depth can be overridden by setting thedepth
. The depth limit helps mitigate abuse whenqs_codec
is used to parse user input, and it is recommended to keep it a reasonably small number.
- duplicates: Duplicates = 1¶
Strategy for handling duplicate keys in the input.
COMBINE
(default): merge values into a list (e.g.,a=1&a=2
→{"a": [1, 2]}
).FIRST
: keep the first value and ignore subsequent ones ({"a": 1}
).LAST
: keep only the last value seen ({"a": 2}
).
- ignore_query_prefix: bool = False¶
Set to
True
to ignore the leading question mark query prefix in the encoded input.
- interpret_numeric_entities: bool = False¶
Set to
True
to interpret HTML numeric entities (&#...;
) in the encoded input.
- legacy_decoder: Callable[[...], str | None] | None = None¶
Back‑compat adapter for legacy decoders of the form
decoder(value, charset)
. Preferdecoder
which may optionally accept akind
argument. When both are supplied,decoder
takes precedence (mirroring Kotlin/C#/Swift/Dart behavior).
- list_limit: int = 20¶
20
).During decoding, keys like
a[0]
,a[1]
, … are treated as list indices. If an index exceeds this limit, the container is treated as adict
instead, with the numeric index kept as a string key (e.g.,{"999": "x"}
) to prevent creation of massive sparse lists (e.g.,a[999999999]
).This limit also applies to comma–split lists when
comma=True
. Set a larger value if you explicitly need more items, or set a smaller one to harden against abuse.- Type:
Maximum number of indexed items allowed in a single list (default
- parameter_limit: int | float = 1000¶
For similar reasons, by default
qs_codec
will only parse up to 1000 parameters. This can be overridden by passing aparameter_limit
option.
- parse_lists: bool = True¶
To disable
list
parsing entirely, setparse_lists
toFalse
.
- raise_on_limit_exceeded: bool = False¶
Raise instead of degrading gracefully when limits are exceeded.
When
True
, the decoder raises: - aDecodeError
for parameter and list limit violations; and - anIndexError
when nesting deeper thandepth
andstrict_depth=True
.When
False
(default), the decoder degrades gracefully: it slices the parameter list atparameter_limit
, stops adding items beyondlist_limit
, and—ifstrict_depth=True
—stops descending oncedepth
is reached without raising.
- strict_depth: bool = False¶
Enforce the
depth
limit when decoding nested structures.When
True
, the decoder will not descend beyonddepth
levels. Combined withraise_on_limit_exceeded
:if
raise_on_limit_exceeded=True
, exceeding the depth raises anIndexError
;if
False
, the decoder stops descending and treats deeper content as a terminal value, preserving the last valid container without raising.
- strict_null_handling: bool = False¶
Set to
True
to decode values without=
toNone
.
qs_codec.models.encode_options module¶
Configuration object for encode.
EncodeOptions mirrors the behavior and defaults of the reference qs implementation. It controls how Python values (dicts/lists/scalars) are turned into a URL-encoded query string. The options here are intentionally close to the Node.js library so behavior is predictable across languages.
Key interactions to be aware of: - allow_dots vs encode_dot_in_keys: when unspecified, allow_dots mirrors the value of encode_dot_in_keys (see __post_init__). - indices is deprecated and mapped to list_format for parity with newer ports. - encoder and serialize_date let you customize scalar/date serialization, while encode=False short-circuits the encoder entirely. - sort may return -1/0/+1 (like strcmp/NSComparisonResult.rawValue) to deterministically order keys.
- class qs_codec.models.encode_options.EncodeOptions(allow_dots: bool = None, add_query_prefix: bool = False, allow_empty_lists: bool = False, indices: bool | None = None, list_format: ~qs_codec.enums.list_format.ListFormat = ListFormat.INDICES, charset: ~qs_codec.enums.charset.Charset = Charset.UTF8, charset_sentinel: bool = False, delimiter: str = '&', encode: bool = True, encode_dot_in_keys: bool = None, encode_values_only: bool = False, format: ~qs_codec.enums.format.Format = Format.RFC3986, filter: ~typing.Callable | ~typing.List[str | int] | None = None, skip_nulls: bool = False, serialize_date: ~typing.Callable[[~datetime.datetime], str | None] = <function EncodeUtils.serialize_date>, encoder: ~typing.Callable[[~typing.Any, ~qs_codec.enums.charset.Charset | None, ~qs_codec.enums.format.Format | None], str] = <property object>, strict_null_handling: bool = False, comma_round_trip: bool | None = None, sort: ~typing.Callable[[~typing.Any, ~typing.Any], int] | None = None)[source]¶
Bases:
object
Options that configure the output of encode.
Each field corresponds to a knob in the query-string encoder. Defaults aim to be unsurprising and compatible with other Techouse qs ports. See per-field docs below for details and caveats.
- add_query_prefix: bool = False¶
When True, prefix the output with a ? (useful when appending to a base URL).
- allow_dots: bool = None¶
When True, interpret dotted keys as object paths during encoding (e.g. a.b=1). If None, mirrors encode_dot_in_keys (see __post_init__).
- allow_empty_lists: bool = False¶
When True, include empty lists in the output (e.g. a[]= instead of omitting).
- charset: Charset = _CharsetDataMixin(encoding='utf-8')¶
Character encoding used by the encoder (defaults to UTF‑8).
- charset_sentinel: bool = False¶
When True, include a sentinel parameter announcing the charset (e.g. utf8=✓).
- comma_round_trip: bool | None = None¶
Only used with ListFormat.COMMA. When True, single‑item lists append [] so they round‑trip back to a list on decode.
- delimiter: str = '&'¶
Pair delimiter between tokens (typically &; ; and others are allowed).
- encode: bool = True¶
Master switch. When False, values/keys are not percent‑encoded (joined as-is).
- encode_dot_in_keys: bool = None¶
When True, encode dots in keys literally. With encode_values_only=True, only key dots are encoded while values remain untouched.
- encode_values_only: bool = False¶
When True, the encoder is applied to values only; keys are left unencoded.
- property encoder: Callable[[Any, Charset | None, Format | None], str]¶
(value, charset|None, format|None) -> str. Note: when encode=False, this is bypassed and values are joined without percent‑encoding.
- Type:
Custom scalar encoder. Signature
- filter: Callable | List[str | int] | None = None¶
Restrict which keys get included. - If a callable is provided, it is invoked for each key and should return the replacement value (or None to drop when skip_nulls applies). - If a list is provided, only those keys/indices are retained.
- format: Format = _FormatDataMixin(format_name='RFC3986', formatter=<function Formatter.rfc3986>)¶
Space handling and percent‑encoding style. RFC3986 encodes spaces as %20, while RFC1738 uses +.
- indices: bool | None = None¶
prefer list_format. If set, maps to ListFormat.INDICES when True or ListFormat.REPEAT when False.
- Type:
Deprecated
- list_format: ListFormat = _ListFormatDataMixin(list_format_name='INDICES', generator=<function ListFormatGenerator.indices>)¶
Controls how lists are encoded (indices/brackets/repeat/comma). See ListFormat.
- serialize_date() str ¶
Hook to stringify datetime values before encoding; returning None is treated as a null value (subject to null-handling options), not as a fallback to ISO-8601.
- skip_nulls: bool = False¶
When True, omit keys whose value is None entirely (no trailing =).
- sort: Callable[[Any, Any], int] | None = None¶
Optional comparator for deterministic key ordering. Must return -1, 0, or +1.
- strict_null_handling: bool = False¶
None → a (no =), empty string → a=.
- Type:
When True, distinguish empty strings from None
qs_codec.models.undefined module¶
Undefined sentinel.
This module defines a tiny singleton Undefined used as a sentinel to mean “no value provided / omit this key”, similar to JavaScript’s undefined.
Unlike None (which commonly means an explicit null), Undefined is used by the encoder and helper utilities to skip emitting a key or to signal that a value is intentionally absent and should not be serialized.
The sentinel is identity-based: every construction returns the same instance, so is comparisons are reliable (e.g., value is Undefined()).
- class qs_codec.models.undefined.Undefined[source]¶
Bases:
object
Singleton sentinel object representing an “undefined” value.
Notes
This is not the same as None. Use None to represent a null value and Undefined() to represent “no value / omit”.
All calls to
Undefined()
return the same instance. Prefer identity checks (is
) over equality checks.
Examples
>>> from qs_codec.models.undefined import Undefined >>> a = Undefined() >>> b = Undefined() >>> a is b True >>> # Use it to indicate a key should be omitted when encoding: >>> maybe_value = Undefined() >>> if maybe_value is Undefined(): ... pass # skip emitting the key
qs_codec.models.weak_wrapper module¶
Weakly wrap any object with identity equality and deep content hashing.
- class qs_codec.models.weak_wrapper.WeakWrapper(value: Any)[source]¶
Bases:
object
Wrapper suitable for use as a WeakKeyDictionary key.
Holds a strong reference to the proxy (keeps proxy alive while wrapper exists).
Exposes a weakref to the proxy via _wref so tests can observe/force GC.
Equality is proxy identity; hash is a deep hash of the underlying value.
- property value: Any¶
Guard with the weakref so tests can simulate GC by swapping _wref.