Add files using upload-large-folder tool
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- lib/python3.12/site-packages/asttokens/util.py +487 -0
- lib/python3.12/site-packages/charset_normalizer-3.4.4.dist-info/INSTALLER +1 -0
- lib/python3.12/site-packages/charset_normalizer-3.4.4.dist-info/METADATA +764 -0
- lib/python3.12/site-packages/charset_normalizer-3.4.4.dist-info/RECORD +35 -0
- lib/python3.12/site-packages/charset_normalizer-3.4.4.dist-info/WHEEL +7 -0
- lib/python3.12/site-packages/charset_normalizer-3.4.4.dist-info/entry_points.txt +2 -0
- lib/python3.12/site-packages/charset_normalizer-3.4.4.dist-info/licenses/LICENSE +21 -0
- lib/python3.12/site-packages/charset_normalizer-3.4.4.dist-info/top_level.txt +1 -0
- lib/python3.12/site-packages/cycler/__init__.py +573 -0
- lib/python3.12/site-packages/cycler/__pycache__/__init__.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/cycler/py.typed +0 -0
- lib/python3.12/site-packages/frozenlist/__init__.py +86 -0
- lib/python3.12/site-packages/frozenlist/__init__.pyi +47 -0
- lib/python3.12/site-packages/frozenlist/__pycache__/__init__.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/frozenlist/_frozenlist.pyx +148 -0
- lib/python3.12/site-packages/frozenlist/py.typed +1 -0
- lib/python3.12/site-packages/hjson-3.1.0.dist-info/INSTALLER +1 -0
- lib/python3.12/site-packages/hjson-3.1.0.dist-info/LICENSE.txt +81 -0
- lib/python3.12/site-packages/hjson-3.1.0.dist-info/METADATA +125 -0
- lib/python3.12/site-packages/hjson-3.1.0.dist-info/RECORD +76 -0
- lib/python3.12/site-packages/hjson-3.1.0.dist-info/WHEEL +5 -0
- lib/python3.12/site-packages/hjson-3.1.0.dist-info/entry_points.txt +2 -0
- lib/python3.12/site-packages/hjson-3.1.0.dist-info/top_level.txt +1 -0
- lib/python3.12/site-packages/hjson/__init__.py +642 -0
- lib/python3.12/site-packages/hjson/__pycache__/__init__.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/hjson/__pycache__/compat.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/hjson/__pycache__/decoder.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/hjson/__pycache__/encoder.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/hjson/__pycache__/encoderH.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/hjson/__pycache__/ordered_dict.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/hjson/__pycache__/scanner.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/hjson/__pycache__/tool.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/hjson/compat.py +46 -0
- lib/python3.12/site-packages/hjson/decoder.py +569 -0
- lib/python3.12/site-packages/hjson/encoder.py +521 -0
- lib/python3.12/site-packages/hjson/encoderH.py +552 -0
- lib/python3.12/site-packages/hjson/ordered_dict.py +119 -0
- lib/python3.12/site-packages/hjson/scanner.py +56 -0
- lib/python3.12/site-packages/hjson/tests/__init__.py +61 -0
- lib/python3.12/site-packages/hjson/tests/__pycache__/__init__.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/hjson/tests/__pycache__/test_bigint_as_string.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/hjson/tests/__pycache__/test_bitsize_int_as_string.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/hjson/tests/__pycache__/test_check_circular.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/hjson/tests/__pycache__/test_decimal.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/hjson/tests/__pycache__/test_decode.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/hjson/tests/__pycache__/test_default.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/hjson/tests/__pycache__/test_dump.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/hjson/tests/__pycache__/test_encode_basestring_ascii.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/hjson/tests/__pycache__/test_errors.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/hjson/tests/__pycache__/test_fail.cpython-312.pyc +0 -0
lib/python3.12/site-packages/asttokens/util.py
ADDED
|
@@ -0,0 +1,487 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2016 Grist Labs, Inc.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# https://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
import ast
|
| 16 |
+
import collections
|
| 17 |
+
import io
|
| 18 |
+
import sys
|
| 19 |
+
import token
|
| 20 |
+
import tokenize
|
| 21 |
+
from abc import ABCMeta
|
| 22 |
+
from ast import Module, expr, AST
|
| 23 |
+
from functools import lru_cache
|
| 24 |
+
from typing import (
|
| 25 |
+
Callable,
|
| 26 |
+
Dict,
|
| 27 |
+
Iterable,
|
| 28 |
+
Iterator,
|
| 29 |
+
List,
|
| 30 |
+
Optional,
|
| 31 |
+
Tuple,
|
| 32 |
+
Union,
|
| 33 |
+
cast,
|
| 34 |
+
Any,
|
| 35 |
+
TYPE_CHECKING,
|
| 36 |
+
Type,
|
| 37 |
+
)
|
| 38 |
+
|
| 39 |
+
if TYPE_CHECKING: # pragma: no cover
|
| 40 |
+
from .astroid_compat import NodeNG
|
| 41 |
+
|
| 42 |
+
# Type class used to expand out the definition of AST to include fields added by this library
|
| 43 |
+
# It's not actually used for anything other than type checking though!
|
| 44 |
+
class EnhancedAST(AST):
|
| 45 |
+
# Additional attributes set by mark_tokens
|
| 46 |
+
first_token = None # type: Token
|
| 47 |
+
last_token = None # type: Token
|
| 48 |
+
lineno = 0 # type: int
|
| 49 |
+
end_lineno = 0 # type : int
|
| 50 |
+
end_col_offset = 0 # type : int
|
| 51 |
+
|
| 52 |
+
AstNode = Union[EnhancedAST, NodeNG]
|
| 53 |
+
|
| 54 |
+
TokenInfo = tokenize.TokenInfo
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
def token_repr(tok_type, string):
|
| 58 |
+
# type: (int, Optional[str]) -> str
|
| 59 |
+
"""Returns a human-friendly representation of a token with the given type and string."""
|
| 60 |
+
# repr() prefixes unicode with 'u' on Python2 but not Python3; strip it out for consistency.
|
| 61 |
+
return '%s:%s' % (token.tok_name[tok_type], repr(string).lstrip('u'))
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
class Token(collections.namedtuple('Token', 'type string start end line index startpos endpos')):
|
| 65 |
+
"""
|
| 66 |
+
TokenInfo is an 8-tuple containing the same 5 fields as the tokens produced by the tokenize
|
| 67 |
+
module, and 3 additional ones useful for this module:
|
| 68 |
+
|
| 69 |
+
- [0] .type Token type (see token.py)
|
| 70 |
+
- [1] .string Token (a string)
|
| 71 |
+
- [2] .start Starting (row, column) indices of the token (a 2-tuple of ints)
|
| 72 |
+
- [3] .end Ending (row, column) indices of the token (a 2-tuple of ints)
|
| 73 |
+
- [4] .line Original line (string)
|
| 74 |
+
- [5] .index Index of the token in the list of tokens that it belongs to.
|
| 75 |
+
- [6] .startpos Starting character offset into the input text.
|
| 76 |
+
- [7] .endpos Ending character offset into the input text.
|
| 77 |
+
"""
|
| 78 |
+
def __str__(self):
|
| 79 |
+
# type: () -> str
|
| 80 |
+
return token_repr(self.type, self.string)
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
def match_token(token, tok_type, tok_str=None):
|
| 84 |
+
# type: (Token, int, Optional[str]) -> bool
|
| 85 |
+
"""Returns true if token is of the given type and, if a string is given, has that string."""
|
| 86 |
+
return token.type == tok_type and (tok_str is None or token.string == tok_str)
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
def expect_token(token, tok_type, tok_str=None):
|
| 90 |
+
# type: (Token, int, Optional[str]) -> None
|
| 91 |
+
"""
|
| 92 |
+
Verifies that the given token is of the expected type. If tok_str is given, the token string
|
| 93 |
+
is verified too. If the token doesn't match, raises an informative ValueError.
|
| 94 |
+
"""
|
| 95 |
+
if not match_token(token, tok_type, tok_str):
|
| 96 |
+
raise ValueError("Expected token %s, got %s on line %s col %s" % (
|
| 97 |
+
token_repr(tok_type, tok_str), str(token),
|
| 98 |
+
token.start[0], token.start[1] + 1))
|
| 99 |
+
|
| 100 |
+
|
| 101 |
+
def is_non_coding_token(token_type):
|
| 102 |
+
# type: (int) -> bool
|
| 103 |
+
"""
|
| 104 |
+
These are considered non-coding tokens, as they don't affect the syntax tree.
|
| 105 |
+
"""
|
| 106 |
+
return token_type in (token.NL, token.COMMENT, token.ENCODING)
|
| 107 |
+
|
| 108 |
+
|
| 109 |
+
def generate_tokens(text):
|
| 110 |
+
# type: (str) -> Iterator[TokenInfo]
|
| 111 |
+
"""
|
| 112 |
+
Generates standard library tokens for the given code.
|
| 113 |
+
"""
|
| 114 |
+
# tokenize.generate_tokens is technically an undocumented API for Python3, but allows us to use the same API as for
|
| 115 |
+
# Python2. See https://stackoverflow.com/a/4952291/328565.
|
| 116 |
+
# FIXME: Remove cast once https://github.com/python/typeshed/issues/7003 gets fixed
|
| 117 |
+
return tokenize.generate_tokens(cast(Callable[[], str], io.StringIO(text).readline))
|
| 118 |
+
|
| 119 |
+
|
| 120 |
+
def iter_children_func(node):
|
| 121 |
+
# type: (AST) -> Callable
|
| 122 |
+
"""
|
| 123 |
+
Returns a function which yields all direct children of a AST node,
|
| 124 |
+
skipping children that are singleton nodes.
|
| 125 |
+
The function depends on whether ``node`` is from ``ast`` or from the ``astroid`` module.
|
| 126 |
+
"""
|
| 127 |
+
return iter_children_astroid if hasattr(node, 'get_children') else iter_children_ast
|
| 128 |
+
|
| 129 |
+
|
| 130 |
+
def iter_children_astroid(node, include_joined_str=False):
|
| 131 |
+
# type: (NodeNG, bool) -> Union[Iterator, List]
|
| 132 |
+
if not include_joined_str and is_joined_str(node):
|
| 133 |
+
return []
|
| 134 |
+
|
| 135 |
+
return node.get_children()
|
| 136 |
+
|
| 137 |
+
|
| 138 |
+
SINGLETONS = {c for n, c in ast.__dict__.items() if isinstance(c, type) and
|
| 139 |
+
issubclass(c, (ast.expr_context, ast.boolop, ast.operator, ast.unaryop, ast.cmpop))}
|
| 140 |
+
|
| 141 |
+
|
| 142 |
+
def iter_children_ast(node, include_joined_str=False):
|
| 143 |
+
# type: (AST, bool) -> Iterator[Union[AST, expr]]
|
| 144 |
+
if not include_joined_str and is_joined_str(node):
|
| 145 |
+
return
|
| 146 |
+
|
| 147 |
+
if isinstance(node, ast.Dict):
|
| 148 |
+
# override the iteration order: instead of <all keys>, <all values>,
|
| 149 |
+
# yield keys and values in source order (key1, value1, key2, value2, ...)
|
| 150 |
+
for (key, value) in zip(node.keys, node.values):
|
| 151 |
+
if key is not None:
|
| 152 |
+
yield key
|
| 153 |
+
yield value
|
| 154 |
+
return
|
| 155 |
+
|
| 156 |
+
for child in ast.iter_child_nodes(node):
|
| 157 |
+
# Skip singleton children; they don't reflect particular positions in the code and break the
|
| 158 |
+
# assumptions about the tree consisting of distinct nodes. Note that collecting classes
|
| 159 |
+
# beforehand and checking them in a set is faster than using isinstance each time.
|
| 160 |
+
if child.__class__ not in SINGLETONS:
|
| 161 |
+
yield child
|
| 162 |
+
|
| 163 |
+
|
| 164 |
+
stmt_class_names = {n for n, c in ast.__dict__.items()
|
| 165 |
+
if isinstance(c, type) and issubclass(c, ast.stmt)}
|
| 166 |
+
expr_class_names = ({n for n, c in ast.__dict__.items()
|
| 167 |
+
if isinstance(c, type) and issubclass(c, ast.expr)} |
|
| 168 |
+
{'AssignName', 'DelName', 'Const', 'AssignAttr', 'DelAttr'})
|
| 169 |
+
|
| 170 |
+
# These feel hacky compared to isinstance() but allow us to work with both ast and astroid nodes
|
| 171 |
+
# in the same way, and without even importing astroid.
|
| 172 |
+
def is_expr(node):
|
| 173 |
+
# type: (AstNode) -> bool
|
| 174 |
+
"""Returns whether node is an expression node."""
|
| 175 |
+
return node.__class__.__name__ in expr_class_names
|
| 176 |
+
|
| 177 |
+
def is_stmt(node):
|
| 178 |
+
# type: (AstNode) -> bool
|
| 179 |
+
"""Returns whether node is a statement node."""
|
| 180 |
+
return node.__class__.__name__ in stmt_class_names
|
| 181 |
+
|
| 182 |
+
def is_module(node):
|
| 183 |
+
# type: (AstNode) -> bool
|
| 184 |
+
"""Returns whether node is a module node."""
|
| 185 |
+
return node.__class__.__name__ == 'Module'
|
| 186 |
+
|
| 187 |
+
def is_joined_str(node):
|
| 188 |
+
# type: (AstNode) -> bool
|
| 189 |
+
"""Returns whether node is a JoinedStr node, used to represent f-strings."""
|
| 190 |
+
# At the moment, nodes below JoinedStr have wrong line/col info, and trying to process them only
|
| 191 |
+
# leads to errors.
|
| 192 |
+
return node.__class__.__name__ == 'JoinedStr'
|
| 193 |
+
|
| 194 |
+
|
| 195 |
+
def is_expr_stmt(node):
|
| 196 |
+
# type: (AstNode) -> bool
|
| 197 |
+
"""Returns whether node is an `Expr` node, which is a statement that is an expression."""
|
| 198 |
+
return node.__class__.__name__ == 'Expr'
|
| 199 |
+
|
| 200 |
+
|
| 201 |
+
|
| 202 |
+
CONSTANT_CLASSES: Tuple[Type, ...] = (ast.Constant,)
|
| 203 |
+
try:
|
| 204 |
+
from astroid.nodes import Const
|
| 205 |
+
CONSTANT_CLASSES += (Const,)
|
| 206 |
+
except ImportError: # pragma: no cover
|
| 207 |
+
# astroid is not available
|
| 208 |
+
pass
|
| 209 |
+
|
| 210 |
+
def is_constant(node):
|
| 211 |
+
# type: (AstNode) -> bool
|
| 212 |
+
"""Returns whether node is a Constant node."""
|
| 213 |
+
return isinstance(node, CONSTANT_CLASSES)
|
| 214 |
+
|
| 215 |
+
|
| 216 |
+
def is_ellipsis(node):
|
| 217 |
+
# type: (AstNode) -> bool
|
| 218 |
+
"""Returns whether node is an Ellipsis node."""
|
| 219 |
+
return is_constant(node) and node.value is Ellipsis # type: ignore
|
| 220 |
+
|
| 221 |
+
|
| 222 |
+
def is_starred(node):
|
| 223 |
+
# type: (AstNode) -> bool
|
| 224 |
+
"""Returns whether node is a starred expression node."""
|
| 225 |
+
return node.__class__.__name__ == 'Starred'
|
| 226 |
+
|
| 227 |
+
|
| 228 |
+
def is_slice(node):
|
| 229 |
+
# type: (AstNode) -> bool
|
| 230 |
+
"""Returns whether node represents a slice, e.g. `1:2` in `x[1:2]`"""
|
| 231 |
+
# Before 3.9, a tuple containing a slice is an ExtSlice,
|
| 232 |
+
# but this was removed in https://bugs.python.org/issue34822
|
| 233 |
+
return (
|
| 234 |
+
node.__class__.__name__ in ('Slice', 'ExtSlice')
|
| 235 |
+
or (
|
| 236 |
+
node.__class__.__name__ == 'Tuple'
|
| 237 |
+
and any(map(is_slice, cast(ast.Tuple, node).elts))
|
| 238 |
+
)
|
| 239 |
+
)
|
| 240 |
+
|
| 241 |
+
|
| 242 |
+
def is_empty_astroid_slice(node):
|
| 243 |
+
# type: (AstNode) -> bool
|
| 244 |
+
return (
|
| 245 |
+
node.__class__.__name__ == "Slice"
|
| 246 |
+
and not isinstance(node, ast.AST)
|
| 247 |
+
and node.lower is node.upper is node.step is None
|
| 248 |
+
)
|
| 249 |
+
|
| 250 |
+
|
| 251 |
+
# Sentinel value used by visit_tree().
|
| 252 |
+
_PREVISIT = object()
|
| 253 |
+
|
| 254 |
+
def visit_tree(node, previsit, postvisit):
|
| 255 |
+
# type: (Module, Callable[[AstNode, Optional[Token]], Tuple[Optional[Token], Optional[Token]]], Optional[Callable[[AstNode, Optional[Token], Optional[Token]], None]]) -> None
|
| 256 |
+
"""
|
| 257 |
+
Scans the tree under the node depth-first using an explicit stack. It avoids implicit recursion
|
| 258 |
+
via the function call stack to avoid hitting 'maximum recursion depth exceeded' error.
|
| 259 |
+
|
| 260 |
+
It calls ``previsit()`` and ``postvisit()`` as follows:
|
| 261 |
+
|
| 262 |
+
* ``previsit(node, par_value)`` - should return ``(par_value, value)``
|
| 263 |
+
``par_value`` is as returned from ``previsit()`` of the parent.
|
| 264 |
+
|
| 265 |
+
* ``postvisit(node, par_value, value)`` - should return ``value``
|
| 266 |
+
``par_value`` is as returned from ``previsit()`` of the parent, and ``value`` is as
|
| 267 |
+
returned from ``previsit()`` of this node itself. The return ``value`` is ignored except
|
| 268 |
+
the one for the root node, which is returned from the overall ``visit_tree()`` call.
|
| 269 |
+
|
| 270 |
+
For the initial node, ``par_value`` is None. ``postvisit`` may be None.
|
| 271 |
+
"""
|
| 272 |
+
if not postvisit:
|
| 273 |
+
postvisit = lambda node, pvalue, value: None
|
| 274 |
+
|
| 275 |
+
iter_children = iter_children_func(node)
|
| 276 |
+
done = set()
|
| 277 |
+
ret = None
|
| 278 |
+
stack = [(node, None, _PREVISIT)] # type: List[Tuple[AstNode, Optional[Token], Union[Optional[Token], object]]]
|
| 279 |
+
while stack:
|
| 280 |
+
current, par_value, value = stack.pop()
|
| 281 |
+
if value is _PREVISIT:
|
| 282 |
+
assert current not in done # protect againt infinite loop in case of a bad tree.
|
| 283 |
+
done.add(current)
|
| 284 |
+
|
| 285 |
+
pvalue, post_value = previsit(current, par_value)
|
| 286 |
+
stack.append((current, par_value, post_value))
|
| 287 |
+
|
| 288 |
+
# Insert all children in reverse order (so that first child ends up on top of the stack).
|
| 289 |
+
ins = len(stack)
|
| 290 |
+
for n in iter_children(current):
|
| 291 |
+
stack.insert(ins, (n, pvalue, _PREVISIT))
|
| 292 |
+
else:
|
| 293 |
+
ret = postvisit(current, par_value, cast(Optional[Token], value))
|
| 294 |
+
return ret
|
| 295 |
+
|
| 296 |
+
|
| 297 |
+
def walk(node, include_joined_str=False):
|
| 298 |
+
# type: (AST, bool) -> Iterator[Union[Module, AstNode]]
|
| 299 |
+
"""
|
| 300 |
+
Recursively yield all descendant nodes in the tree starting at ``node`` (including ``node``
|
| 301 |
+
itself), using depth-first pre-order traversal (yieling parents before their children).
|
| 302 |
+
|
| 303 |
+
This is similar to ``ast.walk()``, but with a different order, and it works for both ``ast`` and
|
| 304 |
+
``astroid`` trees. Also, as ``iter_children()``, it skips singleton nodes generated by ``ast``.
|
| 305 |
+
|
| 306 |
+
By default, ``JoinedStr`` (f-string) nodes and their contents are skipped
|
| 307 |
+
because they previously couldn't be handled. Set ``include_joined_str`` to True to include them.
|
| 308 |
+
"""
|
| 309 |
+
iter_children = iter_children_func(node)
|
| 310 |
+
done = set()
|
| 311 |
+
stack = [node]
|
| 312 |
+
while stack:
|
| 313 |
+
current = stack.pop()
|
| 314 |
+
assert current not in done # protect againt infinite loop in case of a bad tree.
|
| 315 |
+
done.add(current)
|
| 316 |
+
|
| 317 |
+
yield current
|
| 318 |
+
|
| 319 |
+
# Insert all children in reverse order (so that first child ends up on top of the stack).
|
| 320 |
+
# This is faster than building a list and reversing it.
|
| 321 |
+
ins = len(stack)
|
| 322 |
+
for c in iter_children(current, include_joined_str):
|
| 323 |
+
stack.insert(ins, c)
|
| 324 |
+
|
| 325 |
+
|
| 326 |
+
def replace(text, replacements):
|
| 327 |
+
# type: (str, List[Tuple[int, int, str]]) -> str
|
| 328 |
+
"""
|
| 329 |
+
Replaces multiple slices of text with new values. This is a convenience method for making code
|
| 330 |
+
modifications of ranges e.g. as identified by ``ASTTokens.get_text_range(node)``. Replacements is
|
| 331 |
+
an iterable of ``(start, end, new_text)`` tuples.
|
| 332 |
+
|
| 333 |
+
For example, ``replace("this is a test", [(0, 4, "X"), (8, 9, "THE")])`` produces
|
| 334 |
+
``"X is THE test"``.
|
| 335 |
+
"""
|
| 336 |
+
p = 0
|
| 337 |
+
parts = []
|
| 338 |
+
for (start, end, new_text) in sorted(replacements):
|
| 339 |
+
parts.append(text[p:start])
|
| 340 |
+
parts.append(new_text)
|
| 341 |
+
p = end
|
| 342 |
+
parts.append(text[p:])
|
| 343 |
+
return ''.join(parts)
|
| 344 |
+
|
| 345 |
+
|
| 346 |
+
class NodeMethods:
|
| 347 |
+
"""
|
| 348 |
+
Helper to get `visit_{node_type}` methods given a node's class and cache the results.
|
| 349 |
+
"""
|
| 350 |
+
def __init__(self):
|
| 351 |
+
# type: () -> None
|
| 352 |
+
self._cache = {} # type: Dict[Union[ABCMeta, type], Callable[[AstNode, Token, Token], Tuple[Token, Token]]]
|
| 353 |
+
|
| 354 |
+
def get(self, obj, cls):
|
| 355 |
+
# type: (Any, Union[ABCMeta, type]) -> Callable
|
| 356 |
+
"""
|
| 357 |
+
Using the lowercase name of the class as node_type, returns `obj.visit_{node_type}`,
|
| 358 |
+
or `obj.visit_default` if the type-specific method is not found.
|
| 359 |
+
"""
|
| 360 |
+
method = self._cache.get(cls)
|
| 361 |
+
if not method:
|
| 362 |
+
name = "visit_" + cls.__name__.lower()
|
| 363 |
+
method = getattr(obj, name, obj.visit_default)
|
| 364 |
+
self._cache[cls] = method
|
| 365 |
+
return method
|
| 366 |
+
|
| 367 |
+
|
| 368 |
+
def patched_generate_tokens(original_tokens):
|
| 369 |
+
# type: (Iterable[TokenInfo]) -> Iterator[TokenInfo]
|
| 370 |
+
"""
|
| 371 |
+
Fixes tokens yielded by `tokenize.generate_tokens` to handle more non-ASCII characters in identifiers.
|
| 372 |
+
Workaround for https://github.com/python/cpython/issues/68382.
|
| 373 |
+
Should only be used when tokenizing a string that is known to be valid syntax,
|
| 374 |
+
because it assumes that error tokens are not actually errors.
|
| 375 |
+
Combines groups of consecutive NAME, NUMBER, and/or ERRORTOKEN tokens into a single NAME token.
|
| 376 |
+
"""
|
| 377 |
+
group = [] # type: List[tokenize.TokenInfo]
|
| 378 |
+
for tok in original_tokens:
|
| 379 |
+
if (
|
| 380 |
+
tok.type in (tokenize.NAME, tokenize.ERRORTOKEN, tokenize.NUMBER)
|
| 381 |
+
# Only combine tokens if they have no whitespace in between
|
| 382 |
+
and (not group or group[-1].end == tok.start)
|
| 383 |
+
):
|
| 384 |
+
group.append(tok)
|
| 385 |
+
else:
|
| 386 |
+
for combined_token in combine_tokens(group):
|
| 387 |
+
yield combined_token
|
| 388 |
+
group = []
|
| 389 |
+
yield tok
|
| 390 |
+
for combined_token in combine_tokens(group):
|
| 391 |
+
yield combined_token
|
| 392 |
+
|
| 393 |
+
def combine_tokens(group):
|
| 394 |
+
# type: (List[tokenize.TokenInfo]) -> List[tokenize.TokenInfo]
|
| 395 |
+
if not any(tok.type == tokenize.ERRORTOKEN for tok in group) or len({tok.line for tok in group}) != 1:
|
| 396 |
+
return group
|
| 397 |
+
return [
|
| 398 |
+
tokenize.TokenInfo(
|
| 399 |
+
type=tokenize.NAME,
|
| 400 |
+
string="".join(t.string for t in group),
|
| 401 |
+
start=group[0].start,
|
| 402 |
+
end=group[-1].end,
|
| 403 |
+
line=group[0].line,
|
| 404 |
+
)
|
| 405 |
+
]
|
| 406 |
+
|
| 407 |
+
|
| 408 |
+
def last_stmt(node):
|
| 409 |
+
# type: (AstNode) -> AstNode
|
| 410 |
+
"""
|
| 411 |
+
If the given AST node contains multiple statements, return the last one.
|
| 412 |
+
Otherwise, just return the node.
|
| 413 |
+
"""
|
| 414 |
+
child_stmts = [
|
| 415 |
+
child for child in iter_children_func(node)(node)
|
| 416 |
+
if is_stmt(child) or type(child).__name__ in (
|
| 417 |
+
"excepthandler",
|
| 418 |
+
"ExceptHandler",
|
| 419 |
+
"match_case",
|
| 420 |
+
"MatchCase",
|
| 421 |
+
"TryExcept",
|
| 422 |
+
"TryFinally",
|
| 423 |
+
)
|
| 424 |
+
]
|
| 425 |
+
if child_stmts:
|
| 426 |
+
return last_stmt(child_stmts[-1])
|
| 427 |
+
return node
|
| 428 |
+
|
| 429 |
+
|
| 430 |
+
|
| 431 |
+
@lru_cache(maxsize=None)
|
| 432 |
+
def fstring_positions_work():
|
| 433 |
+
# type: () -> bool
|
| 434 |
+
"""
|
| 435 |
+
The positions attached to nodes inside f-string FormattedValues have some bugs
|
| 436 |
+
that were fixed in Python 3.9.7 in https://github.com/python/cpython/pull/27729.
|
| 437 |
+
This checks for those bugs more concretely without relying on the Python version.
|
| 438 |
+
Specifically this checks:
|
| 439 |
+
- Values with a format spec or conversion
|
| 440 |
+
- Repeated (i.e. identical-looking) expressions
|
| 441 |
+
- f-strings implicitly concatenated over multiple lines.
|
| 442 |
+
- Multiline, triple-quoted f-strings.
|
| 443 |
+
"""
|
| 444 |
+
source = """(
|
| 445 |
+
f"a {b}{b} c {d!r} e {f:g} h {i:{j}} k {l:{m:n}}"
|
| 446 |
+
f"a {b}{b} c {d!r} e {f:g} h {i:{j}} k {l:{m:n}}"
|
| 447 |
+
f"{x + y + z} {x} {y} {z} {z} {z!a} {z:z}"
|
| 448 |
+
f'''
|
| 449 |
+
{s} {t}
|
| 450 |
+
{u} {v}
|
| 451 |
+
'''
|
| 452 |
+
)"""
|
| 453 |
+
tree = ast.parse(source)
|
| 454 |
+
name_nodes = [node for node in ast.walk(tree) if isinstance(node, ast.Name)]
|
| 455 |
+
name_positions = [(node.lineno, node.col_offset) for node in name_nodes]
|
| 456 |
+
positions_are_unique = len(set(name_positions)) == len(name_positions)
|
| 457 |
+
correct_source_segments = all(
|
| 458 |
+
ast.get_source_segment(source, node) == node.id
|
| 459 |
+
for node in name_nodes
|
| 460 |
+
)
|
| 461 |
+
return positions_are_unique and correct_source_segments
|
| 462 |
+
|
| 463 |
+
def annotate_fstring_nodes(tree):
|
| 464 |
+
# type: (ast.AST) -> None
|
| 465 |
+
"""
|
| 466 |
+
Add a special attribute `_broken_positions` to nodes inside f-strings
|
| 467 |
+
if the lineno/col_offset cannot be trusted.
|
| 468 |
+
"""
|
| 469 |
+
if sys.version_info >= (3, 12):
|
| 470 |
+
# f-strings were weirdly implemented until https://peps.python.org/pep-0701/
|
| 471 |
+
# In Python 3.12, inner nodes have sensible positions.
|
| 472 |
+
return
|
| 473 |
+
for joinedstr in walk(tree, include_joined_str=True):
|
| 474 |
+
if not isinstance(joinedstr, ast.JoinedStr):
|
| 475 |
+
continue
|
| 476 |
+
for part in joinedstr.values:
|
| 477 |
+
# The ast positions of the FormattedValues/Constant nodes span the full f-string, which is weird.
|
| 478 |
+
setattr(part, '_broken_positions', True) # use setattr for mypy
|
| 479 |
+
|
| 480 |
+
if isinstance(part, ast.FormattedValue):
|
| 481 |
+
if not fstring_positions_work():
|
| 482 |
+
for child in walk(part.value):
|
| 483 |
+
setattr(child, '_broken_positions', True)
|
| 484 |
+
|
| 485 |
+
if part.format_spec: # this is another JoinedStr
|
| 486 |
+
# Again, the standard positions span the full f-string.
|
| 487 |
+
setattr(part.format_spec, '_broken_positions', True)
|
lib/python3.12/site-packages/charset_normalizer-3.4.4.dist-info/INSTALLER
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
pip
|
lib/python3.12/site-packages/charset_normalizer-3.4.4.dist-info/METADATA
ADDED
|
@@ -0,0 +1,764 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Metadata-Version: 2.4
|
| 2 |
+
Name: charset-normalizer
|
| 3 |
+
Version: 3.4.4
|
| 4 |
+
Summary: The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet.
|
| 5 |
+
Author-email: "Ahmed R. TAHRI" <tahri.ahmed@proton.me>
|
| 6 |
+
Maintainer-email: "Ahmed R. TAHRI" <tahri.ahmed@proton.me>
|
| 7 |
+
License: MIT
|
| 8 |
+
Project-URL: Changelog, https://github.com/jawah/charset_normalizer/blob/master/CHANGELOG.md
|
| 9 |
+
Project-URL: Documentation, https://charset-normalizer.readthedocs.io/
|
| 10 |
+
Project-URL: Code, https://github.com/jawah/charset_normalizer
|
| 11 |
+
Project-URL: Issue tracker, https://github.com/jawah/charset_normalizer/issues
|
| 12 |
+
Keywords: encoding,charset,charset-detector,detector,normalization,unicode,chardet,detect
|
| 13 |
+
Classifier: Development Status :: 5 - Production/Stable
|
| 14 |
+
Classifier: Intended Audience :: Developers
|
| 15 |
+
Classifier: Operating System :: OS Independent
|
| 16 |
+
Classifier: Programming Language :: Python
|
| 17 |
+
Classifier: Programming Language :: Python :: 3
|
| 18 |
+
Classifier: Programming Language :: Python :: 3.7
|
| 19 |
+
Classifier: Programming Language :: Python :: 3.8
|
| 20 |
+
Classifier: Programming Language :: Python :: 3.9
|
| 21 |
+
Classifier: Programming Language :: Python :: 3.10
|
| 22 |
+
Classifier: Programming Language :: Python :: 3.11
|
| 23 |
+
Classifier: Programming Language :: Python :: 3.12
|
| 24 |
+
Classifier: Programming Language :: Python :: 3.13
|
| 25 |
+
Classifier: Programming Language :: Python :: 3.14
|
| 26 |
+
Classifier: Programming Language :: Python :: 3 :: Only
|
| 27 |
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
| 28 |
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
| 29 |
+
Classifier: Topic :: Text Processing :: Linguistic
|
| 30 |
+
Classifier: Topic :: Utilities
|
| 31 |
+
Classifier: Typing :: Typed
|
| 32 |
+
Requires-Python: >=3.7
|
| 33 |
+
Description-Content-Type: text/markdown
|
| 34 |
+
License-File: LICENSE
|
| 35 |
+
Provides-Extra: unicode-backport
|
| 36 |
+
Dynamic: license-file
|
| 37 |
+
|
| 38 |
+
<h1 align="center">Charset Detection, for Everyone 👋</h1>
|
| 39 |
+
|
| 40 |
+
<p align="center">
|
| 41 |
+
<sup>The Real First Universal Charset Detector</sup><br>
|
| 42 |
+
<a href="https://pypi.org/project/charset-normalizer">
|
| 43 |
+
<img src="https://img.shields.io/pypi/pyversions/charset_normalizer.svg?orange=blue" />
|
| 44 |
+
</a>
|
| 45 |
+
<a href="https://pepy.tech/project/charset-normalizer/">
|
| 46 |
+
<img alt="Download Count Total" src="https://static.pepy.tech/badge/charset-normalizer/month" />
|
| 47 |
+
</a>
|
| 48 |
+
<a href="https://bestpractices.coreinfrastructure.org/projects/7297">
|
| 49 |
+
<img src="https://bestpractices.coreinfrastructure.org/projects/7297/badge">
|
| 50 |
+
</a>
|
| 51 |
+
</p>
|
| 52 |
+
<p align="center">
|
| 53 |
+
<sup><i>Featured Packages</i></sup><br>
|
| 54 |
+
<a href="https://github.com/jawah/niquests">
|
| 55 |
+
<img alt="Static Badge" src="https://img.shields.io/badge/Niquests-Most_Advanced_HTTP_Client-cyan">
|
| 56 |
+
</a>
|
| 57 |
+
<a href="https://github.com/jawah/wassima">
|
| 58 |
+
<img alt="Static Badge" src="https://img.shields.io/badge/Wassima-Certifi_Replacement-cyan">
|
| 59 |
+
</a>
|
| 60 |
+
</p>
|
| 61 |
+
<p align="center">
|
| 62 |
+
<sup><i>In other language (unofficial port - by the community)</i></sup><br>
|
| 63 |
+
<a href="https://github.com/nickspring/charset-normalizer-rs">
|
| 64 |
+
<img alt="Static Badge" src="https://img.shields.io/badge/Rust-red">
|
| 65 |
+
</a>
|
| 66 |
+
</p>
|
| 67 |
+
|
| 68 |
+
> A library that helps you read text from an unknown charset encoding.<br /> Motivated by `chardet`,
|
| 69 |
+
> I'm trying to resolve the issue by taking a new approach.
|
| 70 |
+
> All IANA character set names for which the Python core library provides codecs are supported.
|
| 71 |
+
|
| 72 |
+
<p align="center">
|
| 73 |
+
>>>>> <a href="https://charsetnormalizerweb.ousret.now.sh" target="_blank">👉 Try Me Online Now, Then Adopt Me 👈 </a> <<<<<
|
| 74 |
+
</p>
|
| 75 |
+
|
| 76 |
+
This project offers you an alternative to **Universal Charset Encoding Detector**, also known as **Chardet**.
|
| 77 |
+
|
| 78 |
+
| Feature | [Chardet](https://github.com/chardet/chardet) | Charset Normalizer | [cChardet](https://github.com/PyYoshi/cChardet) |
|
| 79 |
+
|--------------------------------------------------|:---------------------------------------------:|:--------------------------------------------------------------------------------------------------:|:-----------------------------------------------:|
|
| 80 |
+
| `Fast` | ❌ | ✅ | ✅ |
|
| 81 |
+
| `Universal**` | ❌ | ✅ | ❌ |
|
| 82 |
+
| `Reliable` **without** distinguishable standards | ❌ | ✅ | ✅ |
|
| 83 |
+
| `Reliable` **with** distinguishable standards | ✅ | ✅ | ✅ |
|
| 84 |
+
| `License` | LGPL-2.1<br>_restrictive_ | MIT | MPL-1.1<br>_restrictive_ |
|
| 85 |
+
| `Native Python` | ✅ | ✅ | ❌ |
|
| 86 |
+
| `Detect spoken language` | ❌ | ✅ | N/A |
|
| 87 |
+
| `UnicodeDecodeError Safety` | ❌ | ✅ | ❌ |
|
| 88 |
+
| `Whl Size (min)` | 193.6 kB | 42 kB | ~200 kB |
|
| 89 |
+
| `Supported Encoding` | 33 | 🎉 [99](https://charset-normalizer.readthedocs.io/en/latest/user/support.html#supported-encodings) | 40 |
|
| 90 |
+
|
| 91 |
+
<p align="center">
|
| 92 |
+
<img src="https://i.imgflip.com/373iay.gif" alt="Reading Normalized Text" width="226"/><img src="https://media.tenor.com/images/c0180f70732a18b4965448d33adba3d0/tenor.gif" alt="Cat Reading Text" width="200"/>
|
| 93 |
+
</p>
|
| 94 |
+
|
| 95 |
+
*\*\* : They are clearly using specific code for a specific encoding even if covering most of used one*<br>
|
| 96 |
+
|
| 97 |
+
## ⚡ Performance
|
| 98 |
+
|
| 99 |
+
This package offer better performance than its counterpart Chardet. Here are some numbers.
|
| 100 |
+
|
| 101 |
+
| Package | Accuracy | Mean per file (ms) | File per sec (est) |
|
| 102 |
+
|-----------------------------------------------|:--------:|:------------------:|:------------------:|
|
| 103 |
+
| [chardet](https://github.com/chardet/chardet) | 86 % | 63 ms | 16 file/sec |
|
| 104 |
+
| charset-normalizer | **98 %** | **10 ms** | 100 file/sec |
|
| 105 |
+
|
| 106 |
+
| Package | 99th percentile | 95th percentile | 50th percentile |
|
| 107 |
+
|-----------------------------------------------|:---------------:|:---------------:|:---------------:|
|
| 108 |
+
| [chardet](https://github.com/chardet/chardet) | 265 ms | 71 ms | 7 ms |
|
| 109 |
+
| charset-normalizer | 100 ms | 50 ms | 5 ms |
|
| 110 |
+
|
| 111 |
+
_updated as of december 2024 using CPython 3.12_
|
| 112 |
+
|
| 113 |
+
Chardet's performance on larger file (1MB+) are very poor. Expect huge difference on large payload.
|
| 114 |
+
|
| 115 |
+
> Stats are generated using 400+ files using default parameters. More details on used files, see GHA workflows.
|
| 116 |
+
> And yes, these results might change at any time. The dataset can be updated to include more files.
|
| 117 |
+
> The actual delays heavily depends on your CPU capabilities. The factors should remain the same.
|
| 118 |
+
> Keep in mind that the stats are generous and that Chardet accuracy vs our is measured using Chardet initial capability
|
| 119 |
+
> (e.g. Supported Encoding) Challenge-them if you want.
|
| 120 |
+
|
| 121 |
+
## ✨ Installation
|
| 122 |
+
|
| 123 |
+
Using pip:
|
| 124 |
+
|
| 125 |
+
```sh
|
| 126 |
+
pip install charset-normalizer -U
|
| 127 |
+
```
|
| 128 |
+
|
| 129 |
+
## 🚀 Basic Usage
|
| 130 |
+
|
| 131 |
+
### CLI
|
| 132 |
+
This package comes with a CLI.
|
| 133 |
+
|
| 134 |
+
```
|
| 135 |
+
usage: normalizer [-h] [-v] [-a] [-n] [-m] [-r] [-f] [-t THRESHOLD]
|
| 136 |
+
file [file ...]
|
| 137 |
+
|
| 138 |
+
The Real First Universal Charset Detector. Discover originating encoding used
|
| 139 |
+
on text file. Normalize text to unicode.
|
| 140 |
+
|
| 141 |
+
positional arguments:
|
| 142 |
+
files File(s) to be analysed
|
| 143 |
+
|
| 144 |
+
optional arguments:
|
| 145 |
+
-h, --help show this help message and exit
|
| 146 |
+
-v, --verbose Display complementary information about file if any.
|
| 147 |
+
Stdout will contain logs about the detection process.
|
| 148 |
+
-a, --with-alternative
|
| 149 |
+
Output complementary possibilities if any. Top-level
|
| 150 |
+
JSON WILL be a list.
|
| 151 |
+
-n, --normalize Permit to normalize input file. If not set, program
|
| 152 |
+
does not write anything.
|
| 153 |
+
-m, --minimal Only output the charset detected to STDOUT. Disabling
|
| 154 |
+
JSON output.
|
| 155 |
+
-r, --replace Replace file when trying to normalize it instead of
|
| 156 |
+
creating a new one.
|
| 157 |
+
-f, --force Replace file without asking if you are sure, use this
|
| 158 |
+
flag with caution.
|
| 159 |
+
-t THRESHOLD, --threshold THRESHOLD
|
| 160 |
+
Define a custom maximum amount of chaos allowed in
|
| 161 |
+
decoded content. 0. <= chaos <= 1.
|
| 162 |
+
--version Show version information and exit.
|
| 163 |
+
```
|
| 164 |
+
|
| 165 |
+
```bash
|
| 166 |
+
normalizer ./data/sample.1.fr.srt
|
| 167 |
+
```
|
| 168 |
+
|
| 169 |
+
or
|
| 170 |
+
|
| 171 |
+
```bash
|
| 172 |
+
python -m charset_normalizer ./data/sample.1.fr.srt
|
| 173 |
+
```
|
| 174 |
+
|
| 175 |
+
🎉 Since version 1.4.0 the CLI produce easily usable stdout result in JSON format.
|
| 176 |
+
|
| 177 |
+
```json
|
| 178 |
+
{
|
| 179 |
+
"path": "/home/default/projects/charset_normalizer/data/sample.1.fr.srt",
|
| 180 |
+
"encoding": "cp1252",
|
| 181 |
+
"encoding_aliases": [
|
| 182 |
+
"1252",
|
| 183 |
+
"windows_1252"
|
| 184 |
+
],
|
| 185 |
+
"alternative_encodings": [
|
| 186 |
+
"cp1254",
|
| 187 |
+
"cp1256",
|
| 188 |
+
"cp1258",
|
| 189 |
+
"iso8859_14",
|
| 190 |
+
"iso8859_15",
|
| 191 |
+
"iso8859_16",
|
| 192 |
+
"iso8859_3",
|
| 193 |
+
"iso8859_9",
|
| 194 |
+
"latin_1",
|
| 195 |
+
"mbcs"
|
| 196 |
+
],
|
| 197 |
+
"language": "French",
|
| 198 |
+
"alphabets": [
|
| 199 |
+
"Basic Latin",
|
| 200 |
+
"Latin-1 Supplement"
|
| 201 |
+
],
|
| 202 |
+
"has_sig_or_bom": false,
|
| 203 |
+
"chaos": 0.149,
|
| 204 |
+
"coherence": 97.152,
|
| 205 |
+
"unicode_path": null,
|
| 206 |
+
"is_preferred": true
|
| 207 |
+
}
|
| 208 |
+
```
|
| 209 |
+
|
| 210 |
+
### Python
|
| 211 |
+
*Just print out normalized text*
|
| 212 |
+
```python
|
| 213 |
+
from charset_normalizer import from_path
|
| 214 |
+
|
| 215 |
+
results = from_path('./my_subtitle.srt')
|
| 216 |
+
|
| 217 |
+
print(str(results.best()))
|
| 218 |
+
```
|
| 219 |
+
|
| 220 |
+
*Upgrade your code without effort*
|
| 221 |
+
```python
|
| 222 |
+
from charset_normalizer import detect
|
| 223 |
+
```
|
| 224 |
+
|
| 225 |
+
The above code will behave the same as **chardet**. We ensure that we offer the best (reasonable) BC result possible.
|
| 226 |
+
|
| 227 |
+
See the docs for advanced usage : [readthedocs.io](https://charset-normalizer.readthedocs.io/en/latest/)
|
| 228 |
+
|
| 229 |
+
## 😇 Why
|
| 230 |
+
|
| 231 |
+
When I started using Chardet, I noticed that it was not suited to my expectations, and I wanted to propose a
|
| 232 |
+
reliable alternative using a completely different method. Also! I never back down on a good challenge!
|
| 233 |
+
|
| 234 |
+
I **don't care** about the **originating charset** encoding, because **two different tables** can
|
| 235 |
+
produce **two identical rendered string.**
|
| 236 |
+
What I want is to get readable text, the best I can.
|
| 237 |
+
|
| 238 |
+
In a way, **I'm brute forcing text decoding.** How cool is that ? 😎
|
| 239 |
+
|
| 240 |
+
Don't confuse package **ftfy** with charset-normalizer or chardet. ftfy goal is to repair Unicode string whereas charset-normalizer to convert raw file in unknown encoding to unicode.
|
| 241 |
+
|
| 242 |
+
## 🍰 How
|
| 243 |
+
|
| 244 |
+
- Discard all charset encoding table that could not fit the binary content.
|
| 245 |
+
- Measure noise, or the mess once opened (by chunks) with a corresponding charset encoding.
|
| 246 |
+
- Extract matches with the lowest mess detected.
|
| 247 |
+
- Additionally, we measure coherence / probe for a language.
|
| 248 |
+
|
| 249 |
+
**Wait a minute**, what is noise/mess and coherence according to **YOU ?**
|
| 250 |
+
|
| 251 |
+
*Noise :* I opened hundred of text files, **written by humans**, with the wrong encoding table. **I observed**, then
|
| 252 |
+
**I established** some ground rules about **what is obvious** when **it seems like** a mess (aka. defining noise in rendered text).
|
| 253 |
+
I know that my interpretation of what is noise is probably incomplete, feel free to contribute in order to
|
| 254 |
+
improve or rewrite it.
|
| 255 |
+
|
| 256 |
+
*Coherence :* For each language there is on earth, we have computed ranked letter appearance occurrences (the best we can). So I thought
|
| 257 |
+
that intel is worth something here. So I use those records against decoded text to check if I can detect intelligent design.
|
| 258 |
+
|
| 259 |
+
## ⚡ Known limitations
|
| 260 |
+
|
| 261 |
+
- Language detection is unreliable when text contains two or more languages sharing identical letters. (eg. HTML (english tags) + Turkish content (Sharing Latin characters))
|
| 262 |
+
- Every charset detector heavily depends on sufficient content. In common cases, do not bother run detection on very tiny content.
|
| 263 |
+
|
| 264 |
+
## ⚠️ About Python EOLs
|
| 265 |
+
|
| 266 |
+
**If you are running:**
|
| 267 |
+
|
| 268 |
+
- Python >=2.7,<3.5: Unsupported
|
| 269 |
+
- Python 3.5: charset-normalizer < 2.1
|
| 270 |
+
- Python 3.6: charset-normalizer < 3.1
|
| 271 |
+
- Python 3.7: charset-normalizer < 4.0
|
| 272 |
+
|
| 273 |
+
Upgrade your Python interpreter as soon as possible.
|
| 274 |
+
|
| 275 |
+
## 👤 Contributing
|
| 276 |
+
|
| 277 |
+
Contributions, issues and feature requests are very much welcome.<br />
|
| 278 |
+
Feel free to check [issues page](https://github.com/ousret/charset_normalizer/issues) if you want to contribute.
|
| 279 |
+
|
| 280 |
+
## 📝 License
|
| 281 |
+
|
| 282 |
+
Copyright © [Ahmed TAHRI @Ousret](https://github.com/Ousret).<br />
|
| 283 |
+
This project is [MIT](https://github.com/Ousret/charset_normalizer/blob/master/LICENSE) licensed.
|
| 284 |
+
|
| 285 |
+
Characters frequencies used in this project © 2012 [Denny Vrandečić](http://simia.net/letters/)
|
| 286 |
+
|
| 287 |
+
## 💼 For Enterprise
|
| 288 |
+
|
| 289 |
+
Professional support for charset-normalizer is available as part of the [Tidelift
|
| 290 |
+
Subscription][1]. Tidelift gives software development teams a single source for
|
| 291 |
+
purchasing and maintaining their software, with professional grade assurances
|
| 292 |
+
from the experts who know it best, while seamlessly integrating with existing
|
| 293 |
+
tools.
|
| 294 |
+
|
| 295 |
+
[1]: https://tidelift.com/subscription/pkg/pypi-charset-normalizer?utm_source=pypi-charset-normalizer&utm_medium=readme
|
| 296 |
+
|
| 297 |
+
[](https://www.bestpractices.dev/projects/7297)
|
| 298 |
+
|
| 299 |
+
# Changelog
|
| 300 |
+
All notable changes to charset-normalizer will be documented in this file. This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
| 301 |
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
| 302 |
+
|
| 303 |
+
## [3.4.4](https://github.com/Ousret/charset_normalizer/compare/3.4.2...3.4.4) (2025-10-13)
|
| 304 |
+
|
| 305 |
+
### Changed
|
| 306 |
+
- Bound `setuptools` to a specific constraint `setuptools>=68,<=81`.
|
| 307 |
+
- Raised upper bound of mypyc for the optional pre-built extension to v1.18.2
|
| 308 |
+
|
| 309 |
+
### Removed
|
| 310 |
+
- `setuptools-scm` as a build dependency.
|
| 311 |
+
|
| 312 |
+
### Misc
|
| 313 |
+
- Enforced hashes in `dev-requirements.txt` and created `ci-requirements.txt` for security purposes.
|
| 314 |
+
- Additional pre-built wheels for riscv64, s390x, and armv7l architectures.
|
| 315 |
+
- Restore ` multiple.intoto.jsonl` in GitHub releases in addition to individual attestation file per wheel.
|
| 316 |
+
|
| 317 |
+
## [3.4.3](https://github.com/Ousret/charset_normalizer/compare/3.4.2...3.4.3) (2025-08-09)
|
| 318 |
+
|
| 319 |
+
### Changed
|
| 320 |
+
- mypy(c) is no longer a required dependency at build time if `CHARSET_NORMALIZER_USE_MYPYC` isn't set to `1`. (#595) (#583)
|
| 321 |
+
- automatically lower confidence on small bytes samples that are not Unicode in `detect` output legacy function. (#391)
|
| 322 |
+
|
| 323 |
+
### Added
|
| 324 |
+
- Custom build backend to overcome inability to mark mypy as an optional dependency in the build phase.
|
| 325 |
+
- Support for Python 3.14
|
| 326 |
+
|
| 327 |
+
### Fixed
|
| 328 |
+
- sdist archive contained useless directories.
|
| 329 |
+
- automatically fallback on valid UTF-16 or UTF-32 even if the md says it's noisy. (#633)
|
| 330 |
+
|
| 331 |
+
### Misc
|
| 332 |
+
- SBOM are automatically published to the relevant GitHub release to comply with regulatory changes.
|
| 333 |
+
Each published wheel comes with its SBOM. We choose CycloneDX as the format.
|
| 334 |
+
- Prebuilt optimized wheel are no longer distributed by default for CPython 3.7 due to a change in cibuildwheel.
|
| 335 |
+
|
| 336 |
+
## [3.4.2](https://github.com/Ousret/charset_normalizer/compare/3.4.1...3.4.2) (2025-05-02)
|
| 337 |
+
|
| 338 |
+
### Fixed
|
| 339 |
+
- Addressed the DeprecationWarning in our CLI regarding `argparse.FileType` by backporting the target class into the package. (#591)
|
| 340 |
+
- Improved the overall reliability of the detector with CJK Ideographs. (#605) (#587)
|
| 341 |
+
|
| 342 |
+
### Changed
|
| 343 |
+
- Optional mypyc compilation upgraded to version 1.15 for Python >= 3.8
|
| 344 |
+
|
| 345 |
+
## [3.4.1](https://github.com/Ousret/charset_normalizer/compare/3.4.0...3.4.1) (2024-12-24)
|
| 346 |
+
|
| 347 |
+
### Changed
|
| 348 |
+
- Project metadata are now stored using `pyproject.toml` instead of `setup.cfg` using setuptools as the build backend.
|
| 349 |
+
- Enforce annotation delayed loading for a simpler and consistent types in the project.
|
| 350 |
+
- Optional mypyc compilation upgraded to version 1.14 for Python >= 3.8
|
| 351 |
+
|
| 352 |
+
### Added
|
| 353 |
+
- pre-commit configuration.
|
| 354 |
+
- noxfile.
|
| 355 |
+
|
| 356 |
+
### Removed
|
| 357 |
+
- `build-requirements.txt` as per using `pyproject.toml` native build configuration.
|
| 358 |
+
- `bin/integration.py` and `bin/serve.py` in favor of downstream integration test (see noxfile).
|
| 359 |
+
- `setup.cfg` in favor of `pyproject.toml` metadata configuration.
|
| 360 |
+
- Unused `utils.range_scan` function.
|
| 361 |
+
|
| 362 |
+
### Fixed
|
| 363 |
+
- Converting content to Unicode bytes may insert `utf_8` instead of preferred `utf-8`. (#572)
|
| 364 |
+
- Deprecation warning "'count' is passed as positional argument" when converting to Unicode bytes on Python 3.13+
|
| 365 |
+
|
| 366 |
+
## [3.4.0](https://github.com/Ousret/charset_normalizer/compare/3.3.2...3.4.0) (2024-10-08)
|
| 367 |
+
|
| 368 |
+
### Added
|
| 369 |
+
- Argument `--no-preemptive` in the CLI to prevent the detector to search for hints.
|
| 370 |
+
- Support for Python 3.13 (#512)
|
| 371 |
+
|
| 372 |
+
### Fixed
|
| 373 |
+
- Relax the TypeError exception thrown when trying to compare a CharsetMatch with anything else than a CharsetMatch.
|
| 374 |
+
- Improved the general reliability of the detector based on user feedbacks. (#520) (#509) (#498) (#407) (#537)
|
| 375 |
+
- Declared charset in content (preemptive detection) not changed when converting to utf-8 bytes. (#381)
|
| 376 |
+
|
| 377 |
+
## [3.3.2](https://github.com/Ousret/charset_normalizer/compare/3.3.1...3.3.2) (2023-10-31)
|
| 378 |
+
|
| 379 |
+
### Fixed
|
| 380 |
+
- Unintentional memory usage regression when using large payload that match several encoding (#376)
|
| 381 |
+
- Regression on some detection case showcased in the documentation (#371)
|
| 382 |
+
|
| 383 |
+
### Added
|
| 384 |
+
- Noise (md) probe that identify malformed arabic representation due to the presence of letters in isolated form (credit to my wife)
|
| 385 |
+
|
| 386 |
+
## [3.3.1](https://github.com/Ousret/charset_normalizer/compare/3.3.0...3.3.1) (2023-10-22)
|
| 387 |
+
|
| 388 |
+
### Changed
|
| 389 |
+
- Optional mypyc compilation upgraded to version 1.6.1 for Python >= 3.8
|
| 390 |
+
- Improved the general detection reliability based on reports from the community
|
| 391 |
+
|
| 392 |
+
## [3.3.0](https://github.com/Ousret/charset_normalizer/compare/3.2.0...3.3.0) (2023-09-30)
|
| 393 |
+
|
| 394 |
+
### Added
|
| 395 |
+
- Allow to execute the CLI (e.g. normalizer) through `python -m charset_normalizer.cli` or `python -m charset_normalizer`
|
| 396 |
+
- Support for 9 forgotten encoding that are supported by Python but unlisted in `encoding.aliases` as they have no alias (#323)
|
| 397 |
+
|
| 398 |
+
### Removed
|
| 399 |
+
- (internal) Redundant utils.is_ascii function and unused function is_private_use_only
|
| 400 |
+
- (internal) charset_normalizer.assets is moved inside charset_normalizer.constant
|
| 401 |
+
|
| 402 |
+
### Changed
|
| 403 |
+
- (internal) Unicode code blocks in constants are updated using the latest v15.0.0 definition to improve detection
|
| 404 |
+
- Optional mypyc compilation upgraded to version 1.5.1 for Python >= 3.8
|
| 405 |
+
|
| 406 |
+
### Fixed
|
| 407 |
+
- Unable to properly sort CharsetMatch when both chaos/noise and coherence were close due to an unreachable condition in \_\_lt\_\_ (#350)
|
| 408 |
+
|
| 409 |
+
## [3.2.0](https://github.com/Ousret/charset_normalizer/compare/3.1.0...3.2.0) (2023-06-07)
|
| 410 |
+
|
| 411 |
+
### Changed
|
| 412 |
+
- Typehint for function `from_path` no longer enforce `PathLike` as its first argument
|
| 413 |
+
- Minor improvement over the global detection reliability
|
| 414 |
+
|
| 415 |
+
### Added
|
| 416 |
+
- Introduce function `is_binary` that relies on main capabilities, and optimized to detect binaries
|
| 417 |
+
- Propagate `enable_fallback` argument throughout `from_bytes`, `from_path`, and `from_fp` that allow a deeper control over the detection (default True)
|
| 418 |
+
- Explicit support for Python 3.12
|
| 419 |
+
|
| 420 |
+
### Fixed
|
| 421 |
+
- Edge case detection failure where a file would contain 'very-long' camel cased word (Issue #289)
|
| 422 |
+
|
| 423 |
+
## [3.1.0](https://github.com/Ousret/charset_normalizer/compare/3.0.1...3.1.0) (2023-03-06)
|
| 424 |
+
|
| 425 |
+
### Added
|
| 426 |
+
- Argument `should_rename_legacy` for legacy function `detect` and disregard any new arguments without errors (PR #262)
|
| 427 |
+
|
| 428 |
+
### Removed
|
| 429 |
+
- Support for Python 3.6 (PR #260)
|
| 430 |
+
|
| 431 |
+
### Changed
|
| 432 |
+
- Optional speedup provided by mypy/c 1.0.1
|
| 433 |
+
|
| 434 |
+
## [3.0.1](https://github.com/Ousret/charset_normalizer/compare/3.0.0...3.0.1) (2022-11-18)
|
| 435 |
+
|
| 436 |
+
### Fixed
|
| 437 |
+
- Multi-bytes cutter/chunk generator did not always cut correctly (PR #233)
|
| 438 |
+
|
| 439 |
+
### Changed
|
| 440 |
+
- Speedup provided by mypy/c 0.990 on Python >= 3.7
|
| 441 |
+
|
| 442 |
+
## [3.0.0](https://github.com/Ousret/charset_normalizer/compare/2.1.1...3.0.0) (2022-10-20)
|
| 443 |
+
|
| 444 |
+
### Added
|
| 445 |
+
- Extend the capability of explain=True when cp_isolation contains at most two entries (min one), will log in details of the Mess-detector results
|
| 446 |
+
- Support for alternative language frequency set in charset_normalizer.assets.FREQUENCIES
|
| 447 |
+
- Add parameter `language_threshold` in `from_bytes`, `from_path` and `from_fp` to adjust the minimum expected coherence ratio
|
| 448 |
+
- `normalizer --version` now specify if current version provide extra speedup (meaning mypyc compilation whl)
|
| 449 |
+
|
| 450 |
+
### Changed
|
| 451 |
+
- Build with static metadata using 'build' frontend
|
| 452 |
+
- Make the language detection stricter
|
| 453 |
+
- Optional: Module `md.py` can be compiled using Mypyc to provide an extra speedup up to 4x faster than v2.1
|
| 454 |
+
|
| 455 |
+
### Fixed
|
| 456 |
+
- CLI with opt --normalize fail when using full path for files
|
| 457 |
+
- TooManyAccentuatedPlugin induce false positive on the mess detection when too few alpha character have been fed to it
|
| 458 |
+
- Sphinx warnings when generating the documentation
|
| 459 |
+
|
| 460 |
+
### Removed
|
| 461 |
+
- Coherence detector no longer return 'Simple English' instead return 'English'
|
| 462 |
+
- Coherence detector no longer return 'Classical Chinese' instead return 'Chinese'
|
| 463 |
+
- Breaking: Method `first()` and `best()` from CharsetMatch
|
| 464 |
+
- UTF-7 will no longer appear as "detected" without a recognized SIG/mark (is unreliable/conflict with ASCII)
|
| 465 |
+
- Breaking: Class aliases CharsetDetector, CharsetDoctor, CharsetNormalizerMatch and CharsetNormalizerMatches
|
| 466 |
+
- Breaking: Top-level function `normalize`
|
| 467 |
+
- Breaking: Properties `chaos_secondary_pass`, `coherence_non_latin` and `w_counter` from CharsetMatch
|
| 468 |
+
- Support for the backport `unicodedata2`
|
| 469 |
+
|
| 470 |
+
## [3.0.0rc1](https://github.com/Ousret/charset_normalizer/compare/3.0.0b2...3.0.0rc1) (2022-10-18)
|
| 471 |
+
|
| 472 |
+
### Added
|
| 473 |
+
- Extend the capability of explain=True when cp_isolation contains at most two entries (min one), will log in details of the Mess-detector results
|
| 474 |
+
- Support for alternative language frequency set in charset_normalizer.assets.FREQUENCIES
|
| 475 |
+
- Add parameter `language_threshold` in `from_bytes`, `from_path` and `from_fp` to adjust the minimum expected coherence ratio
|
| 476 |
+
|
| 477 |
+
### Changed
|
| 478 |
+
- Build with static metadata using 'build' frontend
|
| 479 |
+
- Make the language detection stricter
|
| 480 |
+
|
| 481 |
+
### Fixed
|
| 482 |
+
- CLI with opt --normalize fail when using full path for files
|
| 483 |
+
- TooManyAccentuatedPlugin induce false positive on the mess detection when too few alpha character have been fed to it
|
| 484 |
+
|
| 485 |
+
### Removed
|
| 486 |
+
- Coherence detector no longer return 'Simple English' instead return 'English'
|
| 487 |
+
- Coherence detector no longer return 'Classical Chinese' instead return 'Chinese'
|
| 488 |
+
|
| 489 |
+
## [3.0.0b2](https://github.com/Ousret/charset_normalizer/compare/3.0.0b1...3.0.0b2) (2022-08-21)
|
| 490 |
+
|
| 491 |
+
### Added
|
| 492 |
+
- `normalizer --version` now specify if current version provide extra speedup (meaning mypyc compilation whl)
|
| 493 |
+
|
| 494 |
+
### Removed
|
| 495 |
+
- Breaking: Method `first()` and `best()` from CharsetMatch
|
| 496 |
+
- UTF-7 will no longer appear as "detected" without a recognized SIG/mark (is unreliable/conflict with ASCII)
|
| 497 |
+
|
| 498 |
+
### Fixed
|
| 499 |
+
- Sphinx warnings when generating the documentation
|
| 500 |
+
|
| 501 |
+
## [3.0.0b1](https://github.com/Ousret/charset_normalizer/compare/2.1.0...3.0.0b1) (2022-08-15)
|
| 502 |
+
|
| 503 |
+
### Changed
|
| 504 |
+
- Optional: Module `md.py` can be compiled using Mypyc to provide an extra speedup up to 4x faster than v2.1
|
| 505 |
+
|
| 506 |
+
### Removed
|
| 507 |
+
- Breaking: Class aliases CharsetDetector, CharsetDoctor, CharsetNormalizerMatch and CharsetNormalizerMatches
|
| 508 |
+
- Breaking: Top-level function `normalize`
|
| 509 |
+
- Breaking: Properties `chaos_secondary_pass`, `coherence_non_latin` and `w_counter` from CharsetMatch
|
| 510 |
+
- Support for the backport `unicodedata2`
|
| 511 |
+
|
| 512 |
+
## [2.1.1](https://github.com/Ousret/charset_normalizer/compare/2.1.0...2.1.1) (2022-08-19)
|
| 513 |
+
|
| 514 |
+
### Deprecated
|
| 515 |
+
- Function `normalize` scheduled for removal in 3.0
|
| 516 |
+
|
| 517 |
+
### Changed
|
| 518 |
+
- Removed useless call to decode in fn is_unprintable (#206)
|
| 519 |
+
|
| 520 |
+
### Fixed
|
| 521 |
+
- Third-party library (i18n xgettext) crashing not recognizing utf_8 (PEP 263) with underscore from [@aleksandernovikov](https://github.com/aleksandernovikov) (#204)
|
| 522 |
+
|
| 523 |
+
## [2.1.0](https://github.com/Ousret/charset_normalizer/compare/2.0.12...2.1.0) (2022-06-19)
|
| 524 |
+
|
| 525 |
+
### Added
|
| 526 |
+
- Output the Unicode table version when running the CLI with `--version` (PR #194)
|
| 527 |
+
|
| 528 |
+
### Changed
|
| 529 |
+
- Re-use decoded buffer for single byte character sets from [@nijel](https://github.com/nijel) (PR #175)
|
| 530 |
+
- Fixing some performance bottlenecks from [@deedy5](https://github.com/deedy5) (PR #183)
|
| 531 |
+
|
| 532 |
+
### Fixed
|
| 533 |
+
- Workaround potential bug in cpython with Zero Width No-Break Space located in Arabic Presentation Forms-B, Unicode 1.1 not acknowledged as space (PR #175)
|
| 534 |
+
- CLI default threshold aligned with the API threshold from [@oleksandr-kuzmenko](https://github.com/oleksandr-kuzmenko) (PR #181)
|
| 535 |
+
|
| 536 |
+
### Removed
|
| 537 |
+
- Support for Python 3.5 (PR #192)
|
| 538 |
+
|
| 539 |
+
### Deprecated
|
| 540 |
+
- Use of backport unicodedata from `unicodedata2` as Python is quickly catching up, scheduled for removal in 3.0 (PR #194)
|
| 541 |
+
|
| 542 |
+
## [2.0.12](https://github.com/Ousret/charset_normalizer/compare/2.0.11...2.0.12) (2022-02-12)
|
| 543 |
+
|
| 544 |
+
### Fixed
|
| 545 |
+
- ASCII miss-detection on rare cases (PR #170)
|
| 546 |
+
|
| 547 |
+
## [2.0.11](https://github.com/Ousret/charset_normalizer/compare/2.0.10...2.0.11) (2022-01-30)
|
| 548 |
+
|
| 549 |
+
### Added
|
| 550 |
+
- Explicit support for Python 3.11 (PR #164)
|
| 551 |
+
|
| 552 |
+
### Changed
|
| 553 |
+
- The logging behavior have been completely reviewed, now using only TRACE and DEBUG levels (PR #163 #165)
|
| 554 |
+
|
| 555 |
+
## [2.0.10](https://github.com/Ousret/charset_normalizer/compare/2.0.9...2.0.10) (2022-01-04)
|
| 556 |
+
|
| 557 |
+
### Fixed
|
| 558 |
+
- Fallback match entries might lead to UnicodeDecodeError for large bytes sequence (PR #154)
|
| 559 |
+
|
| 560 |
+
### Changed
|
| 561 |
+
- Skipping the language-detection (CD) on ASCII (PR #155)
|
| 562 |
+
|
| 563 |
+
## [2.0.9](https://github.com/Ousret/charset_normalizer/compare/2.0.8...2.0.9) (2021-12-03)
|
| 564 |
+
|
| 565 |
+
### Changed
|
| 566 |
+
- Moderating the logging impact (since 2.0.8) for specific environments (PR #147)
|
| 567 |
+
|
| 568 |
+
### Fixed
|
| 569 |
+
- Wrong logging level applied when setting kwarg `explain` to True (PR #146)
|
| 570 |
+
|
| 571 |
+
## [2.0.8](https://github.com/Ousret/charset_normalizer/compare/2.0.7...2.0.8) (2021-11-24)
|
| 572 |
+
### Changed
|
| 573 |
+
- Improvement over Vietnamese detection (PR #126)
|
| 574 |
+
- MD improvement on trailing data and long foreign (non-pure latin) data (PR #124)
|
| 575 |
+
- Efficiency improvements in cd/alphabet_languages from [@adbar](https://github.com/adbar) (PR #122)
|
| 576 |
+
- call sum() without an intermediary list following PEP 289 recommendations from [@adbar](https://github.com/adbar) (PR #129)
|
| 577 |
+
- Code style as refactored by Sourcery-AI (PR #131)
|
| 578 |
+
- Minor adjustment on the MD around european words (PR #133)
|
| 579 |
+
- Remove and replace SRTs from assets / tests (PR #139)
|
| 580 |
+
- Initialize the library logger with a `NullHandler` by default from [@nmaynes](https://github.com/nmaynes) (PR #135)
|
| 581 |
+
- Setting kwarg `explain` to True will add provisionally (bounded to function lifespan) a specific stream handler (PR #135)
|
| 582 |
+
|
| 583 |
+
### Fixed
|
| 584 |
+
- Fix large (misleading) sequence giving UnicodeDecodeError (PR #137)
|
| 585 |
+
- Avoid using too insignificant chunk (PR #137)
|
| 586 |
+
|
| 587 |
+
### Added
|
| 588 |
+
- Add and expose function `set_logging_handler` to configure a specific StreamHandler from [@nmaynes](https://github.com/nmaynes) (PR #135)
|
| 589 |
+
- Add `CHANGELOG.md` entries, format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) (PR #141)
|
| 590 |
+
|
| 591 |
+
## [2.0.7](https://github.com/Ousret/charset_normalizer/compare/2.0.6...2.0.7) (2021-10-11)
|
| 592 |
+
### Added
|
| 593 |
+
- Add support for Kazakh (Cyrillic) language detection (PR #109)
|
| 594 |
+
|
| 595 |
+
### Changed
|
| 596 |
+
- Further, improve inferring the language from a given single-byte code page (PR #112)
|
| 597 |
+
- Vainly trying to leverage PEP263 when PEP3120 is not supported (PR #116)
|
| 598 |
+
- Refactoring for potential performance improvements in loops from [@adbar](https://github.com/adbar) (PR #113)
|
| 599 |
+
- Various detection improvement (MD+CD) (PR #117)
|
| 600 |
+
|
| 601 |
+
### Removed
|
| 602 |
+
- Remove redundant logging entry about detected language(s) (PR #115)
|
| 603 |
+
|
| 604 |
+
### Fixed
|
| 605 |
+
- Fix a minor inconsistency between Python 3.5 and other versions regarding language detection (PR #117 #102)
|
| 606 |
+
|
| 607 |
+
## [2.0.6](https://github.com/Ousret/charset_normalizer/compare/2.0.5...2.0.6) (2021-09-18)
|
| 608 |
+
### Fixed
|
| 609 |
+
- Unforeseen regression with the loss of the backward-compatibility with some older minor of Python 3.5.x (PR #100)
|
| 610 |
+
- Fix CLI crash when using --minimal output in certain cases (PR #103)
|
| 611 |
+
|
| 612 |
+
### Changed
|
| 613 |
+
- Minor improvement to the detection efficiency (less than 1%) (PR #106 #101)
|
| 614 |
+
|
| 615 |
+
## [2.0.5](https://github.com/Ousret/charset_normalizer/compare/2.0.4...2.0.5) (2021-09-14)
|
| 616 |
+
### Changed
|
| 617 |
+
- The project now comply with: flake8, mypy, isort and black to ensure a better overall quality (PR #81)
|
| 618 |
+
- The BC-support with v1.x was improved, the old staticmethods are restored (PR #82)
|
| 619 |
+
- The Unicode detection is slightly improved (PR #93)
|
| 620 |
+
- Add syntax sugar \_\_bool\_\_ for results CharsetMatches list-container (PR #91)
|
| 621 |
+
|
| 622 |
+
### Removed
|
| 623 |
+
- The project no longer raise warning on tiny content given for detection, will be simply logged as warning instead (PR #92)
|
| 624 |
+
|
| 625 |
+
### Fixed
|
| 626 |
+
- In some rare case, the chunks extractor could cut in the middle of a multi-byte character and could mislead the mess detection (PR #95)
|
| 627 |
+
- Some rare 'space' characters could trip up the UnprintablePlugin/Mess detection (PR #96)
|
| 628 |
+
- The MANIFEST.in was not exhaustive (PR #78)
|
| 629 |
+
|
| 630 |
+
## [2.0.4](https://github.com/Ousret/charset_normalizer/compare/2.0.3...2.0.4) (2021-07-30)
|
| 631 |
+
### Fixed
|
| 632 |
+
- The CLI no longer raise an unexpected exception when no encoding has been found (PR #70)
|
| 633 |
+
- Fix accessing the 'alphabets' property when the payload contains surrogate characters (PR #68)
|
| 634 |
+
- The logger could mislead (explain=True) on detected languages and the impact of one MBCS match (PR #72)
|
| 635 |
+
- Submatch factoring could be wrong in rare edge cases (PR #72)
|
| 636 |
+
- Multiple files given to the CLI were ignored when publishing results to STDOUT. (After the first path) (PR #72)
|
| 637 |
+
- Fix line endings from CRLF to LF for certain project files (PR #67)
|
| 638 |
+
|
| 639 |
+
### Changed
|
| 640 |
+
- Adjust the MD to lower the sensitivity, thus improving the global detection reliability (PR #69 #76)
|
| 641 |
+
- Allow fallback on specified encoding if any (PR #71)
|
| 642 |
+
|
| 643 |
+
## [2.0.3](https://github.com/Ousret/charset_normalizer/compare/2.0.2...2.0.3) (2021-07-16)
|
| 644 |
+
### Changed
|
| 645 |
+
- Part of the detection mechanism has been improved to be less sensitive, resulting in more accurate detection results. Especially ASCII. (PR #63)
|
| 646 |
+
- According to the community wishes, the detection will fall back on ASCII or UTF-8 in a last-resort case. (PR #64)
|
| 647 |
+
|
| 648 |
+
## [2.0.2](https://github.com/Ousret/charset_normalizer/compare/2.0.1...2.0.2) (2021-07-15)
|
| 649 |
+
### Fixed
|
| 650 |
+
- Empty/Too small JSON payload miss-detection fixed. Report from [@tseaver](https://github.com/tseaver) (PR #59)
|
| 651 |
+
|
| 652 |
+
### Changed
|
| 653 |
+
- Don't inject unicodedata2 into sys.modules from [@akx](https://github.com/akx) (PR #57)
|
| 654 |
+
|
| 655 |
+
## [2.0.1](https://github.com/Ousret/charset_normalizer/compare/2.0.0...2.0.1) (2021-07-13)
|
| 656 |
+
### Fixed
|
| 657 |
+
- Make it work where there isn't a filesystem available, dropping assets frequencies.json. Report from [@sethmlarson](https://github.com/sethmlarson). (PR #55)
|
| 658 |
+
- Using explain=False permanently disable the verbose output in the current runtime (PR #47)
|
| 659 |
+
- One log entry (language target preemptive) was not show in logs when using explain=True (PR #47)
|
| 660 |
+
- Fix undesired exception (ValueError) on getitem of instance CharsetMatches (PR #52)
|
| 661 |
+
|
| 662 |
+
### Changed
|
| 663 |
+
- Public function normalize default args values were not aligned with from_bytes (PR #53)
|
| 664 |
+
|
| 665 |
+
### Added
|
| 666 |
+
- You may now use charset aliases in cp_isolation and cp_exclusion arguments (PR #47)
|
| 667 |
+
|
| 668 |
+
## [2.0.0](https://github.com/Ousret/charset_normalizer/compare/1.4.1...2.0.0) (2021-07-02)
|
| 669 |
+
### Changed
|
| 670 |
+
- 4x to 5 times faster than the previous 1.4.0 release. At least 2x faster than Chardet.
|
| 671 |
+
- Accent has been made on UTF-8 detection, should perform rather instantaneous.
|
| 672 |
+
- The backward compatibility with Chardet has been greatly improved. The legacy detect function returns an identical charset name whenever possible.
|
| 673 |
+
- The detection mechanism has been slightly improved, now Turkish content is detected correctly (most of the time)
|
| 674 |
+
- The program has been rewritten to ease the readability and maintainability. (+Using static typing)+
|
| 675 |
+
- utf_7 detection has been reinstated.
|
| 676 |
+
|
| 677 |
+
### Removed
|
| 678 |
+
- This package no longer require anything when used with Python 3.5 (Dropped cached_property)
|
| 679 |
+
- Removed support for these languages: Catalan, Esperanto, Kazakh, Baque, Volapük, Azeri, Galician, Nynorsk, Macedonian, and Serbocroatian.
|
| 680 |
+
- The exception hook on UnicodeDecodeError has been removed.
|
| 681 |
+
|
| 682 |
+
### Deprecated
|
| 683 |
+
- Methods coherence_non_latin, w_counter, chaos_secondary_pass of the class CharsetMatch are now deprecated and scheduled for removal in v3.0
|
| 684 |
+
|
| 685 |
+
### Fixed
|
| 686 |
+
- The CLI output used the relative path of the file(s). Should be absolute.
|
| 687 |
+
|
| 688 |
+
## [1.4.1](https://github.com/Ousret/charset_normalizer/compare/1.4.0...1.4.1) (2021-05-28)
|
| 689 |
+
### Fixed
|
| 690 |
+
- Logger configuration/usage no longer conflict with others (PR #44)
|
| 691 |
+
|
| 692 |
+
## [1.4.0](https://github.com/Ousret/charset_normalizer/compare/1.3.9...1.4.0) (2021-05-21)
|
| 693 |
+
### Removed
|
| 694 |
+
- Using standard logging instead of using the package loguru.
|
| 695 |
+
- Dropping nose test framework in favor of the maintained pytest.
|
| 696 |
+
- Choose to not use dragonmapper package to help with gibberish Chinese/CJK text.
|
| 697 |
+
- Require cached_property only for Python 3.5 due to constraint. Dropping for every other interpreter version.
|
| 698 |
+
- Stop support for UTF-7 that does not contain a SIG.
|
| 699 |
+
- Dropping PrettyTable, replaced with pure JSON output in CLI.
|
| 700 |
+
|
| 701 |
+
### Fixed
|
| 702 |
+
- BOM marker in a CharsetNormalizerMatch instance could be False in rare cases even if obviously present. Due to the sub-match factoring process.
|
| 703 |
+
- Not searching properly for the BOM when trying utf32/16 parent codec.
|
| 704 |
+
|
| 705 |
+
### Changed
|
| 706 |
+
- Improving the package final size by compressing frequencies.json.
|
| 707 |
+
- Huge improvement over the larges payload.
|
| 708 |
+
|
| 709 |
+
### Added
|
| 710 |
+
- CLI now produces JSON consumable output.
|
| 711 |
+
- Return ASCII if given sequences fit. Given reasonable confidence.
|
| 712 |
+
|
| 713 |
+
## [1.3.9](https://github.com/Ousret/charset_normalizer/compare/1.3.8...1.3.9) (2021-05-13)
|
| 714 |
+
|
| 715 |
+
### Fixed
|
| 716 |
+
- In some very rare cases, you may end up getting encode/decode errors due to a bad bytes payload (PR #40)
|
| 717 |
+
|
| 718 |
+
## [1.3.8](https://github.com/Ousret/charset_normalizer/compare/1.3.7...1.3.8) (2021-05-12)
|
| 719 |
+
|
| 720 |
+
### Fixed
|
| 721 |
+
- Empty given payload for detection may cause an exception if trying to access the `alphabets` property. (PR #39)
|
| 722 |
+
|
| 723 |
+
## [1.3.7](https://github.com/Ousret/charset_normalizer/compare/1.3.6...1.3.7) (2021-05-12)
|
| 724 |
+
|
| 725 |
+
### Fixed
|
| 726 |
+
- The legacy detect function should return UTF-8-SIG if sig is present in the payload. (PR #38)
|
| 727 |
+
|
| 728 |
+
## [1.3.6](https://github.com/Ousret/charset_normalizer/compare/1.3.5...1.3.6) (2021-02-09)
|
| 729 |
+
|
| 730 |
+
### Changed
|
| 731 |
+
- Amend the previous release to allow prettytable 2.0 (PR #35)
|
| 732 |
+
|
| 733 |
+
## [1.3.5](https://github.com/Ousret/charset_normalizer/compare/1.3.4...1.3.5) (2021-02-08)
|
| 734 |
+
|
| 735 |
+
### Fixed
|
| 736 |
+
- Fix error while using the package with a python pre-release interpreter (PR #33)
|
| 737 |
+
|
| 738 |
+
### Changed
|
| 739 |
+
- Dependencies refactoring, constraints revised.
|
| 740 |
+
|
| 741 |
+
### Added
|
| 742 |
+
- Add python 3.9 and 3.10 to the supported interpreters
|
| 743 |
+
|
| 744 |
+
MIT License
|
| 745 |
+
|
| 746 |
+
Copyright (c) 2025 TAHRI Ahmed R.
|
| 747 |
+
|
| 748 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 749 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 750 |
+
in the Software without restriction, including without limitation the rights
|
| 751 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 752 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 753 |
+
furnished to do so, subject to the following conditions:
|
| 754 |
+
|
| 755 |
+
The above copyright notice and this permission notice shall be included in all
|
| 756 |
+
copies or substantial portions of the Software.
|
| 757 |
+
|
| 758 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 759 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 760 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 761 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 762 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 763 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 764 |
+
SOFTWARE.
|
lib/python3.12/site-packages/charset_normalizer-3.4.4.dist-info/RECORD
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
../../../bin/normalizer,sha256=n68xuWjXo6taHTYOiNut7Va3FDsRj1XCmzJUDDTkTr4,269
|
| 2 |
+
charset_normalizer-3.4.4.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
|
| 3 |
+
charset_normalizer-3.4.4.dist-info/METADATA,sha256=jVuUFBti8dav19YLvWissTihVdF2ozUY4KKMw7jdkBQ,37303
|
| 4 |
+
charset_normalizer-3.4.4.dist-info/RECORD,,
|
| 5 |
+
charset_normalizer-3.4.4.dist-info/WHEEL,sha256=DxRnWQz-Kp9-4a4hdDHsSv0KUC3H7sN9Nbef3-8RjXU,190
|
| 6 |
+
charset_normalizer-3.4.4.dist-info/entry_points.txt,sha256=ADSTKrkXZ3hhdOVFi6DcUEHQRS0xfxDIE_pEz4wLIXA,65
|
| 7 |
+
charset_normalizer-3.4.4.dist-info/licenses/LICENSE,sha256=bQ1Bv-FwrGx9wkjJpj4lTQ-0WmDVCoJX0K-SxuJJuIc,1071
|
| 8 |
+
charset_normalizer-3.4.4.dist-info/top_level.txt,sha256=7ASyzePr8_xuZWJsnqJjIBtyV8vhEo0wBCv1MPRRi3Q,19
|
| 9 |
+
charset_normalizer/__init__.py,sha256=OKRxRv2Zhnqk00tqkN0c1BtJjm165fWXLydE52IKuHc,1590
|
| 10 |
+
charset_normalizer/__main__.py,sha256=yzYxMR-IhKRHYwcSlavEv8oGdwxsR89mr2X09qXGdps,109
|
| 11 |
+
charset_normalizer/__pycache__/__init__.cpython-312.pyc,,
|
| 12 |
+
charset_normalizer/__pycache__/__main__.cpython-312.pyc,,
|
| 13 |
+
charset_normalizer/__pycache__/api.cpython-312.pyc,,
|
| 14 |
+
charset_normalizer/__pycache__/cd.cpython-312.pyc,,
|
| 15 |
+
charset_normalizer/__pycache__/constant.cpython-312.pyc,,
|
| 16 |
+
charset_normalizer/__pycache__/legacy.cpython-312.pyc,,
|
| 17 |
+
charset_normalizer/__pycache__/md.cpython-312.pyc,,
|
| 18 |
+
charset_normalizer/__pycache__/models.cpython-312.pyc,,
|
| 19 |
+
charset_normalizer/__pycache__/utils.cpython-312.pyc,,
|
| 20 |
+
charset_normalizer/__pycache__/version.cpython-312.pyc,,
|
| 21 |
+
charset_normalizer/api.py,sha256=V07i8aVeCD8T2fSia3C-fn0i9t8qQguEBhsqszg32Ns,22668
|
| 22 |
+
charset_normalizer/cd.py,sha256=WKTo1HDb-H9HfCDc3Bfwq5jzS25Ziy9SE2a74SgTq88,12522
|
| 23 |
+
charset_normalizer/cli/__init__.py,sha256=D8I86lFk2-py45JvqxniTirSj_sFyE6sjaY_0-G1shc,136
|
| 24 |
+
charset_normalizer/cli/__main__.py,sha256=dMaXG6IJXRvqq8z2tig7Qb83-BpWTln55ooiku5_uvg,12646
|
| 25 |
+
charset_normalizer/cli/__pycache__/__init__.cpython-312.pyc,,
|
| 26 |
+
charset_normalizer/cli/__pycache__/__main__.cpython-312.pyc,,
|
| 27 |
+
charset_normalizer/constant.py,sha256=7UVY4ldYhmQMHUdgQ_sgZmzcQ0xxYxpBunqSZ-XJZ8U,42713
|
| 28 |
+
charset_normalizer/legacy.py,sha256=sYBzSpzsRrg_wF4LP536pG64BItw7Tqtc3SMQAHvFLM,2731
|
| 29 |
+
charset_normalizer/md.cpython-312-x86_64-linux-gnu.so,sha256=sZ7umtJLjKfA83NFJ7npkiDyr06zDT8cWtl6uIx2MsM,15912
|
| 30 |
+
charset_normalizer/md.py,sha256=-_oN3h3_X99nkFfqamD3yu45DC_wfk5odH0Tr_CQiXs,20145
|
| 31 |
+
charset_normalizer/md__mypyc.cpython-312-x86_64-linux-gnu.so,sha256=J2WWgLBQiO8sqdFsENp9u5V9uEH0tTwvTLszPdqhsv0,290584
|
| 32 |
+
charset_normalizer/models.py,sha256=lKXhOnIPtiakbK3i__J9wpOfzx3JDTKj7Dn3Rg0VaRI,12394
|
| 33 |
+
charset_normalizer/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 34 |
+
charset_normalizer/utils.py,sha256=sTejPgrdlNsKNucZfJCxJ95lMTLA0ShHLLE3n5wpT9Q,12170
|
| 35 |
+
charset_normalizer/version.py,sha256=nKE4qBNk5WA4LIJ_yIH_aSDfvtsyizkWMg-PUG-UZVk,115
|
lib/python3.12/site-packages/charset_normalizer-3.4.4.dist-info/WHEEL
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Wheel-Version: 1.0
|
| 2 |
+
Generator: setuptools (80.9.0)
|
| 3 |
+
Root-Is-Purelib: false
|
| 4 |
+
Tag: cp312-cp312-manylinux_2_17_x86_64
|
| 5 |
+
Tag: cp312-cp312-manylinux2014_x86_64
|
| 6 |
+
Tag: cp312-cp312-manylinux_2_28_x86_64
|
| 7 |
+
|
lib/python3.12/site-packages/charset_normalizer-3.4.4.dist-info/entry_points.txt
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[console_scripts]
|
| 2 |
+
normalizer = charset_normalizer.cli:cli_detect
|
lib/python3.12/site-packages/charset_normalizer-3.4.4.dist-info/licenses/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
MIT License
|
| 2 |
+
|
| 3 |
+
Copyright (c) 2025 TAHRI Ahmed R.
|
| 4 |
+
|
| 5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 6 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 7 |
+
in the Software without restriction, including without limitation the rights
|
| 8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 9 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 10 |
+
furnished to do so, subject to the following conditions:
|
| 11 |
+
|
| 12 |
+
The above copyright notice and this permission notice shall be included in all
|
| 13 |
+
copies or substantial portions of the Software.
|
| 14 |
+
|
| 15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 21 |
+
SOFTWARE.
|
lib/python3.12/site-packages/charset_normalizer-3.4.4.dist-info/top_level.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
charset_normalizer
|
lib/python3.12/site-packages/cycler/__init__.py
ADDED
|
@@ -0,0 +1,573 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Cycler
|
| 3 |
+
======
|
| 4 |
+
|
| 5 |
+
Cycling through combinations of values, producing dictionaries.
|
| 6 |
+
|
| 7 |
+
You can add cyclers::
|
| 8 |
+
|
| 9 |
+
from cycler import cycler
|
| 10 |
+
cc = (cycler(color=list('rgb')) +
|
| 11 |
+
cycler(linestyle=['-', '--', '-.']))
|
| 12 |
+
for d in cc:
|
| 13 |
+
print(d)
|
| 14 |
+
|
| 15 |
+
Results in::
|
| 16 |
+
|
| 17 |
+
{'color': 'r', 'linestyle': '-'}
|
| 18 |
+
{'color': 'g', 'linestyle': '--'}
|
| 19 |
+
{'color': 'b', 'linestyle': '-.'}
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
You can multiply cyclers::
|
| 23 |
+
|
| 24 |
+
from cycler import cycler
|
| 25 |
+
cc = (cycler(color=list('rgb')) *
|
| 26 |
+
cycler(linestyle=['-', '--', '-.']))
|
| 27 |
+
for d in cc:
|
| 28 |
+
print(d)
|
| 29 |
+
|
| 30 |
+
Results in::
|
| 31 |
+
|
| 32 |
+
{'color': 'r', 'linestyle': '-'}
|
| 33 |
+
{'color': 'r', 'linestyle': '--'}
|
| 34 |
+
{'color': 'r', 'linestyle': '-.'}
|
| 35 |
+
{'color': 'g', 'linestyle': '-'}
|
| 36 |
+
{'color': 'g', 'linestyle': '--'}
|
| 37 |
+
{'color': 'g', 'linestyle': '-.'}
|
| 38 |
+
{'color': 'b', 'linestyle': '-'}
|
| 39 |
+
{'color': 'b', 'linestyle': '--'}
|
| 40 |
+
{'color': 'b', 'linestyle': '-.'}
|
| 41 |
+
"""
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
from __future__ import annotations
|
| 45 |
+
|
| 46 |
+
from collections.abc import Hashable, Iterable, Generator
|
| 47 |
+
import copy
|
| 48 |
+
from functools import reduce
|
| 49 |
+
from itertools import product, cycle
|
| 50 |
+
from operator import mul, add
|
| 51 |
+
# Dict, List, Union required for runtime cast calls
|
| 52 |
+
from typing import TypeVar, Generic, Callable, Union, Dict, List, Any, overload, cast
|
| 53 |
+
|
| 54 |
+
__version__ = "0.12.1"
|
| 55 |
+
|
| 56 |
+
K = TypeVar("K", bound=Hashable)
|
| 57 |
+
L = TypeVar("L", bound=Hashable)
|
| 58 |
+
V = TypeVar("V")
|
| 59 |
+
U = TypeVar("U")
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
def _process_keys(
|
| 63 |
+
left: Cycler[K, V] | Iterable[dict[K, V]],
|
| 64 |
+
right: Cycler[K, V] | Iterable[dict[K, V]] | None,
|
| 65 |
+
) -> set[K]:
|
| 66 |
+
"""
|
| 67 |
+
Helper function to compose cycler keys.
|
| 68 |
+
|
| 69 |
+
Parameters
|
| 70 |
+
----------
|
| 71 |
+
left, right : iterable of dictionaries or None
|
| 72 |
+
The cyclers to be composed.
|
| 73 |
+
|
| 74 |
+
Returns
|
| 75 |
+
-------
|
| 76 |
+
keys : set
|
| 77 |
+
The keys in the composition of the two cyclers.
|
| 78 |
+
"""
|
| 79 |
+
l_peek: dict[K, V] = next(iter(left)) if left != [] else {}
|
| 80 |
+
r_peek: dict[K, V] = next(iter(right)) if right is not None else {}
|
| 81 |
+
l_key: set[K] = set(l_peek.keys())
|
| 82 |
+
r_key: set[K] = set(r_peek.keys())
|
| 83 |
+
if l_key & r_key:
|
| 84 |
+
raise ValueError("Can not compose overlapping cycles")
|
| 85 |
+
return l_key | r_key
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
def concat(left: Cycler[K, V], right: Cycler[K, U]) -> Cycler[K, V | U]:
|
| 89 |
+
r"""
|
| 90 |
+
Concatenate `Cycler`\s, as if chained using `itertools.chain`.
|
| 91 |
+
|
| 92 |
+
The keys must match exactly.
|
| 93 |
+
|
| 94 |
+
Examples
|
| 95 |
+
--------
|
| 96 |
+
>>> num = cycler('a', range(3))
|
| 97 |
+
>>> let = cycler('a', 'abc')
|
| 98 |
+
>>> num.concat(let)
|
| 99 |
+
cycler('a', [0, 1, 2, 'a', 'b', 'c'])
|
| 100 |
+
|
| 101 |
+
Returns
|
| 102 |
+
-------
|
| 103 |
+
`Cycler`
|
| 104 |
+
The concatenated cycler.
|
| 105 |
+
"""
|
| 106 |
+
if left.keys != right.keys:
|
| 107 |
+
raise ValueError(
|
| 108 |
+
"Keys do not match:\n"
|
| 109 |
+
"\tIntersection: {both!r}\n"
|
| 110 |
+
"\tDisjoint: {just_one!r}".format(
|
| 111 |
+
both=left.keys & right.keys, just_one=left.keys ^ right.keys
|
| 112 |
+
)
|
| 113 |
+
)
|
| 114 |
+
_l = cast(Dict[K, List[Union[V, U]]], left.by_key())
|
| 115 |
+
_r = cast(Dict[K, List[Union[V, U]]], right.by_key())
|
| 116 |
+
return reduce(add, (_cycler(k, _l[k] + _r[k]) for k in left.keys))
|
| 117 |
+
|
| 118 |
+
|
| 119 |
+
class Cycler(Generic[K, V]):
|
| 120 |
+
"""
|
| 121 |
+
Composable cycles.
|
| 122 |
+
|
| 123 |
+
This class has compositions methods:
|
| 124 |
+
|
| 125 |
+
``+``
|
| 126 |
+
for 'inner' products (zip)
|
| 127 |
+
|
| 128 |
+
``+=``
|
| 129 |
+
in-place ``+``
|
| 130 |
+
|
| 131 |
+
``*``
|
| 132 |
+
for outer products (`itertools.product`) and integer multiplication
|
| 133 |
+
|
| 134 |
+
``*=``
|
| 135 |
+
in-place ``*``
|
| 136 |
+
|
| 137 |
+
and supports basic slicing via ``[]``.
|
| 138 |
+
|
| 139 |
+
Parameters
|
| 140 |
+
----------
|
| 141 |
+
left, right : Cycler or None
|
| 142 |
+
The 'left' and 'right' cyclers.
|
| 143 |
+
op : func or None
|
| 144 |
+
Function which composes the 'left' and 'right' cyclers.
|
| 145 |
+
"""
|
| 146 |
+
|
| 147 |
+
def __call__(self):
|
| 148 |
+
return cycle(self)
|
| 149 |
+
|
| 150 |
+
def __init__(
|
| 151 |
+
self,
|
| 152 |
+
left: Cycler[K, V] | Iterable[dict[K, V]] | None,
|
| 153 |
+
right: Cycler[K, V] | None = None,
|
| 154 |
+
op: Any = None,
|
| 155 |
+
):
|
| 156 |
+
"""
|
| 157 |
+
Semi-private init.
|
| 158 |
+
|
| 159 |
+
Do not use this directly, use `cycler` function instead.
|
| 160 |
+
"""
|
| 161 |
+
if isinstance(left, Cycler):
|
| 162 |
+
self._left: Cycler[K, V] | list[dict[K, V]] = Cycler(
|
| 163 |
+
left._left, left._right, left._op
|
| 164 |
+
)
|
| 165 |
+
elif left is not None:
|
| 166 |
+
# Need to copy the dictionary or else that will be a residual
|
| 167 |
+
# mutable that could lead to strange errors
|
| 168 |
+
self._left = [copy.copy(v) for v in left]
|
| 169 |
+
else:
|
| 170 |
+
self._left = []
|
| 171 |
+
|
| 172 |
+
if isinstance(right, Cycler):
|
| 173 |
+
self._right: Cycler[K, V] | None = Cycler(
|
| 174 |
+
right._left, right._right, right._op
|
| 175 |
+
)
|
| 176 |
+
else:
|
| 177 |
+
self._right = None
|
| 178 |
+
|
| 179 |
+
self._keys: set[K] = _process_keys(self._left, self._right)
|
| 180 |
+
self._op: Any = op
|
| 181 |
+
|
| 182 |
+
def __contains__(self, k):
|
| 183 |
+
return k in self._keys
|
| 184 |
+
|
| 185 |
+
@property
|
| 186 |
+
def keys(self) -> set[K]:
|
| 187 |
+
"""The keys this Cycler knows about."""
|
| 188 |
+
return set(self._keys)
|
| 189 |
+
|
| 190 |
+
def change_key(self, old: K, new: K) -> None:
|
| 191 |
+
"""
|
| 192 |
+
Change a key in this cycler to a new name.
|
| 193 |
+
Modification is performed in-place.
|
| 194 |
+
|
| 195 |
+
Does nothing if the old key is the same as the new key.
|
| 196 |
+
Raises a ValueError if the new key is already a key.
|
| 197 |
+
Raises a KeyError if the old key isn't a key.
|
| 198 |
+
"""
|
| 199 |
+
if old == new:
|
| 200 |
+
return
|
| 201 |
+
if new in self._keys:
|
| 202 |
+
raise ValueError(
|
| 203 |
+
f"Can't replace {old} with {new}, {new} is already a key"
|
| 204 |
+
)
|
| 205 |
+
if old not in self._keys:
|
| 206 |
+
raise KeyError(
|
| 207 |
+
f"Can't replace {old} with {new}, {old} is not a key"
|
| 208 |
+
)
|
| 209 |
+
|
| 210 |
+
self._keys.remove(old)
|
| 211 |
+
self._keys.add(new)
|
| 212 |
+
|
| 213 |
+
if self._right is not None and old in self._right.keys:
|
| 214 |
+
self._right.change_key(old, new)
|
| 215 |
+
|
| 216 |
+
# self._left should always be non-None
|
| 217 |
+
# if self._keys is non-empty.
|
| 218 |
+
elif isinstance(self._left, Cycler):
|
| 219 |
+
self._left.change_key(old, new)
|
| 220 |
+
else:
|
| 221 |
+
# It should be completely safe at this point to
|
| 222 |
+
# assume that the old key can be found in each
|
| 223 |
+
# iteration.
|
| 224 |
+
self._left = [{new: entry[old]} for entry in self._left]
|
| 225 |
+
|
| 226 |
+
@classmethod
|
| 227 |
+
def _from_iter(cls, label: K, itr: Iterable[V]) -> Cycler[K, V]:
|
| 228 |
+
"""
|
| 229 |
+
Class method to create 'base' Cycler objects
|
| 230 |
+
that do not have a 'right' or 'op' and for which
|
| 231 |
+
the 'left' object is not another Cycler.
|
| 232 |
+
|
| 233 |
+
Parameters
|
| 234 |
+
----------
|
| 235 |
+
label : hashable
|
| 236 |
+
The property key.
|
| 237 |
+
|
| 238 |
+
itr : iterable
|
| 239 |
+
Finite length iterable of the property values.
|
| 240 |
+
|
| 241 |
+
Returns
|
| 242 |
+
-------
|
| 243 |
+
`Cycler`
|
| 244 |
+
New 'base' cycler.
|
| 245 |
+
"""
|
| 246 |
+
ret: Cycler[K, V] = cls(None)
|
| 247 |
+
ret._left = list({label: v} for v in itr)
|
| 248 |
+
ret._keys = {label}
|
| 249 |
+
return ret
|
| 250 |
+
|
| 251 |
+
def __getitem__(self, key: slice) -> Cycler[K, V]:
|
| 252 |
+
# TODO : maybe add numpy style fancy slicing
|
| 253 |
+
if isinstance(key, slice):
|
| 254 |
+
trans = self.by_key()
|
| 255 |
+
return reduce(add, (_cycler(k, v[key]) for k, v in trans.items()))
|
| 256 |
+
else:
|
| 257 |
+
raise ValueError("Can only use slices with Cycler.__getitem__")
|
| 258 |
+
|
| 259 |
+
def __iter__(self) -> Generator[dict[K, V], None, None]:
|
| 260 |
+
if self._right is None:
|
| 261 |
+
for left in self._left:
|
| 262 |
+
yield dict(left)
|
| 263 |
+
else:
|
| 264 |
+
if self._op is None:
|
| 265 |
+
raise TypeError(
|
| 266 |
+
"Operation cannot be None when both left and right are defined"
|
| 267 |
+
)
|
| 268 |
+
for a, b in self._op(self._left, self._right):
|
| 269 |
+
out = {}
|
| 270 |
+
out.update(a)
|
| 271 |
+
out.update(b)
|
| 272 |
+
yield out
|
| 273 |
+
|
| 274 |
+
def __add__(self, other: Cycler[L, U]) -> Cycler[K | L, V | U]:
|
| 275 |
+
"""
|
| 276 |
+
Pair-wise combine two equal length cyclers (zip).
|
| 277 |
+
|
| 278 |
+
Parameters
|
| 279 |
+
----------
|
| 280 |
+
other : Cycler
|
| 281 |
+
"""
|
| 282 |
+
if len(self) != len(other):
|
| 283 |
+
raise ValueError(
|
| 284 |
+
f"Can only add equal length cycles, not {len(self)} and {len(other)}"
|
| 285 |
+
)
|
| 286 |
+
return Cycler(
|
| 287 |
+
cast(Cycler[Union[K, L], Union[V, U]], self),
|
| 288 |
+
cast(Cycler[Union[K, L], Union[V, U]], other),
|
| 289 |
+
zip
|
| 290 |
+
)
|
| 291 |
+
|
| 292 |
+
@overload
|
| 293 |
+
def __mul__(self, other: Cycler[L, U]) -> Cycler[K | L, V | U]:
|
| 294 |
+
...
|
| 295 |
+
|
| 296 |
+
@overload
|
| 297 |
+
def __mul__(self, other: int) -> Cycler[K, V]:
|
| 298 |
+
...
|
| 299 |
+
|
| 300 |
+
def __mul__(self, other):
|
| 301 |
+
"""
|
| 302 |
+
Outer product of two cyclers (`itertools.product`) or integer
|
| 303 |
+
multiplication.
|
| 304 |
+
|
| 305 |
+
Parameters
|
| 306 |
+
----------
|
| 307 |
+
other : Cycler or int
|
| 308 |
+
"""
|
| 309 |
+
if isinstance(other, Cycler):
|
| 310 |
+
return Cycler(
|
| 311 |
+
cast(Cycler[Union[K, L], Union[V, U]], self),
|
| 312 |
+
cast(Cycler[Union[K, L], Union[V, U]], other),
|
| 313 |
+
product
|
| 314 |
+
)
|
| 315 |
+
elif isinstance(other, int):
|
| 316 |
+
trans = self.by_key()
|
| 317 |
+
return reduce(
|
| 318 |
+
add, (_cycler(k, v * other) for k, v in trans.items())
|
| 319 |
+
)
|
| 320 |
+
else:
|
| 321 |
+
return NotImplemented
|
| 322 |
+
|
| 323 |
+
@overload
|
| 324 |
+
def __rmul__(self, other: Cycler[L, U]) -> Cycler[K | L, V | U]:
|
| 325 |
+
...
|
| 326 |
+
|
| 327 |
+
@overload
|
| 328 |
+
def __rmul__(self, other: int) -> Cycler[K, V]:
|
| 329 |
+
...
|
| 330 |
+
|
| 331 |
+
def __rmul__(self, other):
|
| 332 |
+
return self * other
|
| 333 |
+
|
| 334 |
+
def __len__(self) -> int:
|
| 335 |
+
op_dict: dict[Callable, Callable[[int, int], int]] = {zip: min, product: mul}
|
| 336 |
+
if self._right is None:
|
| 337 |
+
return len(self._left)
|
| 338 |
+
l_len = len(self._left)
|
| 339 |
+
r_len = len(self._right)
|
| 340 |
+
return op_dict[self._op](l_len, r_len)
|
| 341 |
+
|
| 342 |
+
# iadd and imul do not exapand the the type as the returns must be consistent with
|
| 343 |
+
# self, thus they flag as inconsistent with add/mul
|
| 344 |
+
def __iadd__(self, other: Cycler[K, V]) -> Cycler[K, V]: # type: ignore[misc]
|
| 345 |
+
"""
|
| 346 |
+
In-place pair-wise combine two equal length cyclers (zip).
|
| 347 |
+
|
| 348 |
+
Parameters
|
| 349 |
+
----------
|
| 350 |
+
other : Cycler
|
| 351 |
+
"""
|
| 352 |
+
if not isinstance(other, Cycler):
|
| 353 |
+
raise TypeError("Cannot += with a non-Cycler object")
|
| 354 |
+
# True shallow copy of self is fine since this is in-place
|
| 355 |
+
old_self = copy.copy(self)
|
| 356 |
+
self._keys = _process_keys(old_self, other)
|
| 357 |
+
self._left = old_self
|
| 358 |
+
self._op = zip
|
| 359 |
+
self._right = Cycler(other._left, other._right, other._op)
|
| 360 |
+
return self
|
| 361 |
+
|
| 362 |
+
def __imul__(self, other: Cycler[K, V] | int) -> Cycler[K, V]: # type: ignore[misc]
|
| 363 |
+
"""
|
| 364 |
+
In-place outer product of two cyclers (`itertools.product`).
|
| 365 |
+
|
| 366 |
+
Parameters
|
| 367 |
+
----------
|
| 368 |
+
other : Cycler
|
| 369 |
+
"""
|
| 370 |
+
if not isinstance(other, Cycler):
|
| 371 |
+
raise TypeError("Cannot *= with a non-Cycler object")
|
| 372 |
+
# True shallow copy of self is fine since this is in-place
|
| 373 |
+
old_self = copy.copy(self)
|
| 374 |
+
self._keys = _process_keys(old_self, other)
|
| 375 |
+
self._left = old_self
|
| 376 |
+
self._op = product
|
| 377 |
+
self._right = Cycler(other._left, other._right, other._op)
|
| 378 |
+
return self
|
| 379 |
+
|
| 380 |
+
def __eq__(self, other: object) -> bool:
|
| 381 |
+
if not isinstance(other, Cycler):
|
| 382 |
+
return False
|
| 383 |
+
if len(self) != len(other):
|
| 384 |
+
return False
|
| 385 |
+
if self.keys ^ other.keys:
|
| 386 |
+
return False
|
| 387 |
+
return all(a == b for a, b in zip(self, other))
|
| 388 |
+
|
| 389 |
+
__hash__ = None # type: ignore
|
| 390 |
+
|
| 391 |
+
def __repr__(self) -> str:
|
| 392 |
+
op_map = {zip: "+", product: "*"}
|
| 393 |
+
if self._right is None:
|
| 394 |
+
lab = self.keys.pop()
|
| 395 |
+
itr = list(v[lab] for v in self)
|
| 396 |
+
return f"cycler({lab!r}, {itr!r})"
|
| 397 |
+
else:
|
| 398 |
+
op = op_map.get(self._op, "?")
|
| 399 |
+
msg = "({left!r} {op} {right!r})"
|
| 400 |
+
return msg.format(left=self._left, op=op, right=self._right)
|
| 401 |
+
|
| 402 |
+
def _repr_html_(self) -> str:
|
| 403 |
+
# an table showing the value of each key through a full cycle
|
| 404 |
+
output = "<table>"
|
| 405 |
+
sorted_keys = sorted(self.keys, key=repr)
|
| 406 |
+
for key in sorted_keys:
|
| 407 |
+
output += f"<th>{key!r}</th>"
|
| 408 |
+
for d in iter(self):
|
| 409 |
+
output += "<tr>"
|
| 410 |
+
for k in sorted_keys:
|
| 411 |
+
output += f"<td>{d[k]!r}</td>"
|
| 412 |
+
output += "</tr>"
|
| 413 |
+
output += "</table>"
|
| 414 |
+
return output
|
| 415 |
+
|
| 416 |
+
def by_key(self) -> dict[K, list[V]]:
|
| 417 |
+
"""
|
| 418 |
+
Values by key.
|
| 419 |
+
|
| 420 |
+
This returns the transposed values of the cycler. Iterating
|
| 421 |
+
over a `Cycler` yields dicts with a single value for each key,
|
| 422 |
+
this method returns a `dict` of `list` which are the values
|
| 423 |
+
for the given key.
|
| 424 |
+
|
| 425 |
+
The returned value can be used to create an equivalent `Cycler`
|
| 426 |
+
using only `+`.
|
| 427 |
+
|
| 428 |
+
Returns
|
| 429 |
+
-------
|
| 430 |
+
transpose : dict
|
| 431 |
+
dict of lists of the values for each key.
|
| 432 |
+
"""
|
| 433 |
+
|
| 434 |
+
# TODO : sort out if this is a bottle neck, if there is a better way
|
| 435 |
+
# and if we care.
|
| 436 |
+
|
| 437 |
+
keys = self.keys
|
| 438 |
+
out: dict[K, list[V]] = {k: list() for k in keys}
|
| 439 |
+
|
| 440 |
+
for d in self:
|
| 441 |
+
for k in keys:
|
| 442 |
+
out[k].append(d[k])
|
| 443 |
+
return out
|
| 444 |
+
|
| 445 |
+
# for back compatibility
|
| 446 |
+
_transpose = by_key
|
| 447 |
+
|
| 448 |
+
def simplify(self) -> Cycler[K, V]:
|
| 449 |
+
"""
|
| 450 |
+
Simplify the cycler into a sum (but no products) of cyclers.
|
| 451 |
+
|
| 452 |
+
Returns
|
| 453 |
+
-------
|
| 454 |
+
simple : Cycler
|
| 455 |
+
"""
|
| 456 |
+
# TODO: sort out if it is worth the effort to make sure this is
|
| 457 |
+
# balanced. Currently it is is
|
| 458 |
+
# (((a + b) + c) + d) vs
|
| 459 |
+
# ((a + b) + (c + d))
|
| 460 |
+
# I would believe that there is some performance implications
|
| 461 |
+
trans = self.by_key()
|
| 462 |
+
return reduce(add, (_cycler(k, v) for k, v in trans.items()))
|
| 463 |
+
|
| 464 |
+
concat = concat
|
| 465 |
+
|
| 466 |
+
|
| 467 |
+
@overload
|
| 468 |
+
def cycler(arg: Cycler[K, V]) -> Cycler[K, V]:
|
| 469 |
+
...
|
| 470 |
+
|
| 471 |
+
|
| 472 |
+
@overload
|
| 473 |
+
def cycler(**kwargs: Iterable[V]) -> Cycler[str, V]:
|
| 474 |
+
...
|
| 475 |
+
|
| 476 |
+
|
| 477 |
+
@overload
|
| 478 |
+
def cycler(label: K, itr: Iterable[V]) -> Cycler[K, V]:
|
| 479 |
+
...
|
| 480 |
+
|
| 481 |
+
|
| 482 |
+
def cycler(*args, **kwargs):
|
| 483 |
+
"""
|
| 484 |
+
Create a new `Cycler` object from a single positional argument,
|
| 485 |
+
a pair of positional arguments, or the combination of keyword arguments.
|
| 486 |
+
|
| 487 |
+
cycler(arg)
|
| 488 |
+
cycler(label1=itr1[, label2=iter2[, ...]])
|
| 489 |
+
cycler(label, itr)
|
| 490 |
+
|
| 491 |
+
Form 1 simply copies a given `Cycler` object.
|
| 492 |
+
|
| 493 |
+
Form 2 composes a `Cycler` as an inner product of the
|
| 494 |
+
pairs of keyword arguments. In other words, all of the
|
| 495 |
+
iterables are cycled simultaneously, as if through zip().
|
| 496 |
+
|
| 497 |
+
Form 3 creates a `Cycler` from a label and an iterable.
|
| 498 |
+
This is useful for when the label cannot be a keyword argument
|
| 499 |
+
(e.g., an integer or a name that has a space in it).
|
| 500 |
+
|
| 501 |
+
Parameters
|
| 502 |
+
----------
|
| 503 |
+
arg : Cycler
|
| 504 |
+
Copy constructor for Cycler (does a shallow copy of iterables).
|
| 505 |
+
label : name
|
| 506 |
+
The property key. In the 2-arg form of the function,
|
| 507 |
+
the label can be any hashable object. In the keyword argument
|
| 508 |
+
form of the function, it must be a valid python identifier.
|
| 509 |
+
itr : iterable
|
| 510 |
+
Finite length iterable of the property values.
|
| 511 |
+
Can be a single-property `Cycler` that would
|
| 512 |
+
be like a key change, but as a shallow copy.
|
| 513 |
+
|
| 514 |
+
Returns
|
| 515 |
+
-------
|
| 516 |
+
cycler : Cycler
|
| 517 |
+
New `Cycler` for the given property
|
| 518 |
+
|
| 519 |
+
"""
|
| 520 |
+
if args and kwargs:
|
| 521 |
+
raise TypeError(
|
| 522 |
+
"cycler() can only accept positional OR keyword arguments -- not both."
|
| 523 |
+
)
|
| 524 |
+
|
| 525 |
+
if len(args) == 1:
|
| 526 |
+
if not isinstance(args[0], Cycler):
|
| 527 |
+
raise TypeError(
|
| 528 |
+
"If only one positional argument given, it must "
|
| 529 |
+
"be a Cycler instance."
|
| 530 |
+
)
|
| 531 |
+
return Cycler(args[0])
|
| 532 |
+
elif len(args) == 2:
|
| 533 |
+
return _cycler(*args)
|
| 534 |
+
elif len(args) > 2:
|
| 535 |
+
raise TypeError(
|
| 536 |
+
"Only a single Cycler can be accepted as the lone "
|
| 537 |
+
"positional argument. Use keyword arguments instead."
|
| 538 |
+
)
|
| 539 |
+
|
| 540 |
+
if kwargs:
|
| 541 |
+
return reduce(add, (_cycler(k, v) for k, v in kwargs.items()))
|
| 542 |
+
|
| 543 |
+
raise TypeError("Must have at least a positional OR keyword arguments")
|
| 544 |
+
|
| 545 |
+
|
| 546 |
+
def _cycler(label: K, itr: Iterable[V]) -> Cycler[K, V]:
|
| 547 |
+
"""
|
| 548 |
+
Create a new `Cycler` object from a property name and iterable of values.
|
| 549 |
+
|
| 550 |
+
Parameters
|
| 551 |
+
----------
|
| 552 |
+
label : hashable
|
| 553 |
+
The property key.
|
| 554 |
+
itr : iterable
|
| 555 |
+
Finite length iterable of the property values.
|
| 556 |
+
|
| 557 |
+
Returns
|
| 558 |
+
-------
|
| 559 |
+
cycler : Cycler
|
| 560 |
+
New `Cycler` for the given property
|
| 561 |
+
"""
|
| 562 |
+
if isinstance(itr, Cycler):
|
| 563 |
+
keys = itr.keys
|
| 564 |
+
if len(keys) != 1:
|
| 565 |
+
msg = "Can not create Cycler from a multi-property Cycler"
|
| 566 |
+
raise ValueError(msg)
|
| 567 |
+
|
| 568 |
+
lab = keys.pop()
|
| 569 |
+
# Doesn't need to be a new list because
|
| 570 |
+
# _from_iter() will be creating that new list anyway.
|
| 571 |
+
itr = (v[lab] for v in itr)
|
| 572 |
+
|
| 573 |
+
return Cycler._from_iter(label, itr)
|
lib/python3.12/site-packages/cycler/__pycache__/__init__.cpython-312.pyc
ADDED
|
Binary file (22.9 kB). View file
|
|
|
lib/python3.12/site-packages/cycler/py.typed
ADDED
|
File without changes
|
lib/python3.12/site-packages/frozenlist/__init__.py
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import types
|
| 3 |
+
from collections.abc import MutableSequence
|
| 4 |
+
from functools import total_ordering
|
| 5 |
+
|
| 6 |
+
__version__ = "1.8.0"
|
| 7 |
+
|
| 8 |
+
__all__ = ("FrozenList", "PyFrozenList") # type: Tuple[str, ...]
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
NO_EXTENSIONS = bool(os.environ.get("FROZENLIST_NO_EXTENSIONS")) # type: bool
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
@total_ordering
|
| 15 |
+
class FrozenList(MutableSequence):
|
| 16 |
+
__slots__ = ("_frozen", "_items")
|
| 17 |
+
__class_getitem__ = classmethod(types.GenericAlias)
|
| 18 |
+
|
| 19 |
+
def __init__(self, items=None):
|
| 20 |
+
self._frozen = False
|
| 21 |
+
if items is not None:
|
| 22 |
+
items = list(items)
|
| 23 |
+
else:
|
| 24 |
+
items = []
|
| 25 |
+
self._items = items
|
| 26 |
+
|
| 27 |
+
@property
|
| 28 |
+
def frozen(self):
|
| 29 |
+
return self._frozen
|
| 30 |
+
|
| 31 |
+
def freeze(self):
|
| 32 |
+
self._frozen = True
|
| 33 |
+
|
| 34 |
+
def __getitem__(self, index):
|
| 35 |
+
return self._items[index]
|
| 36 |
+
|
| 37 |
+
def __setitem__(self, index, value):
|
| 38 |
+
if self._frozen:
|
| 39 |
+
raise RuntimeError("Cannot modify frozen list.")
|
| 40 |
+
self._items[index] = value
|
| 41 |
+
|
| 42 |
+
def __delitem__(self, index):
|
| 43 |
+
if self._frozen:
|
| 44 |
+
raise RuntimeError("Cannot modify frozen list.")
|
| 45 |
+
del self._items[index]
|
| 46 |
+
|
| 47 |
+
def __len__(self):
|
| 48 |
+
return self._items.__len__()
|
| 49 |
+
|
| 50 |
+
def __iter__(self):
|
| 51 |
+
return self._items.__iter__()
|
| 52 |
+
|
| 53 |
+
def __reversed__(self):
|
| 54 |
+
return self._items.__reversed__()
|
| 55 |
+
|
| 56 |
+
def __eq__(self, other):
|
| 57 |
+
return list(self) == other
|
| 58 |
+
|
| 59 |
+
def __le__(self, other):
|
| 60 |
+
return list(self) <= other
|
| 61 |
+
|
| 62 |
+
def insert(self, pos, item):
|
| 63 |
+
if self._frozen:
|
| 64 |
+
raise RuntimeError("Cannot modify frozen list.")
|
| 65 |
+
self._items.insert(pos, item)
|
| 66 |
+
|
| 67 |
+
def __repr__(self):
|
| 68 |
+
return f"<FrozenList(frozen={self._frozen}, {self._items!r})>"
|
| 69 |
+
|
| 70 |
+
def __hash__(self):
|
| 71 |
+
if self._frozen:
|
| 72 |
+
return hash(tuple(self))
|
| 73 |
+
else:
|
| 74 |
+
raise RuntimeError("Cannot hash unfrozen list.")
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
PyFrozenList = FrozenList
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
if not NO_EXTENSIONS:
|
| 81 |
+
try:
|
| 82 |
+
from ._frozenlist import FrozenList as CFrozenList # type: ignore
|
| 83 |
+
except ImportError: # pragma: no cover
|
| 84 |
+
pass
|
| 85 |
+
else:
|
| 86 |
+
FrozenList = CFrozenList # type: ignore
|
lib/python3.12/site-packages/frozenlist/__init__.pyi
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import (
|
| 2 |
+
Generic,
|
| 3 |
+
Iterable,
|
| 4 |
+
Iterator,
|
| 5 |
+
List,
|
| 6 |
+
MutableSequence,
|
| 7 |
+
Optional,
|
| 8 |
+
TypeVar,
|
| 9 |
+
Union,
|
| 10 |
+
overload,
|
| 11 |
+
)
|
| 12 |
+
|
| 13 |
+
_T = TypeVar("_T")
|
| 14 |
+
_Arg = Union[List[_T], Iterable[_T]]
|
| 15 |
+
|
| 16 |
+
class FrozenList(MutableSequence[_T], Generic[_T]):
|
| 17 |
+
def __init__(self, items: Optional[_Arg[_T]] = None) -> None: ...
|
| 18 |
+
@property
|
| 19 |
+
def frozen(self) -> bool: ...
|
| 20 |
+
def freeze(self) -> None: ...
|
| 21 |
+
@overload
|
| 22 |
+
def __getitem__(self, i: int) -> _T: ...
|
| 23 |
+
@overload
|
| 24 |
+
def __getitem__(self, s: slice) -> FrozenList[_T]: ...
|
| 25 |
+
@overload
|
| 26 |
+
def __setitem__(self, i: int, o: _T) -> None: ...
|
| 27 |
+
@overload
|
| 28 |
+
def __setitem__(self, s: slice, o: Iterable[_T]) -> None: ...
|
| 29 |
+
@overload
|
| 30 |
+
def __delitem__(self, i: int) -> None: ...
|
| 31 |
+
@overload
|
| 32 |
+
def __delitem__(self, i: slice) -> None: ...
|
| 33 |
+
def __len__(self) -> int: ...
|
| 34 |
+
def __iter__(self) -> Iterator[_T]: ...
|
| 35 |
+
def __reversed__(self) -> Iterator[_T]: ...
|
| 36 |
+
def __eq__(self, other: object) -> bool: ...
|
| 37 |
+
def __le__(self, other: FrozenList[_T]) -> bool: ...
|
| 38 |
+
def __ne__(self, other: object) -> bool: ...
|
| 39 |
+
def __lt__(self, other: FrozenList[_T]) -> bool: ...
|
| 40 |
+
def __ge__(self, other: FrozenList[_T]) -> bool: ...
|
| 41 |
+
def __gt__(self, other: FrozenList[_T]) -> bool: ...
|
| 42 |
+
def insert(self, pos: int, item: _T) -> None: ...
|
| 43 |
+
def __repr__(self) -> str: ...
|
| 44 |
+
def __hash__(self) -> int: ...
|
| 45 |
+
|
| 46 |
+
# types for C accelerators are the same
|
| 47 |
+
CFrozenList = PyFrozenList = FrozenList
|
lib/python3.12/site-packages/frozenlist/__pycache__/__init__.cpython-312.pyc
ADDED
|
Binary file (4.07 kB). View file
|
|
|
lib/python3.12/site-packages/frozenlist/_frozenlist.pyx
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# cython: freethreading_compatible = True
|
| 2 |
+
# distutils: language = c++
|
| 3 |
+
|
| 4 |
+
from cpython.bool cimport PyBool_FromLong
|
| 5 |
+
from libcpp.atomic cimport atomic
|
| 6 |
+
|
| 7 |
+
import copy
|
| 8 |
+
import types
|
| 9 |
+
from collections.abc import MutableSequence
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
cdef class FrozenList:
|
| 13 |
+
__class_getitem__ = classmethod(types.GenericAlias)
|
| 14 |
+
|
| 15 |
+
cdef atomic[bint] _frozen
|
| 16 |
+
cdef list _items
|
| 17 |
+
|
| 18 |
+
def __init__(self, items=None):
|
| 19 |
+
self._frozen.store(False)
|
| 20 |
+
if items is not None:
|
| 21 |
+
items = list(items)
|
| 22 |
+
else:
|
| 23 |
+
items = []
|
| 24 |
+
self._items = items
|
| 25 |
+
|
| 26 |
+
@property
|
| 27 |
+
def frozen(self):
|
| 28 |
+
return PyBool_FromLong(self._frozen.load())
|
| 29 |
+
|
| 30 |
+
cdef object _check_frozen(self):
|
| 31 |
+
if self._frozen.load():
|
| 32 |
+
raise RuntimeError("Cannot modify frozen list.")
|
| 33 |
+
|
| 34 |
+
cdef inline object _fast_len(self):
|
| 35 |
+
return len(self._items)
|
| 36 |
+
|
| 37 |
+
def freeze(self):
|
| 38 |
+
self._frozen.store(True)
|
| 39 |
+
|
| 40 |
+
def __getitem__(self, index):
|
| 41 |
+
return self._items[index]
|
| 42 |
+
|
| 43 |
+
def __setitem__(self, index, value):
|
| 44 |
+
self._check_frozen()
|
| 45 |
+
self._items[index] = value
|
| 46 |
+
|
| 47 |
+
def __delitem__(self, index):
|
| 48 |
+
self._check_frozen()
|
| 49 |
+
del self._items[index]
|
| 50 |
+
|
| 51 |
+
def __len__(self):
|
| 52 |
+
return self._fast_len()
|
| 53 |
+
|
| 54 |
+
def __iter__(self):
|
| 55 |
+
return self._items.__iter__()
|
| 56 |
+
|
| 57 |
+
def __reversed__(self):
|
| 58 |
+
return self._items.__reversed__()
|
| 59 |
+
|
| 60 |
+
def __richcmp__(self, other, op):
|
| 61 |
+
if op == 0: # <
|
| 62 |
+
return list(self) < other
|
| 63 |
+
if op == 1: # <=
|
| 64 |
+
return list(self) <= other
|
| 65 |
+
if op == 2: # ==
|
| 66 |
+
return list(self) == other
|
| 67 |
+
if op == 3: # !=
|
| 68 |
+
return list(self) != other
|
| 69 |
+
if op == 4: # >
|
| 70 |
+
return list(self) > other
|
| 71 |
+
if op == 5: # =>
|
| 72 |
+
return list(self) >= other
|
| 73 |
+
|
| 74 |
+
def insert(self, pos, item):
|
| 75 |
+
self._check_frozen()
|
| 76 |
+
self._items.insert(pos, item)
|
| 77 |
+
|
| 78 |
+
def __contains__(self, item):
|
| 79 |
+
return item in self._items
|
| 80 |
+
|
| 81 |
+
def __iadd__(self, items):
|
| 82 |
+
self._check_frozen()
|
| 83 |
+
self._items += list(items)
|
| 84 |
+
return self
|
| 85 |
+
|
| 86 |
+
def index(self, item):
|
| 87 |
+
return self._items.index(item)
|
| 88 |
+
|
| 89 |
+
def remove(self, item):
|
| 90 |
+
self._check_frozen()
|
| 91 |
+
self._items.remove(item)
|
| 92 |
+
|
| 93 |
+
def clear(self):
|
| 94 |
+
self._check_frozen()
|
| 95 |
+
self._items.clear()
|
| 96 |
+
|
| 97 |
+
def extend(self, items):
|
| 98 |
+
self._check_frozen()
|
| 99 |
+
self._items += list(items)
|
| 100 |
+
|
| 101 |
+
def reverse(self):
|
| 102 |
+
self._check_frozen()
|
| 103 |
+
self._items.reverse()
|
| 104 |
+
|
| 105 |
+
def pop(self, index=-1):
|
| 106 |
+
self._check_frozen()
|
| 107 |
+
return self._items.pop(index)
|
| 108 |
+
|
| 109 |
+
def append(self, item):
|
| 110 |
+
self._check_frozen()
|
| 111 |
+
return self._items.append(item)
|
| 112 |
+
|
| 113 |
+
def count(self, item):
|
| 114 |
+
return self._items.count(item)
|
| 115 |
+
|
| 116 |
+
def __repr__(self):
|
| 117 |
+
return '<FrozenList(frozen={}, {!r})>'.format(self._frozen.load(),
|
| 118 |
+
self._items)
|
| 119 |
+
|
| 120 |
+
def __hash__(self):
|
| 121 |
+
if self._frozen.load():
|
| 122 |
+
return hash(tuple(self._items))
|
| 123 |
+
else:
|
| 124 |
+
raise RuntimeError("Cannot hash unfrozen list.")
|
| 125 |
+
|
| 126 |
+
def __deepcopy__(self, memo):
|
| 127 |
+
cdef FrozenList new_list
|
| 128 |
+
obj_id = id(self)
|
| 129 |
+
|
| 130 |
+
# Return existing copy if already processed (circular reference)
|
| 131 |
+
if obj_id in memo:
|
| 132 |
+
return memo[obj_id]
|
| 133 |
+
|
| 134 |
+
# Create new instance and register immediately
|
| 135 |
+
new_list = self.__class__([])
|
| 136 |
+
memo[obj_id] = new_list
|
| 137 |
+
|
| 138 |
+
# Deep copy items
|
| 139 |
+
new_list._items[:] = [copy.deepcopy(item, memo) for item in self._items]
|
| 140 |
+
|
| 141 |
+
# Preserve frozen state
|
| 142 |
+
if self._frozen.load():
|
| 143 |
+
new_list.freeze()
|
| 144 |
+
|
| 145 |
+
return new_list
|
| 146 |
+
|
| 147 |
+
|
| 148 |
+
MutableSequence.register(FrozenList)
|
lib/python3.12/site-packages/frozenlist/py.typed
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
Marker
|
lib/python3.12/site-packages/hjson-3.1.0.dist-info/INSTALLER
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
pip
|
lib/python3.12/site-packages/hjson-3.1.0.dist-info/LICENSE.txt
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
hjson (based on simplejson) is dual-licensed software. It is available under the terms
|
| 2 |
+
of the MIT license, or the Academic Free License version 2.1. The full
|
| 3 |
+
text of each license agreement is included below. This code is also
|
| 4 |
+
licensed to the Python Software Foundation (PSF) under a Contributor
|
| 5 |
+
Agreement.
|
| 6 |
+
|
| 7 |
+
MIT License
|
| 8 |
+
===========
|
| 9 |
+
|
| 10 |
+
Copyright (c) 2006 Bob Ippolito
|
| 11 |
+
Copyright (c) 2015 Christian Zangl
|
| 12 |
+
|
| 13 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
| 14 |
+
this software and associated documentation files (the "Software"), to deal in
|
| 15 |
+
the Software without restriction, including without limitation the rights to
|
| 16 |
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
| 17 |
+
of the Software, and to permit persons to whom the Software is furnished to do
|
| 18 |
+
so, subject to the following conditions:
|
| 19 |
+
|
| 20 |
+
The above copyright notice and this permission notice shall be included in all
|
| 21 |
+
copies or substantial portions of the Software.
|
| 22 |
+
|
| 23 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 24 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 25 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 26 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 27 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 28 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 29 |
+
SOFTWARE.
|
| 30 |
+
|
| 31 |
+
Academic Free License v. 2.1
|
| 32 |
+
============================
|
| 33 |
+
|
| 34 |
+
Copyright (c) 2006 Bob Ippolito. All rights reserved.
|
| 35 |
+
Copyright (c) 2015 Christian Zangl
|
| 36 |
+
|
| 37 |
+
This Academic Free License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following notice immediately following the copyright notice for the Original Work:
|
| 38 |
+
|
| 39 |
+
Licensed under the Academic Free License version 2.1
|
| 40 |
+
|
| 41 |
+
1) Grant of Copyright License. Licensor hereby grants You a world-wide, royalty-free, non-exclusive, perpetual, sublicenseable license to do the following:
|
| 42 |
+
|
| 43 |
+
a) to reproduce the Original Work in copies;
|
| 44 |
+
|
| 45 |
+
b) to prepare derivative works ("Derivative Works") based upon the Original Work;
|
| 46 |
+
|
| 47 |
+
c) to distribute copies of the Original Work and Derivative Works to the public;
|
| 48 |
+
|
| 49 |
+
d) to perform the Original Work publicly; and
|
| 50 |
+
|
| 51 |
+
e) to display the Original Work publicly.
|
| 52 |
+
|
| 53 |
+
2) Grant of Patent License. Licensor hereby grants You a world-wide, royalty-free, non-exclusive, perpetual, sublicenseable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, to make, use, sell and offer for sale the Original Work and Derivative Works.
|
| 54 |
+
|
| 55 |
+
3) Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor hereby agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work, and by publishing the address of that information repository in a notice immediately following the copyright notice that applies to the Original Work.
|
| 56 |
+
|
| 57 |
+
4) Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior written permission of the Licensor. Nothing in this License shall be deemed to grant any rights to trademarks, copyrights, patents, trade secrets or any other intellectual property of Licensor except as expressly stated herein. No patent license is granted to make, use, sell or offer to sell embodiments of any patent claims other than the licensed claims defined in Section 2. No right is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under different terms from this License any Original Work that Licensor otherwise would have a right to license.
|
| 58 |
+
|
| 59 |
+
5) This section intentionally omitted.
|
| 60 |
+
|
| 61 |
+
6) Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work.
|
| 62 |
+
|
| 63 |
+
7) Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately proceeding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to Original Work is granted hereunder except under this disclaimer.
|
| 64 |
+
|
| 65 |
+
8) Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to any person for any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to liability for death or personal injury resulting from Licensor's negligence to the extent applicable law prohibits such limitation. Some jurisdictions do not allow the exclusion or limitation of incidental or consequential damages, so this exclusion and limitation may not apply to You.
|
| 66 |
+
|
| 67 |
+
9) Acceptance and Termination. If You distribute copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. Nothing else but this License (or another written agreement between Licensor and You) grants You permission to create Derivative Works based upon the Original Work or to exercise any of the rights granted in Section 1 herein, and any attempt to do so except under the terms of this License (or another written agreement between Licensor and You) is expressly prohibited by U.S. copyright law, the equivalent laws of other countries, and by international treaty. Therefore, by exercising any of the rights granted to You in Section 1 herein, You indicate Your acceptance of this License and all of its terms and conditions.
|
| 68 |
+
|
| 69 |
+
10) Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware.
|
| 70 |
+
|
| 71 |
+
11) Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of the U.S. Copyright Act, 17 U.S.C. § 101 et seq., the equivalent laws of other countries, and international treaty. This section shall survive the termination of this License.
|
| 72 |
+
|
| 73 |
+
12) Attorneys Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License.
|
| 74 |
+
|
| 75 |
+
13) Miscellaneous. This License represents the complete agreement concerning the subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable.
|
| 76 |
+
|
| 77 |
+
14) Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
|
| 78 |
+
|
| 79 |
+
15) Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You.
|
| 80 |
+
|
| 81 |
+
This license is Copyright (C) 2003-2004 Lawrence E. Rosen. All rights reserved. Permission is hereby granted to copy and distribute this license without modification. This license may not be modified without the express written permission of its copyright owner.
|
lib/python3.12/site-packages/hjson-3.1.0.dist-info/METADATA
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Metadata-Version: 2.1
|
| 2 |
+
Name: hjson
|
| 3 |
+
Version: 3.1.0
|
| 4 |
+
Summary: Hjson, a user interface for JSON.
|
| 5 |
+
Home-page: http://github.com/hjson/hjson-py
|
| 6 |
+
Author: Christian Zangl
|
| 7 |
+
Author-email: laktak@cdak.net
|
| 8 |
+
License: MIT License
|
| 9 |
+
Keywords: json comments configuration
|
| 10 |
+
Platform: any
|
| 11 |
+
Classifier: Development Status :: 5 - Production/Stable
|
| 12 |
+
Classifier: Intended Audience :: Developers
|
| 13 |
+
Classifier: License :: OSI Approved :: MIT License
|
| 14 |
+
Classifier: License :: OSI Approved :: Academic Free License (AFL)
|
| 15 |
+
Classifier: Programming Language :: Python
|
| 16 |
+
Classifier: Programming Language :: Python :: 2
|
| 17 |
+
Classifier: Programming Language :: Python :: 2.6
|
| 18 |
+
Classifier: Programming Language :: Python :: 2.7
|
| 19 |
+
Classifier: Programming Language :: Python :: 3
|
| 20 |
+
Classifier: Programming Language :: Python :: 3.3
|
| 21 |
+
Classifier: Programming Language :: Python :: 3.4
|
| 22 |
+
Classifier: Programming Language :: Python :: 3.5
|
| 23 |
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
| 24 |
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
| 25 |
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
| 26 |
+
License-File: LICENSE.txt
|
| 27 |
+
|
| 28 |
+
hjson-py
|
| 29 |
+
========
|
| 30 |
+
|
| 31 |
+
`Hjson`_, a user interface for JSON
|
| 32 |
+
|
| 33 |
+
Hjson works with Python 2.5+ and Python 3.3+ (based on `simplejson`_)
|
| 34 |
+
|
| 35 |
+
Installation
|
| 36 |
+
============
|
| 37 |
+
|
| 38 |
+
- ``pip install hjson``
|
| 39 |
+
|
| 40 |
+
- or download from https://pypi.python.org/pypi/hjson
|
| 41 |
+
|
| 42 |
+
Commandline
|
| 43 |
+
-----------
|
| 44 |
+
|
| 45 |
+
::
|
| 46 |
+
|
| 47 |
+
Usage:
|
| 48 |
+
hjson [options]
|
| 49 |
+
hjson [options] <input>
|
| 50 |
+
hjson (-h | --help)
|
| 51 |
+
hjson (-V | --version)
|
| 52 |
+
|
| 53 |
+
Options:
|
| 54 |
+
-h --help Show this screen.
|
| 55 |
+
-j Output as formatted JSON.
|
| 56 |
+
-c Output as JSON.
|
| 57 |
+
-V --version Show version.
|
| 58 |
+
|
| 59 |
+
E.g. ``echo '{"json":"obj"}' | hjson``
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
Usage
|
| 63 |
+
=====
|
| 64 |
+
|
| 65 |
+
.. code-block:: python
|
| 66 |
+
|
| 67 |
+
import hjson
|
| 68 |
+
|
| 69 |
+
Decoding Hjson
|
| 70 |
+
--------------
|
| 71 |
+
|
| 72 |
+
.. code-block:: python
|
| 73 |
+
|
| 74 |
+
text = """{
|
| 75 |
+
foo: a
|
| 76 |
+
bar: 1
|
| 77 |
+
}"""
|
| 78 |
+
|
| 79 |
+
hjson.loads(text)
|
| 80 |
+
|
| 81 |
+
Result:
|
| 82 |
+
|
| 83 |
+
.. code-block:: python
|
| 84 |
+
|
| 85 |
+
OrderedDict([('foo', 'a'), ('bar', 1)])
|
| 86 |
+
|
| 87 |
+
Encoding Python object hierarchies
|
| 88 |
+
----------------------------------
|
| 89 |
+
|
| 90 |
+
.. code-block:: python
|
| 91 |
+
|
| 92 |
+
hjson.dumps({'foo': 'text', 'bar': (1, 2)})
|
| 93 |
+
|
| 94 |
+
Result:
|
| 95 |
+
|
| 96 |
+
::
|
| 97 |
+
|
| 98 |
+
{
|
| 99 |
+
foo: text
|
| 100 |
+
bar:
|
| 101 |
+
[
|
| 102 |
+
1
|
| 103 |
+
2
|
| 104 |
+
]
|
| 105 |
+
}
|
| 106 |
+
|
| 107 |
+
Encoding as JSON
|
| 108 |
+
----------------
|
| 109 |
+
|
| 110 |
+
Note that this is probably not as performant as the simplejson version.
|
| 111 |
+
|
| 112 |
+
.. code-block:: python
|
| 113 |
+
|
| 114 |
+
hjson.dumpsJSON(['foo', {'bar': ('baz', None, 1.0, 2)}])
|
| 115 |
+
|
| 116 |
+
Result: ``'["foo", {"bar": ["baz", null, 1.0, 2]}]'``
|
| 117 |
+
|
| 118 |
+
API
|
| 119 |
+
===
|
| 120 |
+
|
| 121 |
+
`hjson-py`_
|
| 122 |
+
|
| 123 |
+
.. _Hjson: https://hjson.github.io
|
| 124 |
+
.. _simplejson: https://github.com/simplejson/simplejson
|
| 125 |
+
.. _hjson-py: http://hjson.github.io/hjson-py/
|
lib/python3.12/site-packages/hjson-3.1.0.dist-info/RECORD
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
../../../bin/hjson,sha256=clpm0xqd9FMuLXKV748jrsYgIxZoerUz3H-BSH_7TkA,245
|
| 2 |
+
hjson-3.1.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
|
| 3 |
+
hjson-3.1.0.dist-info/LICENSE.txt,sha256=WRxR9JxoM7zhpdU-Y3haBGet1nRaWogOTRD05kzETEw,10462
|
| 4 |
+
hjson-3.1.0.dist-info/METADATA,sha256=ws3nEqUXFpWNgmH-FYScGumUSgt2dkiPHNm7RZh-gcU,2610
|
| 5 |
+
hjson-3.1.0.dist-info/RECORD,,
|
| 6 |
+
hjson-3.1.0.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
|
| 7 |
+
hjson-3.1.0.dist-info/entry_points.txt,sha256=sghBTcTrqdK0kKuznOZXeEBtjoBnLweHF83S9NK-r0E,42
|
| 8 |
+
hjson-3.1.0.dist-info/top_level.txt,sha256=earxK7niVoW6ADlKRFHCs3JpUL4FBSnJn9qmrYgnEUE,6
|
| 9 |
+
hjson/__init__.py,sha256=Py1drAVt-Xpi2xJAHxVXDdX-NniC4llxZVPjY7MyPCY,26580
|
| 10 |
+
hjson/__pycache__/__init__.cpython-312.pyc,,
|
| 11 |
+
hjson/__pycache__/compat.cpython-312.pyc,,
|
| 12 |
+
hjson/__pycache__/decoder.cpython-312.pyc,,
|
| 13 |
+
hjson/__pycache__/encoder.cpython-312.pyc,,
|
| 14 |
+
hjson/__pycache__/encoderH.cpython-312.pyc,,
|
| 15 |
+
hjson/__pycache__/ordered_dict.cpython-312.pyc,,
|
| 16 |
+
hjson/__pycache__/scanner.cpython-312.pyc,,
|
| 17 |
+
hjson/__pycache__/tool.cpython-312.pyc,,
|
| 18 |
+
hjson/compat.py,sha256=uvmTYe1Oa18tT_6tFRtYnzTdAkyd73B9zwMF7ZafI04,1036
|
| 19 |
+
hjson/decoder.py,sha256=oHz7g2sQd4S-AJbSzur9VJWqAHDWL25FVQ-G35XlGCA,19563
|
| 20 |
+
hjson/encoder.py,sha256=NhR3YSMVzL3UP8OLAtP2Dr6dW9UbJzjVS-SXp1DzvcY,19168
|
| 21 |
+
hjson/encoderH.py,sha256=wJ8D0gAyR3n6e3MahCIU3OahI5_xuHPWV_ZlI839xCs,20481
|
| 22 |
+
hjson/ordered_dict.py,sha256=DXtgiqkkaNWXDLZ0DGXIjF_CPzGV5qpC-PSeS1zcps8,3370
|
| 23 |
+
hjson/scanner.py,sha256=IL8poQGvCsb82y7qY5jrlSrZ5xcENpPUti3tNKhprYw,1779
|
| 24 |
+
hjson/tests/__init__.py,sha256=_A-1Tn7q7ccNPro_QfbKiXo_bTL9ED5RUX9AeSLG4TA,2011
|
| 25 |
+
hjson/tests/__pycache__/__init__.cpython-312.pyc,,
|
| 26 |
+
hjson/tests/__pycache__/test_bigint_as_string.cpython-312.pyc,,
|
| 27 |
+
hjson/tests/__pycache__/test_bitsize_int_as_string.cpython-312.pyc,,
|
| 28 |
+
hjson/tests/__pycache__/test_check_circular.cpython-312.pyc,,
|
| 29 |
+
hjson/tests/__pycache__/test_decimal.cpython-312.pyc,,
|
| 30 |
+
hjson/tests/__pycache__/test_decode.cpython-312.pyc,,
|
| 31 |
+
hjson/tests/__pycache__/test_default.cpython-312.pyc,,
|
| 32 |
+
hjson/tests/__pycache__/test_dump.cpython-312.pyc,,
|
| 33 |
+
hjson/tests/__pycache__/test_encode_basestring_ascii.cpython-312.pyc,,
|
| 34 |
+
hjson/tests/__pycache__/test_errors.cpython-312.pyc,,
|
| 35 |
+
hjson/tests/__pycache__/test_fail.cpython-312.pyc,,
|
| 36 |
+
hjson/tests/__pycache__/test_float.cpython-312.pyc,,
|
| 37 |
+
hjson/tests/__pycache__/test_for_json.cpython-312.pyc,,
|
| 38 |
+
hjson/tests/__pycache__/test_hjson.cpython-312.pyc,,
|
| 39 |
+
hjson/tests/__pycache__/test_indent.cpython-312.pyc,,
|
| 40 |
+
hjson/tests/__pycache__/test_item_sort_key.cpython-312.pyc,,
|
| 41 |
+
hjson/tests/__pycache__/test_namedtuple.cpython-312.pyc,,
|
| 42 |
+
hjson/tests/__pycache__/test_pass1.cpython-312.pyc,,
|
| 43 |
+
hjson/tests/__pycache__/test_pass2.cpython-312.pyc,,
|
| 44 |
+
hjson/tests/__pycache__/test_pass3.cpython-312.pyc,,
|
| 45 |
+
hjson/tests/__pycache__/test_recursion.cpython-312.pyc,,
|
| 46 |
+
hjson/tests/__pycache__/test_scanstring.cpython-312.pyc,,
|
| 47 |
+
hjson/tests/__pycache__/test_separators.cpython-312.pyc,,
|
| 48 |
+
hjson/tests/__pycache__/test_tool.cpython-312.pyc,,
|
| 49 |
+
hjson/tests/__pycache__/test_tuple.cpython-312.pyc,,
|
| 50 |
+
hjson/tests/__pycache__/test_unicode.cpython-312.pyc,,
|
| 51 |
+
hjson/tests/test_bigint_as_string.py,sha256=bhEtYEXWUhxyi25iLm4sPWFrt5RZ9PfFEknX1cdzP-Y,2265
|
| 52 |
+
hjson/tests/test_bitsize_int_as_string.py,sha256=-73xJ8CE2hDjGOWTERRrYbDHaY0kgBNQXC0g8nIgd4k,2332
|
| 53 |
+
hjson/tests/test_check_circular.py,sha256=64kZhsab6OcwYmJNLRqNW-19dp1UdgYbZiGzopKyR9s,940
|
| 54 |
+
hjson/tests/test_decimal.py,sha256=Qw0IBPSPYoGZXwvXkkM1cz6lpqjkPRzNDBSul-RdR_4,2556
|
| 55 |
+
hjson/tests/test_decode.py,sha256=Sm4052xVjv7ZtZFdRVMsnvQeh2eCNoXv24YOUJJLMdg,4437
|
| 56 |
+
hjson/tests/test_default.py,sha256=WWDLhDVfih4PrenmiEcvshhUOl_bNsm3jML96-AtGmo,224
|
| 57 |
+
hjson/tests/test_dump.py,sha256=5WU4Rd6vsHOwXGpGqQKIw1ZBNgRWUqMY8w3DnJVWfxo,5061
|
| 58 |
+
hjson/tests/test_encode_basestring_ascii.py,sha256=up4y9JMdGXdBXkEjfqwiG-sudSdcKw0RQfO_76za-To,2102
|
| 59 |
+
hjson/tests/test_errors.py,sha256=vg3-z36T9O-UeDHG4ZtW-nQBNAvraWKBrDA70yG989c,1549
|
| 60 |
+
hjson/tests/test_fail.py,sha256=Giinb944NX0bPwBHYUjVZ4ZlNB611Wg0wxVWxv4bDaU,5688
|
| 61 |
+
hjson/tests/test_float.py,sha256=LCUL-2xT8PYq99jQi6-Ddk9pMuC1mLrcJboTfvR08HM,1011
|
| 62 |
+
hjson/tests/test_for_json.py,sha256=ZLtypdX0ALctxMB8c3fQvx3k9OHY5t71gBxGNOXemrc,2778
|
| 63 |
+
hjson/tests/test_hjson.py,sha256=CdvXR05nu8bF_jZ-Hhj3bh8LRi8tdSJTruayj69HoDk,2327
|
| 64 |
+
hjson/tests/test_indent.py,sha256=8oUK5E8DTz1c3RkUU-nOELmr9wOKoaHHOAsxDai66iE,2589
|
| 65 |
+
hjson/tests/test_item_sort_key.py,sha256=piYy-ntwdcb_qS-y8jPFI6rVZlHCNqtTFGnaZSEvWH8,1134
|
| 66 |
+
hjson/tests/test_namedtuple.py,sha256=iK7B95JH4f2L3_MB3rY9NagEVZ1X62JHpjlm1J4t5uM,4066
|
| 67 |
+
hjson/tests/test_pass1.py,sha256=wdnBz55dY4ou8IIdZFypJ_72J6HCtLZw0YesoNOTopQ,1745
|
| 68 |
+
hjson/tests/test_pass2.py,sha256=O389C8IeJ9ysqWKUftXOeHvBi5_47VSdBM_2bCee1SQ,385
|
| 69 |
+
hjson/tests/test_pass3.py,sha256=wwemRholrfv4lwgC0ArnXiS_tfA-NnbRrZqo71YZXaA,481
|
| 70 |
+
hjson/tests/test_recursion.py,sha256=APQAtnchO6KZdduYqvEJDrBOmAKqyORwAX4ldbzAW7A,1694
|
| 71 |
+
hjson/tests/test_scanstring.py,sha256=U-5gUsaO7jEai6zfKMKADfBi-xFg8IHC1dBeElvTsn4,5953
|
| 72 |
+
hjson/tests/test_separators.py,sha256=krudtyZbx0k7OrZZ-jGCZH6yrmsj1seRkUblQLRq4Rw,945
|
| 73 |
+
hjson/tests/test_tool.py,sha256=PyDhrmMTpJSvBXvsWG8P-yrw1BT5kSp6OcSrMLvzmIw,2826
|
| 74 |
+
hjson/tests/test_tuple.py,sha256=FkOyw4s7WZ1HUGxtHYiwaBwhW2thlDB3JFSpUZCbI_I,1976
|
| 75 |
+
hjson/tests/test_unicode.py,sha256=5npz25rj4T3ZhXF_OsSmVveBUtTuz92DQ_cHDe-Jfrw,7099
|
| 76 |
+
hjson/tool.py,sha256=PVlr-lSDWfrGdkh5exWQo_21HGgHLR4OpZ1S41Dqjgo,1897
|
lib/python3.12/site-packages/hjson-3.1.0.dist-info/WHEEL
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Wheel-Version: 1.0
|
| 2 |
+
Generator: bdist_wheel (0.37.1)
|
| 3 |
+
Root-Is-Purelib: true
|
| 4 |
+
Tag: py3-none-any
|
| 5 |
+
|
lib/python3.12/site-packages/hjson-3.1.0.dist-info/entry_points.txt
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[console_scripts]
|
| 2 |
+
hjson = hjson.tool:main
|
lib/python3.12/site-packages/hjson-3.1.0.dist-info/top_level.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
hjson
|
lib/python3.12/site-packages/hjson/__init__.py
ADDED
|
@@ -0,0 +1,642 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
r"""Hjson, the Human JSON. A configuration file format that caters to
|
| 2 |
+
humans and helps reduce the errors they make.
|
| 3 |
+
|
| 4 |
+
For details and syntax see <https://hjson.github.io>.
|
| 5 |
+
|
| 6 |
+
Decoding Hjson::
|
| 7 |
+
|
| 8 |
+
>>> import hjson
|
| 9 |
+
>>> text = "{\n foo: a\n bar: 1\n}"
|
| 10 |
+
>>> hjson.loads(text)
|
| 11 |
+
OrderedDict([('foo', 'a'), ('bar', 1)])
|
| 12 |
+
|
| 13 |
+
Encoding Python object hierarchies::
|
| 14 |
+
|
| 15 |
+
>>> import hjson
|
| 16 |
+
>>> # hjson.dumps({'foo': 'text', 'bar': (1, 2)})
|
| 17 |
+
>>> hjson.dumps(OrderedDict([('foo', 'text'), ('bar', (1, 2))]))
|
| 18 |
+
'{\n foo: text\n bar:\n [\n 1\n 2\n ]\n}'
|
| 19 |
+
|
| 20 |
+
Encoding as JSON::
|
| 21 |
+
|
| 22 |
+
Note that this is probably not as performant as the simplejson version.
|
| 23 |
+
|
| 24 |
+
>>> import hjson
|
| 25 |
+
>>> hjson.dumpsJSON(['foo', {'bar': ('baz', None, 1.0, 2)}])
|
| 26 |
+
'["foo", {"bar": ["baz", null, 1.0, 2]}]'
|
| 27 |
+
|
| 28 |
+
Using hjson.tool from the shell to validate and pretty-print::
|
| 29 |
+
|
| 30 |
+
$ echo '{"json":"obj"}' | python -m hjson.tool
|
| 31 |
+
{
|
| 32 |
+
json: obj
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
Other formats are -c for compact or -j for formatted JSON.
|
| 36 |
+
|
| 37 |
+
"""
|
| 38 |
+
from __future__ import absolute_import
|
| 39 |
+
__version__ = '3.1.0'
|
| 40 |
+
__all__ = [
|
| 41 |
+
'dump', 'dumps', 'load', 'loads',
|
| 42 |
+
'dumpJSON', 'dumpsJSON',
|
| 43 |
+
'HjsonDecoder', 'HjsonDecodeError', 'HjsonEncoder', 'JSONEncoder',
|
| 44 |
+
'OrderedDict', 'simple_first',
|
| 45 |
+
]
|
| 46 |
+
|
| 47 |
+
# based on simplejson by
|
| 48 |
+
# __author__ = 'Bob Ippolito <bob@redivi.com>'
|
| 49 |
+
__author__ = 'Christian Zangl <coralllama@gmail.com>'
|
| 50 |
+
|
| 51 |
+
from decimal import Decimal
|
| 52 |
+
|
| 53 |
+
from .scanner import HjsonDecodeError
|
| 54 |
+
from .decoder import HjsonDecoder
|
| 55 |
+
from .encoderH import HjsonEncoder
|
| 56 |
+
from .encoder import JSONEncoder
|
| 57 |
+
def _import_OrderedDict():
|
| 58 |
+
import collections
|
| 59 |
+
try:
|
| 60 |
+
return collections.OrderedDict
|
| 61 |
+
except AttributeError:
|
| 62 |
+
from . import ordered_dict
|
| 63 |
+
return ordered_dict.OrderedDict
|
| 64 |
+
OrderedDict = _import_OrderedDict()
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
_default_decoder = HjsonDecoder(encoding=None, object_hook=None,
|
| 68 |
+
object_pairs_hook=OrderedDict)
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
def load(fp, encoding=None, cls=None, object_hook=None, parse_float=None,
|
| 72 |
+
parse_int=None, object_pairs_hook=OrderedDict,
|
| 73 |
+
use_decimal=False, namedtuple_as_object=True, tuple_as_array=True,
|
| 74 |
+
**kw):
|
| 75 |
+
"""Deserialize ``fp`` (a ``.read()``-supporting file-like object containing
|
| 76 |
+
a JSON document) to a Python object.
|
| 77 |
+
|
| 78 |
+
*encoding* determines the encoding used to interpret any
|
| 79 |
+
:class:`str` objects decoded by this instance (``'utf-8'`` by
|
| 80 |
+
default). It has no effect when decoding :class:`unicode` objects.
|
| 81 |
+
|
| 82 |
+
Note that currently only encodings that are a superset of ASCII work,
|
| 83 |
+
strings of other encodings should be passed in as :class:`unicode`.
|
| 84 |
+
|
| 85 |
+
*object_hook*, if specified, will be called with the result of every
|
| 86 |
+
JSON object decoded and its return value will be used in place of the
|
| 87 |
+
given :class:`dict`. This can be used to provide custom
|
| 88 |
+
deserializations (e.g. to support JSON-RPC class hinting).
|
| 89 |
+
|
| 90 |
+
*object_pairs_hook* is an optional function that will be called with
|
| 91 |
+
the result of any object literal decode with an ordered list of pairs.
|
| 92 |
+
The return value of *object_pairs_hook* will be used instead of the
|
| 93 |
+
:class:`dict`. This feature can be used to implement custom decoders
|
| 94 |
+
that rely on the order that the key and value pairs are decoded (for
|
| 95 |
+
example, :func:`collections.OrderedDict` will remember the order of
|
| 96 |
+
insertion). If *object_hook* is also defined, the *object_pairs_hook*
|
| 97 |
+
takes priority.
|
| 98 |
+
|
| 99 |
+
*parse_float*, if specified, will be called with the string of every
|
| 100 |
+
JSON float to be decoded. By default, this is equivalent to
|
| 101 |
+
``float(num_str)``. This can be used to use another datatype or parser
|
| 102 |
+
for JSON floats (e.g. :class:`decimal.Decimal`).
|
| 103 |
+
|
| 104 |
+
*parse_int*, if specified, will be called with the string of every
|
| 105 |
+
JSON int to be decoded. By default, this is equivalent to
|
| 106 |
+
``int(num_str)``. This can be used to use another datatype or parser
|
| 107 |
+
for JSON integers (e.g. :class:`float`).
|
| 108 |
+
|
| 109 |
+
If *use_decimal* is true (default: ``False``) then it implies
|
| 110 |
+
parse_float=decimal.Decimal for parity with ``dump``.
|
| 111 |
+
|
| 112 |
+
To use a custom ``HjsonDecoder`` subclass, specify it with the ``cls``
|
| 113 |
+
kwarg. NOTE: You should use *object_hook* or *object_pairs_hook* instead
|
| 114 |
+
of subclassing whenever possible.
|
| 115 |
+
|
| 116 |
+
"""
|
| 117 |
+
return loads(fp.read(),
|
| 118 |
+
encoding=encoding, cls=cls, object_hook=object_hook,
|
| 119 |
+
parse_float=parse_float, parse_int=parse_int,
|
| 120 |
+
object_pairs_hook=object_pairs_hook,
|
| 121 |
+
use_decimal=use_decimal, **kw)
|
| 122 |
+
|
| 123 |
+
|
| 124 |
+
def loads(s, encoding=None, cls=None, object_hook=None, parse_float=None,
|
| 125 |
+
parse_int=None, object_pairs_hook=None,
|
| 126 |
+
use_decimal=False, **kw):
|
| 127 |
+
"""Deserialize ``s`` (a ``str`` or ``unicode`` instance containing a JSON
|
| 128 |
+
document) to a Python object.
|
| 129 |
+
|
| 130 |
+
*encoding* determines the encoding used to interpret any
|
| 131 |
+
:class:`str` objects decoded by this instance (``'utf-8'`` by
|
| 132 |
+
default). It has no effect when decoding :class:`unicode` objects.
|
| 133 |
+
|
| 134 |
+
Note that currently only encodings that are a superset of ASCII work,
|
| 135 |
+
strings of other encodings should be passed in as :class:`unicode`.
|
| 136 |
+
|
| 137 |
+
*object_hook*, if specified, will be called with the result of every
|
| 138 |
+
JSON object decoded and its return value will be used in place of the
|
| 139 |
+
given :class:`dict`. This can be used to provide custom
|
| 140 |
+
deserializations (e.g. to support JSON-RPC class hinting).
|
| 141 |
+
|
| 142 |
+
*object_pairs_hook* is an optional function that will be called with
|
| 143 |
+
the result of any object literal decode with an ordered list of pairs.
|
| 144 |
+
The return value of *object_pairs_hook* will be used instead of the
|
| 145 |
+
:class:`dict`. This feature can be used to implement custom decoders
|
| 146 |
+
that rely on the order that the key and value pairs are decoded (for
|
| 147 |
+
example, :func:`collections.OrderedDict` will remember the order of
|
| 148 |
+
insertion). If *object_hook* is also defined, the *object_pairs_hook*
|
| 149 |
+
takes priority.
|
| 150 |
+
|
| 151 |
+
*parse_float*, if specified, will be called with the string of every
|
| 152 |
+
JSON float to be decoded. By default, this is equivalent to
|
| 153 |
+
``float(num_str)``. This can be used to use another datatype or parser
|
| 154 |
+
for JSON floats (e.g. :class:`decimal.Decimal`).
|
| 155 |
+
|
| 156 |
+
*parse_int*, if specified, will be called with the string of every
|
| 157 |
+
JSON int to be decoded. By default, this is equivalent to
|
| 158 |
+
``int(num_str)``. This can be used to use another datatype or parser
|
| 159 |
+
for JSON integers (e.g. :class:`float`).
|
| 160 |
+
|
| 161 |
+
If *use_decimal* is true (default: ``False``) then it implies
|
| 162 |
+
parse_float=decimal.Decimal for parity with ``dump``.
|
| 163 |
+
|
| 164 |
+
To use a custom ``HjsonDecoder`` subclass, specify it with the ``cls``
|
| 165 |
+
kwarg. NOTE: You should use *object_hook* or *object_pairs_hook* instead
|
| 166 |
+
of subclassing whenever possible.
|
| 167 |
+
|
| 168 |
+
"""
|
| 169 |
+
if (cls is None and encoding is None and object_hook is None and
|
| 170 |
+
parse_int is None and parse_float is None and
|
| 171 |
+
object_pairs_hook is None
|
| 172 |
+
and not use_decimal and not kw):
|
| 173 |
+
return _default_decoder.decode(s)
|
| 174 |
+
if cls is None:
|
| 175 |
+
cls = HjsonDecoder
|
| 176 |
+
if object_hook is not None:
|
| 177 |
+
kw['object_hook'] = object_hook
|
| 178 |
+
if object_pairs_hook is not None:
|
| 179 |
+
kw['object_pairs_hook'] = object_pairs_hook
|
| 180 |
+
if parse_float is not None:
|
| 181 |
+
kw['parse_float'] = parse_float
|
| 182 |
+
if parse_int is not None:
|
| 183 |
+
kw['parse_int'] = parse_int
|
| 184 |
+
if use_decimal:
|
| 185 |
+
if parse_float is not None:
|
| 186 |
+
raise TypeError("use_decimal=True implies parse_float=Decimal")
|
| 187 |
+
kw['parse_float'] = Decimal
|
| 188 |
+
return cls(encoding=encoding, **kw).decode(s)
|
| 189 |
+
|
| 190 |
+
|
| 191 |
+
_default_hjson_encoder = HjsonEncoder(
|
| 192 |
+
skipkeys=False,
|
| 193 |
+
ensure_ascii=True,
|
| 194 |
+
check_circular=True,
|
| 195 |
+
indent=None,
|
| 196 |
+
encoding='utf-8',
|
| 197 |
+
default=None,
|
| 198 |
+
use_decimal=True,
|
| 199 |
+
namedtuple_as_object=True,
|
| 200 |
+
tuple_as_array=True,
|
| 201 |
+
bigint_as_string=False,
|
| 202 |
+
item_sort_key=None,
|
| 203 |
+
for_json=False,
|
| 204 |
+
int_as_string_bitcount=None,
|
| 205 |
+
)
|
| 206 |
+
|
| 207 |
+
def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True,
|
| 208 |
+
cls=None, indent=None,
|
| 209 |
+
encoding='utf-8', default=None, use_decimal=True,
|
| 210 |
+
namedtuple_as_object=True, tuple_as_array=True,
|
| 211 |
+
bigint_as_string=False, sort_keys=False, item_sort_key=None,
|
| 212 |
+
for_json=False, int_as_string_bitcount=None, **kw):
|
| 213 |
+
"""Serialize ``obj`` as a JSON formatted stream to ``fp`` (a
|
| 214 |
+
``.write()``-supporting file-like object).
|
| 215 |
+
|
| 216 |
+
If *skipkeys* is true then ``dict`` keys that are not basic types
|
| 217 |
+
(``str``, ``unicode``, ``int``, ``long``, ``float``, ``bool``, ``None``)
|
| 218 |
+
will be skipped instead of raising a ``TypeError``.
|
| 219 |
+
|
| 220 |
+
If *ensure_ascii* is false, then the some chunks written to ``fp``
|
| 221 |
+
may be ``unicode`` instances, subject to normal Python ``str`` to
|
| 222 |
+
``unicode`` coercion rules. Unless ``fp.write()`` explicitly
|
| 223 |
+
understands ``unicode`` (as in ``codecs.getwriter()``) this is likely
|
| 224 |
+
to cause an error.
|
| 225 |
+
|
| 226 |
+
If *check_circular* is false, then the circular reference check
|
| 227 |
+
for container types will be skipped and a circular reference will
|
| 228 |
+
result in an ``OverflowError`` (or worse).
|
| 229 |
+
|
| 230 |
+
*indent* defines the amount of whitespace that the JSON array elements
|
| 231 |
+
and object members will be indented for each level of nesting.
|
| 232 |
+
The default is two spaces.
|
| 233 |
+
|
| 234 |
+
*encoding* is the character encoding for str instances, default is UTF-8.
|
| 235 |
+
|
| 236 |
+
*default(obj)* is a function that should return a serializable version
|
| 237 |
+
of obj or raise ``TypeError``. The default simply raises ``TypeError``.
|
| 238 |
+
|
| 239 |
+
If *use_decimal* is true (default: ``True``) then decimal.Decimal
|
| 240 |
+
will be natively serialized to JSON with full precision.
|
| 241 |
+
|
| 242 |
+
If *namedtuple_as_object* is true (default: ``True``),
|
| 243 |
+
:class:`tuple` subclasses with ``_asdict()`` methods will be encoded
|
| 244 |
+
as JSON objects.
|
| 245 |
+
|
| 246 |
+
If *tuple_as_array* is true (default: ``True``),
|
| 247 |
+
:class:`tuple` (and subclasses) will be encoded as JSON arrays.
|
| 248 |
+
|
| 249 |
+
If *bigint_as_string* is true (default: ``False``), ints 2**53 and higher
|
| 250 |
+
or lower than -2**53 will be encoded as strings. This is to avoid the
|
| 251 |
+
rounding that happens in Javascript otherwise. Note that this is still a
|
| 252 |
+
lossy operation that will not round-trip correctly and should be used
|
| 253 |
+
sparingly.
|
| 254 |
+
|
| 255 |
+
If *int_as_string_bitcount* is a positive number (n), then int of size
|
| 256 |
+
greater than or equal to 2**n or lower than or equal to -2**n will be
|
| 257 |
+
encoded as strings.
|
| 258 |
+
|
| 259 |
+
If specified, *item_sort_key* is a callable used to sort the items in
|
| 260 |
+
each dictionary. This is useful if you want to sort items other than
|
| 261 |
+
in alphabetical order by key. This option takes precedence over
|
| 262 |
+
*sort_keys*.
|
| 263 |
+
|
| 264 |
+
If *sort_keys* is true (default: ``False``), the output of dictionaries
|
| 265 |
+
will be sorted by item.
|
| 266 |
+
|
| 267 |
+
If *for_json* is true (default: ``False``), objects with a ``for_json()``
|
| 268 |
+
method will use the return value of that method for encoding as JSON
|
| 269 |
+
instead of the object.
|
| 270 |
+
|
| 271 |
+
To use a custom ``HjsonEncoder`` subclass (e.g. one that overrides the
|
| 272 |
+
``.default()`` method to serialize additional types), specify it with
|
| 273 |
+
the ``cls`` kwarg. NOTE: You should use *default* or *for_json* instead
|
| 274 |
+
of subclassing whenever possible.
|
| 275 |
+
|
| 276 |
+
"""
|
| 277 |
+
# cached encoder
|
| 278 |
+
if (not skipkeys and ensure_ascii and
|
| 279 |
+
check_circular and
|
| 280 |
+
cls is None and indent is None and
|
| 281 |
+
encoding == 'utf-8' and default is None and use_decimal
|
| 282 |
+
and namedtuple_as_object and tuple_as_array
|
| 283 |
+
and not bigint_as_string and not sort_keys
|
| 284 |
+
and not item_sort_key and not for_json
|
| 285 |
+
and int_as_string_bitcount is None
|
| 286 |
+
and not kw
|
| 287 |
+
):
|
| 288 |
+
iterable = _default_hjson_encoder.iterencode(obj)
|
| 289 |
+
else:
|
| 290 |
+
if cls is None:
|
| 291 |
+
cls = HjsonEncoder
|
| 292 |
+
iterable = cls(skipkeys=skipkeys, ensure_ascii=ensure_ascii,
|
| 293 |
+
check_circular=check_circular, indent=indent,
|
| 294 |
+
encoding=encoding,
|
| 295 |
+
default=default, use_decimal=use_decimal,
|
| 296 |
+
namedtuple_as_object=namedtuple_as_object,
|
| 297 |
+
tuple_as_array=tuple_as_array,
|
| 298 |
+
bigint_as_string=bigint_as_string,
|
| 299 |
+
sort_keys=sort_keys,
|
| 300 |
+
item_sort_key=item_sort_key,
|
| 301 |
+
for_json=for_json,
|
| 302 |
+
int_as_string_bitcount=int_as_string_bitcount,
|
| 303 |
+
**kw).iterencode(obj)
|
| 304 |
+
# could accelerate with writelines in some versions of Python, at
|
| 305 |
+
# a debuggability cost
|
| 306 |
+
for chunk in iterable:
|
| 307 |
+
fp.write(chunk)
|
| 308 |
+
|
| 309 |
+
|
| 310 |
+
def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True,
|
| 311 |
+
cls=None, indent=None,
|
| 312 |
+
encoding='utf-8', default=None, use_decimal=True,
|
| 313 |
+
namedtuple_as_object=True, tuple_as_array=True,
|
| 314 |
+
bigint_as_string=False, sort_keys=False, item_sort_key=None,
|
| 315 |
+
for_json=False, int_as_string_bitcount=None, **kw):
|
| 316 |
+
"""Serialize ``obj`` to a JSON formatted ``str``.
|
| 317 |
+
|
| 318 |
+
If ``skipkeys`` is false then ``dict`` keys that are not basic types
|
| 319 |
+
(``str``, ``unicode``, ``int``, ``long``, ``float``, ``bool``, ``None``)
|
| 320 |
+
will be skipped instead of raising a ``TypeError``.
|
| 321 |
+
|
| 322 |
+
If ``ensure_ascii`` is false, then the return value will be a
|
| 323 |
+
``unicode`` instance subject to normal Python ``str`` to ``unicode``
|
| 324 |
+
coercion rules instead of being escaped to an ASCII ``str``.
|
| 325 |
+
|
| 326 |
+
If ``check_circular`` is false, then the circular reference check
|
| 327 |
+
for container types will be skipped and a circular reference will
|
| 328 |
+
result in an ``OverflowError`` (or worse).
|
| 329 |
+
|
| 330 |
+
*indent* defines the amount of whitespace that the JSON array elements
|
| 331 |
+
and object members will be indented for each level of nesting.
|
| 332 |
+
The default is two spaces.
|
| 333 |
+
|
| 334 |
+
``encoding`` is the character encoding for str instances, default is UTF-8.
|
| 335 |
+
|
| 336 |
+
``default(obj)`` is a function that should return a serializable version
|
| 337 |
+
of obj or raise TypeError. The default simply raises TypeError.
|
| 338 |
+
|
| 339 |
+
If *use_decimal* is true (default: ``True``) then decimal.Decimal
|
| 340 |
+
will be natively serialized to JSON with full precision.
|
| 341 |
+
|
| 342 |
+
If *namedtuple_as_object* is true (default: ``True``),
|
| 343 |
+
:class:`tuple` subclasses with ``_asdict()`` methods will be encoded
|
| 344 |
+
as JSON objects.
|
| 345 |
+
|
| 346 |
+
If *tuple_as_array* is true (default: ``True``),
|
| 347 |
+
:class:`tuple` (and subclasses) will be encoded as JSON arrays.
|
| 348 |
+
|
| 349 |
+
If *bigint_as_string* is true (not the default), ints 2**53 and higher
|
| 350 |
+
or lower than -2**53 will be encoded as strings. This is to avoid the
|
| 351 |
+
rounding that happens in Javascript otherwise.
|
| 352 |
+
|
| 353 |
+
If *int_as_string_bitcount* is a positive number (n), then int of size
|
| 354 |
+
greater than or equal to 2**n or lower than or equal to -2**n will be
|
| 355 |
+
encoded as strings.
|
| 356 |
+
|
| 357 |
+
If specified, *item_sort_key* is a callable used to sort the items in
|
| 358 |
+
each dictionary. This is useful if you want to sort items other than
|
| 359 |
+
in alphabetical order by key. This option takes precendence over
|
| 360 |
+
*sort_keys*.
|
| 361 |
+
|
| 362 |
+
If *sort_keys* is true (default: ``False``), the output of dictionaries
|
| 363 |
+
will be sorted by item.
|
| 364 |
+
|
| 365 |
+
If *for_json* is true (default: ``False``), objects with a ``for_json()``
|
| 366 |
+
method will use the return value of that method for encoding as JSON
|
| 367 |
+
instead of the object.
|
| 368 |
+
|
| 369 |
+
To use a custom ``HjsonEncoder`` subclass (e.g. one that overrides the
|
| 370 |
+
``.default()`` method to serialize additional types), specify it with
|
| 371 |
+
the ``cls`` kwarg. NOTE: You should use *default* instead of subclassing
|
| 372 |
+
whenever possible.
|
| 373 |
+
|
| 374 |
+
"""
|
| 375 |
+
# cached encoder
|
| 376 |
+
if (
|
| 377 |
+
not skipkeys and ensure_ascii and
|
| 378 |
+
check_circular and
|
| 379 |
+
cls is None and indent is None and
|
| 380 |
+
encoding == 'utf-8' and default is None and use_decimal
|
| 381 |
+
and namedtuple_as_object and tuple_as_array
|
| 382 |
+
and not bigint_as_string and not sort_keys
|
| 383 |
+
and not item_sort_key and not for_json
|
| 384 |
+
and int_as_string_bitcount is None
|
| 385 |
+
and not kw
|
| 386 |
+
):
|
| 387 |
+
return _default_hjson_encoder.encode(obj)
|
| 388 |
+
if cls is None:
|
| 389 |
+
cls = HjsonEncoder
|
| 390 |
+
return cls(
|
| 391 |
+
skipkeys=skipkeys, ensure_ascii=ensure_ascii,
|
| 392 |
+
check_circular=check_circular, indent=indent,
|
| 393 |
+
encoding=encoding, default=default,
|
| 394 |
+
use_decimal=use_decimal,
|
| 395 |
+
namedtuple_as_object=namedtuple_as_object,
|
| 396 |
+
tuple_as_array=tuple_as_array,
|
| 397 |
+
bigint_as_string=bigint_as_string,
|
| 398 |
+
sort_keys=sort_keys,
|
| 399 |
+
item_sort_key=item_sort_key,
|
| 400 |
+
for_json=for_json,
|
| 401 |
+
int_as_string_bitcount=int_as_string_bitcount,
|
| 402 |
+
**kw).encode(obj)
|
| 403 |
+
|
| 404 |
+
|
| 405 |
+
|
| 406 |
+
_default_json_encoder = JSONEncoder(
|
| 407 |
+
skipkeys=False,
|
| 408 |
+
ensure_ascii=True,
|
| 409 |
+
check_circular=True,
|
| 410 |
+
indent=None,
|
| 411 |
+
separators=None,
|
| 412 |
+
encoding='utf-8',
|
| 413 |
+
default=None,
|
| 414 |
+
use_decimal=True,
|
| 415 |
+
namedtuple_as_object=True,
|
| 416 |
+
tuple_as_array=True,
|
| 417 |
+
bigint_as_string=False,
|
| 418 |
+
item_sort_key=None,
|
| 419 |
+
for_json=False,
|
| 420 |
+
int_as_string_bitcount=None,
|
| 421 |
+
)
|
| 422 |
+
|
| 423 |
+
def dumpJSON(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True,
|
| 424 |
+
cls=None, indent=None, separators=None,
|
| 425 |
+
encoding='utf-8', default=None, use_decimal=True,
|
| 426 |
+
namedtuple_as_object=True, tuple_as_array=True,
|
| 427 |
+
bigint_as_string=False, sort_keys=False, item_sort_key=None,
|
| 428 |
+
for_json=False, int_as_string_bitcount=None, **kw):
|
| 429 |
+
"""Serialize ``obj`` as a JSON formatted stream to ``fp`` (a
|
| 430 |
+
``.write()``-supporting file-like object).
|
| 431 |
+
|
| 432 |
+
If *skipkeys* is true then ``dict`` keys that are not basic types
|
| 433 |
+
(``str``, ``unicode``, ``int``, ``long``, ``float``, ``bool``, ``None``)
|
| 434 |
+
will be skipped instead of raising a ``TypeError``.
|
| 435 |
+
|
| 436 |
+
If *ensure_ascii* is false, then the some chunks written to ``fp``
|
| 437 |
+
may be ``unicode`` instances, subject to normal Python ``str`` to
|
| 438 |
+
``unicode`` coercion rules. Unless ``fp.write()`` explicitly
|
| 439 |
+
understands ``unicode`` (as in ``codecs.getwriter()``) this is likely
|
| 440 |
+
to cause an error.
|
| 441 |
+
|
| 442 |
+
If *check_circular* is false, then the circular reference check
|
| 443 |
+
for container types will be skipped and a circular reference will
|
| 444 |
+
result in an ``OverflowError`` (or worse).
|
| 445 |
+
|
| 446 |
+
If *indent* is a string, then JSON array elements and object members
|
| 447 |
+
will be pretty-printed with a newline followed by that string repeated
|
| 448 |
+
for each level of nesting. ``None`` (the default) selects the most compact
|
| 449 |
+
representation without any newlines. An integer is also accepted
|
| 450 |
+
and is converted to a string with that many spaces.
|
| 451 |
+
|
| 452 |
+
If specified, *separators* should be an
|
| 453 |
+
``(item_separator, key_separator)`` tuple. The default is ``(', ', ': ')``
|
| 454 |
+
if *indent* is ``None`` and ``(',', ': ')`` otherwise. To get the most
|
| 455 |
+
compact JSON representation, you should specify ``(',', ':')`` to eliminate
|
| 456 |
+
whitespace.
|
| 457 |
+
|
| 458 |
+
*encoding* is the character encoding for str instances, default is UTF-8.
|
| 459 |
+
|
| 460 |
+
*default(obj)* is a function that should return a serializable version
|
| 461 |
+
of obj or raise ``TypeError``. The default simply raises ``TypeError``.
|
| 462 |
+
|
| 463 |
+
If *use_decimal* is true (default: ``True``) then decimal.Decimal
|
| 464 |
+
will be natively serialized to JSON with full precision.
|
| 465 |
+
|
| 466 |
+
If *namedtuple_as_object* is true (default: ``True``),
|
| 467 |
+
:class:`tuple` subclasses with ``_asdict()`` methods will be encoded
|
| 468 |
+
as JSON objects.
|
| 469 |
+
|
| 470 |
+
If *tuple_as_array* is true (default: ``True``),
|
| 471 |
+
:class:`tuple` (and subclasses) will be encoded as JSON arrays.
|
| 472 |
+
|
| 473 |
+
If *bigint_as_string* is true (default: ``False``), ints 2**53 and higher
|
| 474 |
+
or lower than -2**53 will be encoded as strings. This is to avoid the
|
| 475 |
+
rounding that happens in Javascript otherwise. Note that this is still a
|
| 476 |
+
lossy operation that will not round-trip correctly and should be used
|
| 477 |
+
sparingly.
|
| 478 |
+
|
| 479 |
+
If *int_as_string_bitcount* is a positive number (n), then int of size
|
| 480 |
+
greater than or equal to 2**n or lower than or equal to -2**n will be
|
| 481 |
+
encoded as strings.
|
| 482 |
+
|
| 483 |
+
If specified, *item_sort_key* is a callable used to sort the items in
|
| 484 |
+
each dictionary. This is useful if you want to sort items other than
|
| 485 |
+
in alphabetical order by key. This option takes precedence over
|
| 486 |
+
*sort_keys*.
|
| 487 |
+
|
| 488 |
+
If *sort_keys* is true (default: ``False``), the output of dictionaries
|
| 489 |
+
will be sorted by item.
|
| 490 |
+
|
| 491 |
+
If *for_json* is true (default: ``False``), objects with a ``for_json()``
|
| 492 |
+
method will use the return value of that method for encoding as JSON
|
| 493 |
+
instead of the object.
|
| 494 |
+
|
| 495 |
+
To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the
|
| 496 |
+
``.default()`` method to serialize additional types), specify it with
|
| 497 |
+
the ``cls`` kwarg. NOTE: You should use *default* or *for_json* instead
|
| 498 |
+
of subclassing whenever possible.
|
| 499 |
+
|
| 500 |
+
"""
|
| 501 |
+
# cached encoder
|
| 502 |
+
if (not skipkeys and ensure_ascii and
|
| 503 |
+
check_circular and
|
| 504 |
+
cls is None and indent is None and separators is None and
|
| 505 |
+
encoding == 'utf-8' and default is None and use_decimal
|
| 506 |
+
and namedtuple_as_object and tuple_as_array
|
| 507 |
+
and not bigint_as_string and not sort_keys
|
| 508 |
+
and not item_sort_key and not for_json
|
| 509 |
+
and int_as_string_bitcount is None
|
| 510 |
+
and not kw
|
| 511 |
+
):
|
| 512 |
+
iterable = _default_json_encoder.iterencode(obj)
|
| 513 |
+
else:
|
| 514 |
+
if cls is None:
|
| 515 |
+
cls = JSONEncoder
|
| 516 |
+
iterable = cls(skipkeys=skipkeys, ensure_ascii=ensure_ascii,
|
| 517 |
+
check_circular=check_circular, indent=indent,
|
| 518 |
+
separators=separators, encoding=encoding,
|
| 519 |
+
default=default, use_decimal=use_decimal,
|
| 520 |
+
namedtuple_as_object=namedtuple_as_object,
|
| 521 |
+
tuple_as_array=tuple_as_array,
|
| 522 |
+
bigint_as_string=bigint_as_string,
|
| 523 |
+
sort_keys=sort_keys,
|
| 524 |
+
item_sort_key=item_sort_key,
|
| 525 |
+
for_json=for_json,
|
| 526 |
+
int_as_string_bitcount=int_as_string_bitcount,
|
| 527 |
+
**kw).iterencode(obj)
|
| 528 |
+
# could accelerate with writelines in some versions of Python, at
|
| 529 |
+
# a debuggability cost
|
| 530 |
+
for chunk in iterable:
|
| 531 |
+
fp.write(chunk)
|
| 532 |
+
|
| 533 |
+
|
| 534 |
+
def dumpsJSON(obj, skipkeys=False, ensure_ascii=True, check_circular=True,
|
| 535 |
+
cls=None, indent=None, separators=None,
|
| 536 |
+
encoding='utf-8', default=None, use_decimal=True,
|
| 537 |
+
namedtuple_as_object=True, tuple_as_array=True,
|
| 538 |
+
bigint_as_string=False, sort_keys=False, item_sort_key=None,
|
| 539 |
+
for_json=False, int_as_string_bitcount=None, **kw):
|
| 540 |
+
"""Serialize ``obj`` to a JSON formatted ``str``.
|
| 541 |
+
|
| 542 |
+
If ``skipkeys`` is false then ``dict`` keys that are not basic types
|
| 543 |
+
(``str``, ``unicode``, ``int``, ``long``, ``float``, ``bool``, ``None``)
|
| 544 |
+
will be skipped instead of raising a ``TypeError``.
|
| 545 |
+
|
| 546 |
+
If ``ensure_ascii`` is false, then the return value will be a
|
| 547 |
+
``unicode`` instance subject to normal Python ``str`` to ``unicode``
|
| 548 |
+
coercion rules instead of being escaped to an ASCII ``str``.
|
| 549 |
+
|
| 550 |
+
If ``check_circular`` is false, then the circular reference check
|
| 551 |
+
for container types will be skipped and a circular reference will
|
| 552 |
+
result in an ``OverflowError`` (or worse).
|
| 553 |
+
|
| 554 |
+
If ``indent`` is a string, then JSON array elements and object members
|
| 555 |
+
will be pretty-printed with a newline followed by that string repeated
|
| 556 |
+
for each level of nesting. ``None`` (the default) selects the most compact
|
| 557 |
+
representation without any newlines. An integer is also accepted
|
| 558 |
+
and is converted to a string with that many spaces.
|
| 559 |
+
|
| 560 |
+
If specified, ``separators`` should be an
|
| 561 |
+
``(item_separator, key_separator)`` tuple. The default is ``(', ', ': ')``
|
| 562 |
+
if *indent* is ``None`` and ``(',', ': ')`` otherwise. To get the most
|
| 563 |
+
compact JSON representation, you should specify ``(',', ':')`` to eliminate
|
| 564 |
+
whitespace.
|
| 565 |
+
|
| 566 |
+
``encoding`` is the character encoding for str instances, default is UTF-8.
|
| 567 |
+
|
| 568 |
+
``default(obj)`` is a function that should return a serializable version
|
| 569 |
+
of obj or raise TypeError. The default simply raises TypeError.
|
| 570 |
+
|
| 571 |
+
If *use_decimal* is true (default: ``True``) then decimal.Decimal
|
| 572 |
+
will be natively serialized to JSON with full precision.
|
| 573 |
+
|
| 574 |
+
If *namedtuple_as_object* is true (default: ``True``),
|
| 575 |
+
:class:`tuple` subclasses with ``_asdict()`` methods will be encoded
|
| 576 |
+
as JSON objects.
|
| 577 |
+
|
| 578 |
+
If *tuple_as_array* is true (default: ``True``),
|
| 579 |
+
:class:`tuple` (and subclasses) will be encoded as JSON arrays.
|
| 580 |
+
|
| 581 |
+
If *bigint_as_string* is true (not the default), ints 2**53 and higher
|
| 582 |
+
or lower than -2**53 will be encoded as strings. This is to avoid the
|
| 583 |
+
rounding that happens in Javascript otherwise.
|
| 584 |
+
|
| 585 |
+
If *int_as_string_bitcount* is a positive number (n), then int of size
|
| 586 |
+
greater than or equal to 2**n or lower than or equal to -2**n will be
|
| 587 |
+
encoded as strings.
|
| 588 |
+
|
| 589 |
+
If specified, *item_sort_key* is a callable used to sort the items in
|
| 590 |
+
each dictionary. This is useful if you want to sort items other than
|
| 591 |
+
in alphabetical order by key. This option takes precendence over
|
| 592 |
+
*sort_keys*.
|
| 593 |
+
|
| 594 |
+
If *sort_keys* is true (default: ``False``), the output of dictionaries
|
| 595 |
+
will be sorted by item.
|
| 596 |
+
|
| 597 |
+
If *for_json* is true (default: ``False``), objects with a ``for_json()``
|
| 598 |
+
method will use the return value of that method for encoding as JSON
|
| 599 |
+
instead of the object.
|
| 600 |
+
|
| 601 |
+
To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the
|
| 602 |
+
``.default()`` method to serialize additional types), specify it with
|
| 603 |
+
the ``cls`` kwarg. NOTE: You should use *default* instead of subclassing
|
| 604 |
+
whenever possible.
|
| 605 |
+
|
| 606 |
+
"""
|
| 607 |
+
# cached encoder
|
| 608 |
+
if (
|
| 609 |
+
not skipkeys and ensure_ascii and
|
| 610 |
+
check_circular and
|
| 611 |
+
cls is None and indent is None and separators is None and
|
| 612 |
+
encoding == 'utf-8' and default is None and use_decimal
|
| 613 |
+
and namedtuple_as_object and tuple_as_array
|
| 614 |
+
and not bigint_as_string and not sort_keys
|
| 615 |
+
and not item_sort_key and not for_json
|
| 616 |
+
and int_as_string_bitcount is None
|
| 617 |
+
and not kw
|
| 618 |
+
):
|
| 619 |
+
return _default_json_encoder.encode(obj)
|
| 620 |
+
if cls is None:
|
| 621 |
+
cls = JSONEncoder
|
| 622 |
+
return cls(
|
| 623 |
+
skipkeys=skipkeys, ensure_ascii=ensure_ascii,
|
| 624 |
+
check_circular=check_circular, indent=indent,
|
| 625 |
+
separators=separators, encoding=encoding, default=default,
|
| 626 |
+
use_decimal=use_decimal,
|
| 627 |
+
namedtuple_as_object=namedtuple_as_object,
|
| 628 |
+
tuple_as_array=tuple_as_array,
|
| 629 |
+
bigint_as_string=bigint_as_string,
|
| 630 |
+
sort_keys=sort_keys,
|
| 631 |
+
item_sort_key=item_sort_key,
|
| 632 |
+
for_json=for_json,
|
| 633 |
+
int_as_string_bitcount=int_as_string_bitcount,
|
| 634 |
+
**kw).encode(obj)
|
| 635 |
+
|
| 636 |
+
|
| 637 |
+
|
| 638 |
+
def simple_first(kv):
|
| 639 |
+
"""Helper function to pass to item_sort_key to sort simple
|
| 640 |
+
elements to the top, then container elements.
|
| 641 |
+
"""
|
| 642 |
+
return (isinstance(kv[1], (list, dict, tuple)), kv[0])
|
lib/python3.12/site-packages/hjson/__pycache__/__init__.cpython-312.pyc
ADDED
|
Binary file (23.2 kB). View file
|
|
|
lib/python3.12/site-packages/hjson/__pycache__/compat.cpython-312.pyc
ADDED
|
Binary file (1.93 kB). View file
|
|
|
lib/python3.12/site-packages/hjson/__pycache__/decoder.cpython-312.pyc
ADDED
|
Binary file (21 kB). View file
|
|
|
lib/python3.12/site-packages/hjson/__pycache__/encoder.cpython-312.pyc
ADDED
|
Binary file (18.8 kB). View file
|
|
|
lib/python3.12/site-packages/hjson/__pycache__/encoderH.cpython-312.pyc
ADDED
|
Binary file (20 kB). View file
|
|
|
lib/python3.12/site-packages/hjson/__pycache__/ordered_dict.cpython-312.pyc
ADDED
|
Binary file (5.99 kB). View file
|
|
|
lib/python3.12/site-packages/hjson/__pycache__/scanner.cpython-312.pyc
ADDED
|
Binary file (2.81 kB). View file
|
|
|
lib/python3.12/site-packages/hjson/__pycache__/tool.cpython-312.pyc
ADDED
|
Binary file (3 kB). View file
|
|
|
lib/python3.12/site-packages/hjson/compat.py
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Python 3 compatibility shims
|
| 2 |
+
"""
|
| 3 |
+
import sys
|
| 4 |
+
if sys.version_info[0] < 3:
|
| 5 |
+
PY3 = False
|
| 6 |
+
def b(s):
|
| 7 |
+
return s
|
| 8 |
+
def u(s):
|
| 9 |
+
return unicode(s, 'unicode_escape')
|
| 10 |
+
import cStringIO as StringIO
|
| 11 |
+
StringIO = BytesIO = StringIO.StringIO
|
| 12 |
+
text_type = unicode
|
| 13 |
+
binary_type = str
|
| 14 |
+
string_types = (basestring,)
|
| 15 |
+
integer_types = (int, long)
|
| 16 |
+
unichr = unichr
|
| 17 |
+
reload_module = reload
|
| 18 |
+
def fromhex(s):
|
| 19 |
+
return s.decode('hex')
|
| 20 |
+
|
| 21 |
+
else:
|
| 22 |
+
PY3 = True
|
| 23 |
+
if sys.version_info[:2] >= (3, 4):
|
| 24 |
+
from importlib import reload as reload_module
|
| 25 |
+
else:
|
| 26 |
+
from imp import reload as reload_module
|
| 27 |
+
import codecs
|
| 28 |
+
def b(s):
|
| 29 |
+
return codecs.latin_1_encode(s)[0]
|
| 30 |
+
def u(s):
|
| 31 |
+
return s
|
| 32 |
+
import io
|
| 33 |
+
StringIO = io.StringIO
|
| 34 |
+
BytesIO = io.BytesIO
|
| 35 |
+
text_type = str
|
| 36 |
+
binary_type = bytes
|
| 37 |
+
string_types = (str,)
|
| 38 |
+
integer_types = (int,)
|
| 39 |
+
|
| 40 |
+
def unichr(s):
|
| 41 |
+
return u(chr(s))
|
| 42 |
+
|
| 43 |
+
def fromhex(s):
|
| 44 |
+
return bytes.fromhex(s)
|
| 45 |
+
|
| 46 |
+
long_type = integer_types[-1]
|
lib/python3.12/site-packages/hjson/decoder.py
ADDED
|
@@ -0,0 +1,569 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Implementation of HjsonDecoder
|
| 2 |
+
"""
|
| 3 |
+
from __future__ import absolute_import
|
| 4 |
+
import re
|
| 5 |
+
import sys
|
| 6 |
+
import struct
|
| 7 |
+
from .compat import fromhex, b, u, text_type, binary_type, PY3, unichr
|
| 8 |
+
from .scanner import HjsonDecodeError
|
| 9 |
+
|
| 10 |
+
# NOTE (3.1.0): HjsonDecodeError may still be imported from this module for
|
| 11 |
+
# compatibility, but it was never in the __all__
|
| 12 |
+
__all__ = ['HjsonDecoder']
|
| 13 |
+
|
| 14 |
+
FLAGS = re.VERBOSE | re.MULTILINE | re.DOTALL
|
| 15 |
+
|
| 16 |
+
def _floatconstants():
|
| 17 |
+
_BYTES = fromhex('7FF80000000000007FF0000000000000')
|
| 18 |
+
# The struct module in Python 2.4 would get frexp() out of range here
|
| 19 |
+
# when an endian is specified in the format string. Fixed in Python 2.5+
|
| 20 |
+
if sys.byteorder != 'big':
|
| 21 |
+
_BYTES = _BYTES[:8][::-1] + _BYTES[8:][::-1]
|
| 22 |
+
nan, inf = struct.unpack('dd', _BYTES)
|
| 23 |
+
return nan, inf, -inf
|
| 24 |
+
|
| 25 |
+
NaN, PosInf, NegInf = _floatconstants()
|
| 26 |
+
|
| 27 |
+
WHITESPACE = ' \t\n\r'
|
| 28 |
+
PUNCTUATOR = '{}[],:'
|
| 29 |
+
|
| 30 |
+
NUMBER_RE = re.compile(r'[\t ]*(-?(?:0|[1-9]\d*))(\.\d+)?([eE][-+]?\d+)?[\t ]*')
|
| 31 |
+
STRINGCHUNK = re.compile(r'(.*?)([\'"\\\x00-\x1f])', FLAGS)
|
| 32 |
+
BACKSLASH = {
|
| 33 |
+
'"': u('"'), '\'': u('\''), '\\': u('\u005c'), '/': u('/'),
|
| 34 |
+
'b': u('\b'), 'f': u('\f'), 'n': u('\n'), 'r': u('\r'), 't': u('\t'),
|
| 35 |
+
}
|
| 36 |
+
|
| 37 |
+
DEFAULT_ENCODING = "utf-8"
|
| 38 |
+
|
| 39 |
+
def getNext(s, end):
|
| 40 |
+
while 1:
|
| 41 |
+
# Use a slice to prevent IndexError from being raised
|
| 42 |
+
ch = s[end:end + 1]
|
| 43 |
+
# Skip whitespace.
|
| 44 |
+
while ch in WHITESPACE:
|
| 45 |
+
if ch == '': return ch, end
|
| 46 |
+
end += 1
|
| 47 |
+
ch = s[end:end + 1]
|
| 48 |
+
|
| 49 |
+
# Hjson allows comments
|
| 50 |
+
ch2 = s[end + 1:end + 2]
|
| 51 |
+
if ch == '#' or ch == '/' and ch2 == '/':
|
| 52 |
+
end = getEol(s, end)
|
| 53 |
+
elif ch == '/' and ch2 == '*':
|
| 54 |
+
end += 2
|
| 55 |
+
ch = s[end]
|
| 56 |
+
while ch != '' and not (ch == '*' and s[end + 1] == '/'):
|
| 57 |
+
end += 1
|
| 58 |
+
ch = s[end]
|
| 59 |
+
if ch != '':
|
| 60 |
+
end += 2
|
| 61 |
+
else:
|
| 62 |
+
break
|
| 63 |
+
|
| 64 |
+
return ch, end
|
| 65 |
+
|
| 66 |
+
def getEol(s, end):
|
| 67 |
+
# skip until eol
|
| 68 |
+
|
| 69 |
+
while 1:
|
| 70 |
+
ch = s[end:end + 1]
|
| 71 |
+
if ch == '\r' or ch == '\n' or ch == '':
|
| 72 |
+
return end
|
| 73 |
+
end += 1
|
| 74 |
+
|
| 75 |
+
def skipIndent(s, end, n):
|
| 76 |
+
ch = s[end:end + 1]
|
| 77 |
+
while ch != '' and ch in " \t\r" and (n > 0 or n < 0):
|
| 78 |
+
end += 1
|
| 79 |
+
n -= 1
|
| 80 |
+
ch = s[end:end + 1]
|
| 81 |
+
return end
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
def scanstring(s, end, encoding=None, strict=True,
|
| 85 |
+
_b=BACKSLASH, _m=STRINGCHUNK.match, _join=u('').join,
|
| 86 |
+
_PY3=PY3, _maxunicode=sys.maxunicode):
|
| 87 |
+
"""Scan the string s for a JSON string. End is the index of the
|
| 88 |
+
character in s after the quote that started the JSON string.
|
| 89 |
+
Unescapes all valid JSON string escape sequences and raises ValueError
|
| 90 |
+
on attempt to decode an invalid string. If strict is False then literal
|
| 91 |
+
control characters are allowed in the string.
|
| 92 |
+
|
| 93 |
+
Returns a tuple of the decoded string and the index of the character in s
|
| 94 |
+
after the end quote."""
|
| 95 |
+
if encoding is None:
|
| 96 |
+
encoding = DEFAULT_ENCODING
|
| 97 |
+
chunks = []
|
| 98 |
+
_append = chunks.append
|
| 99 |
+
begin = end - 1
|
| 100 |
+
# callers make sure that string starts with " or '
|
| 101 |
+
exitCh = s[begin]
|
| 102 |
+
while 1:
|
| 103 |
+
chunk = _m(s, end)
|
| 104 |
+
if chunk is None:
|
| 105 |
+
raise HjsonDecodeError(
|
| 106 |
+
"Unterminated string starting at", s, begin)
|
| 107 |
+
end = chunk.end()
|
| 108 |
+
content, terminator = chunk.groups()
|
| 109 |
+
# Content is contains zero or more unescaped string characters
|
| 110 |
+
if content:
|
| 111 |
+
if not _PY3 and not isinstance(content, text_type):
|
| 112 |
+
content = text_type(content, encoding)
|
| 113 |
+
_append(content)
|
| 114 |
+
# Terminator is the end of string, a literal control character,
|
| 115 |
+
# or a backslash denoting that an escape sequence follows
|
| 116 |
+
if terminator == exitCh:
|
| 117 |
+
break
|
| 118 |
+
elif terminator == '"' or terminator == '\'':
|
| 119 |
+
_append(terminator)
|
| 120 |
+
continue
|
| 121 |
+
elif terminator != '\\':
|
| 122 |
+
if strict:
|
| 123 |
+
msg = "Invalid control character %r at"
|
| 124 |
+
raise HjsonDecodeError(msg, s, end)
|
| 125 |
+
else:
|
| 126 |
+
_append(terminator)
|
| 127 |
+
continue
|
| 128 |
+
try:
|
| 129 |
+
esc = s[end]
|
| 130 |
+
except IndexError:
|
| 131 |
+
raise HjsonDecodeError(
|
| 132 |
+
"Unterminated string starting at", s, begin)
|
| 133 |
+
# If not a unicode escape sequence, must be in the lookup table
|
| 134 |
+
if esc != 'u':
|
| 135 |
+
try:
|
| 136 |
+
char = _b[esc]
|
| 137 |
+
except KeyError:
|
| 138 |
+
msg = "Invalid \\X escape sequence %r"
|
| 139 |
+
raise HjsonDecodeError(msg, s, end)
|
| 140 |
+
end += 1
|
| 141 |
+
else:
|
| 142 |
+
# Unicode escape sequence
|
| 143 |
+
msg = "Invalid \\uXXXX escape sequence"
|
| 144 |
+
esc = s[end + 1:end + 5]
|
| 145 |
+
escX = esc[1:2]
|
| 146 |
+
if len(esc) != 4 or escX == 'x' or escX == 'X':
|
| 147 |
+
raise HjsonDecodeError(msg, s, end - 1)
|
| 148 |
+
try:
|
| 149 |
+
uni = int(esc, 16)
|
| 150 |
+
except ValueError:
|
| 151 |
+
raise HjsonDecodeError(msg, s, end - 1)
|
| 152 |
+
end += 5
|
| 153 |
+
# Check for surrogate pair on UCS-4 systems
|
| 154 |
+
# Note that this will join high/low surrogate pairs
|
| 155 |
+
# but will also pass unpaired surrogates through
|
| 156 |
+
if (_maxunicode > 65535 and
|
| 157 |
+
uni & 0xfc00 == 0xd800 and
|
| 158 |
+
s[end:end + 2] == '\\u'):
|
| 159 |
+
esc2 = s[end + 2:end + 6]
|
| 160 |
+
escX = esc2[1:2]
|
| 161 |
+
if len(esc2) == 4 and not (escX == 'x' or escX == 'X'):
|
| 162 |
+
try:
|
| 163 |
+
uni2 = int(esc2, 16)
|
| 164 |
+
except ValueError:
|
| 165 |
+
raise HjsonDecodeError(msg, s, end)
|
| 166 |
+
if uni2 & 0xfc00 == 0xdc00:
|
| 167 |
+
uni = 0x10000 + (((uni - 0xd800) << 10) |
|
| 168 |
+
(uni2 - 0xdc00))
|
| 169 |
+
end += 6
|
| 170 |
+
char = unichr(uni)
|
| 171 |
+
# Append the unescaped character
|
| 172 |
+
_append(char)
|
| 173 |
+
return _join(chunks), end
|
| 174 |
+
|
| 175 |
+
def mlscanstring(s, end):
|
| 176 |
+
"""Scan a multiline string"""
|
| 177 |
+
|
| 178 |
+
string = ""
|
| 179 |
+
triple = 0
|
| 180 |
+
|
| 181 |
+
# we are at ''' - get indent
|
| 182 |
+
indent = 0
|
| 183 |
+
while 1:
|
| 184 |
+
ch = s[end-indent-1]
|
| 185 |
+
if ch == '\n': break
|
| 186 |
+
indent += 1
|
| 187 |
+
|
| 188 |
+
# skip white/to (newline)
|
| 189 |
+
end = skipIndent(s, end + 3, -1)
|
| 190 |
+
|
| 191 |
+
ch = s[end]
|
| 192 |
+
if ch == '\n': end = skipIndent(s, end + 1, indent)
|
| 193 |
+
|
| 194 |
+
# When parsing multiline string values, we must look for ' characters
|
| 195 |
+
while 1:
|
| 196 |
+
ch = s[end:end + 1]
|
| 197 |
+
if ch == '':
|
| 198 |
+
raise HjsonDecodeError("Bad multiline string", s, end);
|
| 199 |
+
elif ch == '\'':
|
| 200 |
+
triple += 1
|
| 201 |
+
end += 1
|
| 202 |
+
if triple == 3:
|
| 203 |
+
if string and string[-1] == '\n':
|
| 204 |
+
string = string[:-1] # remove last EOL
|
| 205 |
+
return string, end
|
| 206 |
+
else:
|
| 207 |
+
continue
|
| 208 |
+
else:
|
| 209 |
+
while triple > 0:
|
| 210 |
+
string += '\''
|
| 211 |
+
triple -= 1
|
| 212 |
+
|
| 213 |
+
if ch == '\n':
|
| 214 |
+
string += ch
|
| 215 |
+
end = skipIndent(s, end + 1, indent)
|
| 216 |
+
else:
|
| 217 |
+
if ch != '\r':
|
| 218 |
+
string += ch
|
| 219 |
+
end += 1
|
| 220 |
+
|
| 221 |
+
def scantfnns(context, s, end):
|
| 222 |
+
"""Scan s until eol. return string, True, False or None"""
|
| 223 |
+
|
| 224 |
+
chf, begin = getNext(s, end)
|
| 225 |
+
end = begin
|
| 226 |
+
|
| 227 |
+
if chf in PUNCTUATOR:
|
| 228 |
+
raise HjsonDecodeError("Found a punctuator character when expecting a quoteless string (check your syntax)", s, end);
|
| 229 |
+
|
| 230 |
+
while 1:
|
| 231 |
+
ch = s[end:end + 1]
|
| 232 |
+
|
| 233 |
+
isEol = ch == '\r' or ch == '\n' or ch == ''
|
| 234 |
+
if isEol or ch == ',' or \
|
| 235 |
+
ch == '}' or ch == ']' or \
|
| 236 |
+
ch == '#' or \
|
| 237 |
+
ch == '/' and (s[end + 1:end + 2] == '/' or s[end + 1:end + 2] == '*'):
|
| 238 |
+
|
| 239 |
+
m = None
|
| 240 |
+
mend = end
|
| 241 |
+
if next: mend -= 1
|
| 242 |
+
|
| 243 |
+
if chf == 'n' and s[begin:end].strip() == 'null':
|
| 244 |
+
return None, end
|
| 245 |
+
elif chf == 't' and s[begin:end].strip() == 'true':
|
| 246 |
+
return True, end
|
| 247 |
+
elif chf == 'f' and s[begin:end].strip() == 'false':
|
| 248 |
+
return False, end
|
| 249 |
+
elif chf == '-' or chf >= '0' and chf <= '9':
|
| 250 |
+
m = NUMBER_RE.match(s, begin)
|
| 251 |
+
|
| 252 |
+
if m is not None and m.end() == end:
|
| 253 |
+
integer, frac, exp = m.groups()
|
| 254 |
+
if frac or exp:
|
| 255 |
+
res = context.parse_float(integer + (frac or '') + (exp or ''))
|
| 256 |
+
if int(res) == res and abs(res)<1e10: res = int(res)
|
| 257 |
+
else:
|
| 258 |
+
res = context.parse_int(integer)
|
| 259 |
+
return res, end
|
| 260 |
+
|
| 261 |
+
if isEol:
|
| 262 |
+
return s[begin:end].strip(), end
|
| 263 |
+
|
| 264 |
+
end += 1
|
| 265 |
+
|
| 266 |
+
def scanKeyName(s, end, encoding=None, strict=True):
|
| 267 |
+
"""Scan the string s for a JSON/Hjson key. see scanstring"""
|
| 268 |
+
|
| 269 |
+
ch, end = getNext(s, end)
|
| 270 |
+
|
| 271 |
+
if ch == '"' or ch == '\'':
|
| 272 |
+
return scanstring(s, end + 1, encoding, strict)
|
| 273 |
+
|
| 274 |
+
begin = end
|
| 275 |
+
space = -1
|
| 276 |
+
while 1:
|
| 277 |
+
ch = s[end:end + 1]
|
| 278 |
+
|
| 279 |
+
if ch == '':
|
| 280 |
+
raise HjsonDecodeError("Bad key name (eof)", s, end);
|
| 281 |
+
elif ch == ':':
|
| 282 |
+
if begin == end:
|
| 283 |
+
raise HjsonDecodeError("Found ':' but no key name (for an empty key name use quotes)", s, begin)
|
| 284 |
+
elif space >= 0:
|
| 285 |
+
if space != end - 1: raise HjsonDecodeError("Found whitespace in your key name (use quotes to include)", s, space)
|
| 286 |
+
return s[begin:end].rstrip(), end
|
| 287 |
+
else:
|
| 288 |
+
return s[begin:end], end
|
| 289 |
+
elif ch in WHITESPACE:
|
| 290 |
+
if space < 0 or space == end - 1: space = end
|
| 291 |
+
elif ch == '{' or ch == '}' or ch == '[' or ch == ']' or ch == ',':
|
| 292 |
+
raise HjsonDecodeError("Found '" + ch + "' where a key name was expected (check your syntax or use quotes if the key name includes {}[],: or whitespace)", s, begin)
|
| 293 |
+
end += 1
|
| 294 |
+
|
| 295 |
+
def make_scanner(context):
|
| 296 |
+
parse_object = context.parse_object
|
| 297 |
+
parse_array = context.parse_array
|
| 298 |
+
parse_string = context.parse_string
|
| 299 |
+
parse_mlstring = context.parse_mlstring
|
| 300 |
+
parse_tfnns = context.parse_tfnns
|
| 301 |
+
encoding = context.encoding
|
| 302 |
+
strict = context.strict
|
| 303 |
+
object_hook = context.object_hook
|
| 304 |
+
object_pairs_hook = context.object_pairs_hook
|
| 305 |
+
memo = context.memo
|
| 306 |
+
|
| 307 |
+
def _scan_once(string, idx):
|
| 308 |
+
try:
|
| 309 |
+
ch = string[idx]
|
| 310 |
+
except IndexError:
|
| 311 |
+
raise HjsonDecodeError('Expecting value', string, idx)
|
| 312 |
+
|
| 313 |
+
if ch == '"' or ch == '\'':
|
| 314 |
+
if string[idx:idx + 3] == '\'\'\'':
|
| 315 |
+
return parse_mlstring(string, idx)
|
| 316 |
+
else:
|
| 317 |
+
return parse_string(string, idx + 1, encoding, strict)
|
| 318 |
+
elif ch == '{':
|
| 319 |
+
return parse_object((string, idx + 1), encoding, strict,
|
| 320 |
+
_scan_once, object_hook, object_pairs_hook, memo)
|
| 321 |
+
elif ch == '[':
|
| 322 |
+
return parse_array((string, idx + 1), _scan_once)
|
| 323 |
+
|
| 324 |
+
return parse_tfnns(context, string, idx)
|
| 325 |
+
|
| 326 |
+
def scan_once(string, idx):
|
| 327 |
+
if idx < 0: raise HjsonDecodeError('Expecting value', string, idx)
|
| 328 |
+
try:
|
| 329 |
+
return _scan_once(string, idx)
|
| 330 |
+
finally:
|
| 331 |
+
memo.clear()
|
| 332 |
+
|
| 333 |
+
def scan_object_once(string, idx):
|
| 334 |
+
if idx < 0: raise HjsonDecodeError('Expecting value', string, idx)
|
| 335 |
+
try:
|
| 336 |
+
return parse_object((string, idx), encoding, strict,
|
| 337 |
+
_scan_once, object_hook, object_pairs_hook, memo, True)
|
| 338 |
+
finally:
|
| 339 |
+
memo.clear()
|
| 340 |
+
|
| 341 |
+
return scan_once, scan_object_once
|
| 342 |
+
|
| 343 |
+
|
| 344 |
+
def JSONObject(state, encoding, strict, scan_once, object_hook,
|
| 345 |
+
object_pairs_hook, memo=None, objectWithoutBraces=False):
|
| 346 |
+
(s, end) = state
|
| 347 |
+
# Backwards compatibility
|
| 348 |
+
if memo is None:
|
| 349 |
+
memo = {}
|
| 350 |
+
memo_get = memo.setdefault
|
| 351 |
+
pairs = []
|
| 352 |
+
|
| 353 |
+
ch, end = getNext(s, end)
|
| 354 |
+
|
| 355 |
+
# Trivial empty object
|
| 356 |
+
if not objectWithoutBraces and ch == '}':
|
| 357 |
+
if object_pairs_hook is not None:
|
| 358 |
+
result = object_pairs_hook(pairs)
|
| 359 |
+
return result, end + 1
|
| 360 |
+
pairs = {}
|
| 361 |
+
if object_hook is not None:
|
| 362 |
+
pairs = object_hook(pairs)
|
| 363 |
+
return pairs, end + 1
|
| 364 |
+
|
| 365 |
+
while True:
|
| 366 |
+
key, end = scanKeyName(s, end, encoding, strict)
|
| 367 |
+
key = memo_get(key, key)
|
| 368 |
+
|
| 369 |
+
ch, end = getNext(s, end)
|
| 370 |
+
if ch != ':':
|
| 371 |
+
raise HjsonDecodeError("Expecting ':' delimiter", s, end)
|
| 372 |
+
|
| 373 |
+
ch, end = getNext(s, end + 1)
|
| 374 |
+
|
| 375 |
+
value, end = scan_once(s, end)
|
| 376 |
+
pairs.append((key, value))
|
| 377 |
+
|
| 378 |
+
ch, end = getNext(s, end)
|
| 379 |
+
|
| 380 |
+
if ch == ',':
|
| 381 |
+
ch, end = getNext(s, end + 1)
|
| 382 |
+
|
| 383 |
+
if objectWithoutBraces:
|
| 384 |
+
if ch == '': break;
|
| 385 |
+
else:
|
| 386 |
+
if ch == '}':
|
| 387 |
+
end += 1
|
| 388 |
+
break
|
| 389 |
+
|
| 390 |
+
ch, end = getNext(s, end)
|
| 391 |
+
|
| 392 |
+
if object_pairs_hook is not None:
|
| 393 |
+
result = object_pairs_hook(pairs)
|
| 394 |
+
return result, end
|
| 395 |
+
pairs = dict(pairs)
|
| 396 |
+
if object_hook is not None:
|
| 397 |
+
pairs = object_hook(pairs)
|
| 398 |
+
return pairs, end
|
| 399 |
+
|
| 400 |
+
def JSONArray(state, scan_once):
|
| 401 |
+
(s, end) = state
|
| 402 |
+
values = []
|
| 403 |
+
|
| 404 |
+
ch, end = getNext(s, end)
|
| 405 |
+
|
| 406 |
+
# Look-ahead for trivial empty array
|
| 407 |
+
if ch == ']':
|
| 408 |
+
return values, end + 1
|
| 409 |
+
elif ch == '':
|
| 410 |
+
raise HjsonDecodeError("End of input while parsing an array (did you forget a closing ']'?)", s, end)
|
| 411 |
+
_append = values.append
|
| 412 |
+
while True:
|
| 413 |
+
value, end = scan_once(s, end)
|
| 414 |
+
_append(value)
|
| 415 |
+
|
| 416 |
+
ch, end = getNext(s, end)
|
| 417 |
+
if ch == ',':
|
| 418 |
+
ch, end = getNext(s, end + 1)
|
| 419 |
+
|
| 420 |
+
if ch == ']':
|
| 421 |
+
end += 1
|
| 422 |
+
break
|
| 423 |
+
|
| 424 |
+
ch, end = getNext(s, end)
|
| 425 |
+
|
| 426 |
+
return values, end
|
| 427 |
+
|
| 428 |
+
|
| 429 |
+
class HjsonDecoder(object):
|
| 430 |
+
"""Hjson decoder
|
| 431 |
+
|
| 432 |
+
Performs the following translations in decoding by default:
|
| 433 |
+
|
| 434 |
+
+---------------+-------------------+
|
| 435 |
+
| JSON | Python |
|
| 436 |
+
+===============+===================+
|
| 437 |
+
| object | dict |
|
| 438 |
+
+---------------+-------------------+
|
| 439 |
+
| array | list |
|
| 440 |
+
+---------------+-------------------+
|
| 441 |
+
| string | str, unicode |
|
| 442 |
+
+---------------+-------------------+
|
| 443 |
+
| number (int) | int, long |
|
| 444 |
+
+---------------+-------------------+
|
| 445 |
+
| number (real) | float |
|
| 446 |
+
+---------------+-------------------+
|
| 447 |
+
| true | True |
|
| 448 |
+
+---------------+-------------------+
|
| 449 |
+
| false | False |
|
| 450 |
+
+---------------+-------------------+
|
| 451 |
+
| null | None |
|
| 452 |
+
+---------------+-------------------+
|
| 453 |
+
|
| 454 |
+
"""
|
| 455 |
+
|
| 456 |
+
def __init__(self, encoding=None, object_hook=None, parse_float=None,
|
| 457 |
+
parse_int=None, strict=True,
|
| 458 |
+
object_pairs_hook=None):
|
| 459 |
+
"""
|
| 460 |
+
*encoding* determines the encoding used to interpret any
|
| 461 |
+
:class:`str` objects decoded by this instance (``'utf-8'`` by
|
| 462 |
+
default). It has no effect when decoding :class:`unicode` objects.
|
| 463 |
+
|
| 464 |
+
Note that currently only encodings that are a superset of ASCII work,
|
| 465 |
+
strings of other encodings should be passed in as :class:`unicode`.
|
| 466 |
+
|
| 467 |
+
*object_hook*, if specified, will be called with the result of every
|
| 468 |
+
JSON object decoded and its return value will be used in place of the
|
| 469 |
+
given :class:`dict`. This can be used to provide custom
|
| 470 |
+
deserializations (e.g. to support JSON-RPC class hinting).
|
| 471 |
+
|
| 472 |
+
*object_pairs_hook* is an optional function that will be called with
|
| 473 |
+
the result of any object literal decode with an ordered list of pairs.
|
| 474 |
+
The return value of *object_pairs_hook* will be used instead of the
|
| 475 |
+
:class:`dict`. This feature can be used to implement custom decoders
|
| 476 |
+
that rely on the order that the key and value pairs are decoded (for
|
| 477 |
+
example, :func:`collections.OrderedDict` will remember the order of
|
| 478 |
+
insertion). If *object_hook* is also defined, the *object_pairs_hook*
|
| 479 |
+
takes priority.
|
| 480 |
+
|
| 481 |
+
*parse_float*, if specified, will be called with the string of every
|
| 482 |
+
JSON float to be decoded. By default, this is equivalent to
|
| 483 |
+
``float(num_str)``. This can be used to use another datatype or parser
|
| 484 |
+
for JSON floats (e.g. :class:`decimal.Decimal`).
|
| 485 |
+
|
| 486 |
+
*parse_int*, if specified, will be called with the string of every
|
| 487 |
+
JSON int to be decoded. By default, this is equivalent to
|
| 488 |
+
``int(num_str)``. This can be used to use another datatype or parser
|
| 489 |
+
for JSON integers (e.g. :class:`float`).
|
| 490 |
+
|
| 491 |
+
*strict* controls the parser's behavior when it encounters an
|
| 492 |
+
invalid control character in a string. The default setting of
|
| 493 |
+
``True`` means that unescaped control characters are parse errors, if
|
| 494 |
+
``False`` then control characters will be allowed in strings.
|
| 495 |
+
|
| 496 |
+
"""
|
| 497 |
+
if encoding is None:
|
| 498 |
+
encoding = DEFAULT_ENCODING
|
| 499 |
+
self.encoding = encoding
|
| 500 |
+
self.object_hook = object_hook
|
| 501 |
+
self.object_pairs_hook = object_pairs_hook
|
| 502 |
+
self.parse_float = parse_float or float
|
| 503 |
+
self.parse_int = parse_int or int
|
| 504 |
+
self.strict = strict
|
| 505 |
+
self.parse_object = JSONObject
|
| 506 |
+
self.parse_array = JSONArray
|
| 507 |
+
self.parse_string = scanstring
|
| 508 |
+
self.parse_mlstring = mlscanstring
|
| 509 |
+
self.parse_tfnns = scantfnns
|
| 510 |
+
self.memo = {}
|
| 511 |
+
(self.scan_once, self.scan_object_once) = make_scanner(self)
|
| 512 |
+
|
| 513 |
+
def decode(self, s, _PY3=PY3):
|
| 514 |
+
"""Return the Python representation of ``s`` (a ``str`` or ``unicode``
|
| 515 |
+
instance containing a JSON document)
|
| 516 |
+
|
| 517 |
+
"""
|
| 518 |
+
if _PY3 and isinstance(s, binary_type):
|
| 519 |
+
s = s.decode(self.encoding)
|
| 520 |
+
obj, end = self.raw_decode(s)
|
| 521 |
+
ch, end = getNext(s, end)
|
| 522 |
+
if end != len(s):
|
| 523 |
+
raise HjsonDecodeError("Extra data", s, end, len(s))
|
| 524 |
+
return obj
|
| 525 |
+
|
| 526 |
+
def raw_decode(self, s, idx=0, _PY3=PY3):
|
| 527 |
+
"""Decode a JSON document from ``s`` (a ``str`` or ``unicode``
|
| 528 |
+
beginning with a JSON document) and return a 2-tuple of the Python
|
| 529 |
+
representation and the index in ``s`` where the document ended.
|
| 530 |
+
Optionally, ``idx`` can be used to specify an offset in ``s`` where
|
| 531 |
+
the JSON document begins.
|
| 532 |
+
|
| 533 |
+
This can be used to decode a JSON document from a string that may
|
| 534 |
+
have extraneous data at the end.
|
| 535 |
+
|
| 536 |
+
"""
|
| 537 |
+
if idx < 0:
|
| 538 |
+
# Ensure that raw_decode bails on negative indexes, the regex
|
| 539 |
+
# would otherwise mask this behavior. #98
|
| 540 |
+
raise HjsonDecodeError('Expecting value', s, idx)
|
| 541 |
+
if _PY3 and not isinstance(s, text_type):
|
| 542 |
+
raise TypeError("Input string must be text")
|
| 543 |
+
# strip UTF-8 bom
|
| 544 |
+
if len(s) > idx:
|
| 545 |
+
ord0 = ord(s[idx])
|
| 546 |
+
if ord0 == 0xfeff:
|
| 547 |
+
idx += 1
|
| 548 |
+
elif ord0 == 0xef and s[idx:idx + 3] == '\xef\xbb\xbf':
|
| 549 |
+
idx += 3
|
| 550 |
+
|
| 551 |
+
start_index = idx
|
| 552 |
+
ch, idx = getNext(s, idx)
|
| 553 |
+
|
| 554 |
+
# If blank or comment only file, return dict
|
| 555 |
+
if start_index == 0 and ch == '':
|
| 556 |
+
return {}, 0
|
| 557 |
+
|
| 558 |
+
if ch == '{' or ch == '[':
|
| 559 |
+
return self.scan_once(s, idx)
|
| 560 |
+
else:
|
| 561 |
+
# assume we have a root object without braces
|
| 562 |
+
try:
|
| 563 |
+
return self.scan_object_once(s, idx)
|
| 564 |
+
except HjsonDecodeError as e:
|
| 565 |
+
# test if we are dealing with a single JSON value instead (true/false/null/num/"")
|
| 566 |
+
try:
|
| 567 |
+
return self.scan_once(s, idx)
|
| 568 |
+
except:
|
| 569 |
+
raise e
|
lib/python3.12/site-packages/hjson/encoder.py
ADDED
|
@@ -0,0 +1,521 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Implementation of JSONEncoder
|
| 2 |
+
"""
|
| 3 |
+
from __future__ import absolute_import
|
| 4 |
+
import re
|
| 5 |
+
from operator import itemgetter
|
| 6 |
+
from decimal import Decimal
|
| 7 |
+
from .compat import u, unichr, binary_type, string_types, integer_types, PY3
|
| 8 |
+
from .decoder import PosInf
|
| 9 |
+
|
| 10 |
+
#ESCAPE = re.compile(ur'[\x00-\x1f\\"\b\f\n\r\t\u2028\u2029]')
|
| 11 |
+
# This is required because u() will mangle the string and ur'' isn't valid
|
| 12 |
+
# python3 syntax
|
| 13 |
+
ESCAPE = re.compile(u'[\\x00-\\x1f\\\\"\\b\\f\\n\\r\\t\u2028\u2029]')
|
| 14 |
+
ESCAPE_ASCII = re.compile(r'([\\"]|[^\ -~])')
|
| 15 |
+
HAS_UTF8 = re.compile(r'[\x80-\xff]')
|
| 16 |
+
ESCAPE_DCT = {
|
| 17 |
+
'\\': '\\\\',
|
| 18 |
+
'"': '\\"',
|
| 19 |
+
'\b': '\\b',
|
| 20 |
+
'\f': '\\f',
|
| 21 |
+
'\n': '\\n',
|
| 22 |
+
'\r': '\\r',
|
| 23 |
+
'\t': '\\t',
|
| 24 |
+
}
|
| 25 |
+
for i in range(0x20):
|
| 26 |
+
#ESCAPE_DCT.setdefault(chr(i), '\\u{0:04x}'.format(i))
|
| 27 |
+
ESCAPE_DCT.setdefault(chr(i), '\\u%04x' % (i,))
|
| 28 |
+
for i in [0x2028, 0x2029]:
|
| 29 |
+
ESCAPE_DCT.setdefault(unichr(i), '\\u%04x' % (i,))
|
| 30 |
+
|
| 31 |
+
FLOAT_REPR = repr
|
| 32 |
+
|
| 33 |
+
def encode_basestring(s, _PY3=PY3, _q=u('"')):
|
| 34 |
+
"""Return a JSON representation of a Python string
|
| 35 |
+
|
| 36 |
+
"""
|
| 37 |
+
if _PY3:
|
| 38 |
+
if isinstance(s, binary_type):
|
| 39 |
+
s = s.decode('utf-8')
|
| 40 |
+
else:
|
| 41 |
+
if isinstance(s, str) and HAS_UTF8.search(s) is not None:
|
| 42 |
+
s = s.decode('utf-8')
|
| 43 |
+
def replace(match):
|
| 44 |
+
return ESCAPE_DCT[match.group(0)]
|
| 45 |
+
return _q + ESCAPE.sub(replace, s) + _q
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
def py_encode_basestring_ascii(s, _PY3=PY3):
|
| 49 |
+
"""Return an ASCII-only JSON representation of a Python string
|
| 50 |
+
|
| 51 |
+
"""
|
| 52 |
+
if _PY3:
|
| 53 |
+
if isinstance(s, binary_type):
|
| 54 |
+
s = s.decode('utf-8')
|
| 55 |
+
else:
|
| 56 |
+
if isinstance(s, str) and HAS_UTF8.search(s) is not None:
|
| 57 |
+
s = s.decode('utf-8')
|
| 58 |
+
def replace(match):
|
| 59 |
+
s = match.group(0)
|
| 60 |
+
try:
|
| 61 |
+
return ESCAPE_DCT[s]
|
| 62 |
+
except KeyError:
|
| 63 |
+
n = ord(s)
|
| 64 |
+
if n < 0x10000:
|
| 65 |
+
#return '\\u{0:04x}'.format(n)
|
| 66 |
+
return '\\u%04x' % (n,)
|
| 67 |
+
else:
|
| 68 |
+
# surrogate pair
|
| 69 |
+
n -= 0x10000
|
| 70 |
+
s1 = 0xd800 | ((n >> 10) & 0x3ff)
|
| 71 |
+
s2 = 0xdc00 | (n & 0x3ff)
|
| 72 |
+
#return '\\u{0:04x}\\u{1:04x}'.format(s1, s2)
|
| 73 |
+
return '\\u%04x\\u%04x' % (s1, s2)
|
| 74 |
+
return '"' + str(ESCAPE_ASCII.sub(replace, s)) + '"'
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
encode_basestring_ascii = (
|
| 78 |
+
py_encode_basestring_ascii)
|
| 79 |
+
|
| 80 |
+
class JSONEncoder(object):
|
| 81 |
+
"""Extensible JSON <http://json.org> encoder for Python data structures.
|
| 82 |
+
|
| 83 |
+
Supports the following objects and types by default:
|
| 84 |
+
|
| 85 |
+
+-------------------+---------------+
|
| 86 |
+
| Python | JSON |
|
| 87 |
+
+===================+===============+
|
| 88 |
+
| dict, namedtuple | object |
|
| 89 |
+
+-------------------+---------------+
|
| 90 |
+
| list, tuple | array |
|
| 91 |
+
+-------------------+---------------+
|
| 92 |
+
| str, unicode | string |
|
| 93 |
+
+-------------------+---------------+
|
| 94 |
+
| int, long, float | number |
|
| 95 |
+
+-------------------+---------------+
|
| 96 |
+
| True | true |
|
| 97 |
+
+-------------------+---------------+
|
| 98 |
+
| False | false |
|
| 99 |
+
+-------------------+---------------+
|
| 100 |
+
| None | null |
|
| 101 |
+
+-------------------+---------------+
|
| 102 |
+
|
| 103 |
+
To extend this to recognize other objects, subclass and implement a
|
| 104 |
+
``.default()`` method with another method that returns a serializable
|
| 105 |
+
object for ``o`` if possible, otherwise it should call the superclass
|
| 106 |
+
implementation (to raise ``TypeError``).
|
| 107 |
+
|
| 108 |
+
"""
|
| 109 |
+
item_separator = ', '
|
| 110 |
+
key_separator = ': '
|
| 111 |
+
|
| 112 |
+
def __init__(self, skipkeys=False, ensure_ascii=True,
|
| 113 |
+
check_circular=True, sort_keys=False,
|
| 114 |
+
indent=None, separators=None, encoding='utf-8', default=None,
|
| 115 |
+
use_decimal=True, namedtuple_as_object=True,
|
| 116 |
+
tuple_as_array=True, bigint_as_string=False,
|
| 117 |
+
item_sort_key=None, for_json=False,
|
| 118 |
+
int_as_string_bitcount=None):
|
| 119 |
+
"""Constructor for JSONEncoder, with sensible defaults.
|
| 120 |
+
|
| 121 |
+
If skipkeys is false, then it is a TypeError to attempt
|
| 122 |
+
encoding of keys that are not str, int, long, float or None. If
|
| 123 |
+
skipkeys is True, such items are simply skipped.
|
| 124 |
+
|
| 125 |
+
If ensure_ascii is true, the output is guaranteed to be str
|
| 126 |
+
objects with all incoming unicode characters escaped. If
|
| 127 |
+
ensure_ascii is false, the output will be unicode object.
|
| 128 |
+
|
| 129 |
+
If check_circular is true, then lists, dicts, and custom encoded
|
| 130 |
+
objects will be checked for circular references during encoding to
|
| 131 |
+
prevent an infinite recursion (which would cause an OverflowError).
|
| 132 |
+
Otherwise, no such check takes place.
|
| 133 |
+
|
| 134 |
+
If sort_keys is true, then the output of dictionaries will be
|
| 135 |
+
sorted by key; this is useful for regression tests to ensure
|
| 136 |
+
that JSON serializations can be compared on a day-to-day basis.
|
| 137 |
+
|
| 138 |
+
If indent is a string, then JSON array elements and object members
|
| 139 |
+
will be pretty-printed with a newline followed by that string repeated
|
| 140 |
+
for each level of nesting. ``None`` (the default) selects the most compact
|
| 141 |
+
representation without any newlines. For backwards compatibility with
|
| 142 |
+
versions of hjson earlier than 2.1.0, an integer is also accepted
|
| 143 |
+
and is converted to a string with that many spaces.
|
| 144 |
+
|
| 145 |
+
If specified, separators should be an (item_separator, key_separator)
|
| 146 |
+
tuple. The default is (', ', ': ') if *indent* is ``None`` and
|
| 147 |
+
(',', ': ') otherwise. To get the most compact JSON representation,
|
| 148 |
+
you should specify (',', ':') to eliminate whitespace.
|
| 149 |
+
|
| 150 |
+
If specified, default is a function that gets called for objects
|
| 151 |
+
that can't otherwise be serialized. It should return a JSON encodable
|
| 152 |
+
version of the object or raise a ``TypeError``.
|
| 153 |
+
|
| 154 |
+
If encoding is not None, then all input strings will be
|
| 155 |
+
transformed into unicode using that encoding prior to JSON-encoding.
|
| 156 |
+
The default is UTF-8.
|
| 157 |
+
|
| 158 |
+
If use_decimal is true (not the default), ``decimal.Decimal`` will
|
| 159 |
+
be supported directly by the encoder. For the inverse, decode JSON
|
| 160 |
+
with ``parse_float=decimal.Decimal``.
|
| 161 |
+
|
| 162 |
+
If namedtuple_as_object is true (the default), objects with
|
| 163 |
+
``_asdict()`` methods will be encoded as JSON objects.
|
| 164 |
+
|
| 165 |
+
If tuple_as_array is true (the default), tuple (and subclasses) will
|
| 166 |
+
be encoded as JSON arrays.
|
| 167 |
+
|
| 168 |
+
If bigint_as_string is true (not the default), ints 2**53 and higher
|
| 169 |
+
or lower than -2**53 will be encoded as strings. This is to avoid the
|
| 170 |
+
rounding that happens in Javascript otherwise.
|
| 171 |
+
|
| 172 |
+
If int_as_string_bitcount is a positive number (n), then int of size
|
| 173 |
+
greater than or equal to 2**n or lower than or equal to -2**n will be
|
| 174 |
+
encoded as strings.
|
| 175 |
+
|
| 176 |
+
If specified, item_sort_key is a callable used to sort the items in
|
| 177 |
+
each dictionary. This is useful if you want to sort items other than
|
| 178 |
+
in alphabetical order by key.
|
| 179 |
+
|
| 180 |
+
If for_json is true (not the default), objects with a ``for_json()``
|
| 181 |
+
method will use the return value of that method for encoding as JSON
|
| 182 |
+
instead of the object.
|
| 183 |
+
|
| 184 |
+
"""
|
| 185 |
+
|
| 186 |
+
self.skipkeys = skipkeys
|
| 187 |
+
self.ensure_ascii = ensure_ascii
|
| 188 |
+
self.check_circular = check_circular
|
| 189 |
+
self.sort_keys = sort_keys
|
| 190 |
+
self.use_decimal = use_decimal
|
| 191 |
+
self.namedtuple_as_object = namedtuple_as_object
|
| 192 |
+
self.tuple_as_array = tuple_as_array
|
| 193 |
+
self.bigint_as_string = bigint_as_string
|
| 194 |
+
self.item_sort_key = item_sort_key
|
| 195 |
+
self.for_json = for_json
|
| 196 |
+
self.int_as_string_bitcount = int_as_string_bitcount
|
| 197 |
+
if indent is not None and not isinstance(indent, string_types):
|
| 198 |
+
indent = indent * ' '
|
| 199 |
+
self.indent = indent
|
| 200 |
+
if separators is not None:
|
| 201 |
+
self.item_separator, self.key_separator = separators
|
| 202 |
+
elif indent is not None:
|
| 203 |
+
self.item_separator = ','
|
| 204 |
+
if default is not None:
|
| 205 |
+
self.default = default
|
| 206 |
+
self.encoding = encoding
|
| 207 |
+
|
| 208 |
+
def default(self, o):
|
| 209 |
+
"""Implement this method in a subclass such that it returns
|
| 210 |
+
a serializable object for ``o``, or calls the base implementation
|
| 211 |
+
(to raise a ``TypeError``).
|
| 212 |
+
|
| 213 |
+
For example, to support arbitrary iterators, you could
|
| 214 |
+
implement default like this::
|
| 215 |
+
|
| 216 |
+
def default(self, o):
|
| 217 |
+
try:
|
| 218 |
+
iterable = iter(o)
|
| 219 |
+
except TypeError:
|
| 220 |
+
pass
|
| 221 |
+
else:
|
| 222 |
+
return list(iterable)
|
| 223 |
+
return JSONEncoder.default(self, o)
|
| 224 |
+
|
| 225 |
+
"""
|
| 226 |
+
raise TypeError(repr(o) + " is not JSON serializable")
|
| 227 |
+
|
| 228 |
+
def encode(self, o):
|
| 229 |
+
"""Return a JSON string representation of a Python data structure.
|
| 230 |
+
|
| 231 |
+
>>> from hjson import JSONEncoder
|
| 232 |
+
>>> JSONEncoder().encode({"foo": ["bar", "baz"]})
|
| 233 |
+
'{"foo": ["bar", "baz"]}'
|
| 234 |
+
|
| 235 |
+
"""
|
| 236 |
+
# This is for extremely simple cases and benchmarks.
|
| 237 |
+
if isinstance(o, binary_type):
|
| 238 |
+
_encoding = self.encoding
|
| 239 |
+
if (_encoding is not None and not (_encoding == 'utf-8')):
|
| 240 |
+
o = o.decode(_encoding)
|
| 241 |
+
if isinstance(o, string_types):
|
| 242 |
+
if self.ensure_ascii:
|
| 243 |
+
return encode_basestring_ascii(o)
|
| 244 |
+
else:
|
| 245 |
+
return encode_basestring(o)
|
| 246 |
+
# This doesn't pass the iterator directly to ''.join() because the
|
| 247 |
+
# exceptions aren't as detailed. The list call should be roughly
|
| 248 |
+
# equivalent to the PySequence_Fast that ''.join() would do.
|
| 249 |
+
chunks = self.iterencode(o, _one_shot=True)
|
| 250 |
+
if not isinstance(chunks, (list, tuple)):
|
| 251 |
+
chunks = list(chunks)
|
| 252 |
+
if self.ensure_ascii:
|
| 253 |
+
return ''.join(chunks)
|
| 254 |
+
else:
|
| 255 |
+
return u''.join(chunks)
|
| 256 |
+
|
| 257 |
+
def iterencode(self, o, _one_shot=False):
|
| 258 |
+
"""Encode the given object and yield each string
|
| 259 |
+
representation as available.
|
| 260 |
+
|
| 261 |
+
For example::
|
| 262 |
+
|
| 263 |
+
for chunk in JSONEncoder().iterencode(bigobject):
|
| 264 |
+
mysocket.write(chunk)
|
| 265 |
+
|
| 266 |
+
"""
|
| 267 |
+
if self.check_circular:
|
| 268 |
+
markers = {}
|
| 269 |
+
else:
|
| 270 |
+
markers = None
|
| 271 |
+
if self.ensure_ascii:
|
| 272 |
+
_encoder = encode_basestring_ascii
|
| 273 |
+
else:
|
| 274 |
+
_encoder = encode_basestring
|
| 275 |
+
if self.encoding != 'utf-8':
|
| 276 |
+
def _encoder(o, _orig_encoder=_encoder, _encoding=self.encoding):
|
| 277 |
+
if isinstance(o, binary_type):
|
| 278 |
+
o = o.decode(_encoding)
|
| 279 |
+
return _orig_encoder(o)
|
| 280 |
+
|
| 281 |
+
def floatstr(o, _repr=FLOAT_REPR, _inf=PosInf, _neginf=-PosInf):
|
| 282 |
+
# Check for specials. Note that this type of test is processor
|
| 283 |
+
# and/or platform-specific, so do tests which don't depend on
|
| 284 |
+
# the internals.
|
| 285 |
+
|
| 286 |
+
if o != o:
|
| 287 |
+
text = 'null'
|
| 288 |
+
elif o == _inf:
|
| 289 |
+
text = 'null'
|
| 290 |
+
elif o == _neginf:
|
| 291 |
+
text = 'null'
|
| 292 |
+
else:
|
| 293 |
+
return _repr(o)
|
| 294 |
+
|
| 295 |
+
return text
|
| 296 |
+
|
| 297 |
+
key_memo = {}
|
| 298 |
+
int_as_string_bitcount = (
|
| 299 |
+
53 if self.bigint_as_string else self.int_as_string_bitcount)
|
| 300 |
+
_iterencode = _make_iterencode(
|
| 301 |
+
markers, self.default, _encoder, self.indent, floatstr,
|
| 302 |
+
self.key_separator, self.item_separator, self.sort_keys,
|
| 303 |
+
self.skipkeys, _one_shot, self.use_decimal,
|
| 304 |
+
self.namedtuple_as_object, self.tuple_as_array,
|
| 305 |
+
int_as_string_bitcount,
|
| 306 |
+
self.item_sort_key, self.encoding, self.for_json,
|
| 307 |
+
Decimal=Decimal)
|
| 308 |
+
try:
|
| 309 |
+
return _iterencode(o, 0)
|
| 310 |
+
finally:
|
| 311 |
+
key_memo.clear()
|
| 312 |
+
|
| 313 |
+
|
| 314 |
+
def _make_iterencode(markers, _default, _encoder, _indent, _floatstr,
|
| 315 |
+
_key_separator, _item_separator, _sort_keys, _skipkeys, _one_shot,
|
| 316 |
+
_use_decimal, _namedtuple_as_object, _tuple_as_array,
|
| 317 |
+
_int_as_string_bitcount, _item_sort_key,
|
| 318 |
+
_encoding,_for_json,
|
| 319 |
+
## HACK: hand-optimized bytecode; turn globals into locals
|
| 320 |
+
_PY3=PY3,
|
| 321 |
+
ValueError=ValueError,
|
| 322 |
+
string_types=string_types,
|
| 323 |
+
Decimal=Decimal,
|
| 324 |
+
dict=dict,
|
| 325 |
+
float=float,
|
| 326 |
+
id=id,
|
| 327 |
+
integer_types=integer_types,
|
| 328 |
+
isinstance=isinstance,
|
| 329 |
+
list=list,
|
| 330 |
+
str=str,
|
| 331 |
+
tuple=tuple,
|
| 332 |
+
):
|
| 333 |
+
if _item_sort_key and not callable(_item_sort_key):
|
| 334 |
+
raise TypeError("item_sort_key must be None or callable")
|
| 335 |
+
elif _sort_keys and not _item_sort_key:
|
| 336 |
+
_item_sort_key = itemgetter(0)
|
| 337 |
+
|
| 338 |
+
if (_int_as_string_bitcount is not None and
|
| 339 |
+
(_int_as_string_bitcount <= 0 or
|
| 340 |
+
not isinstance(_int_as_string_bitcount, integer_types))):
|
| 341 |
+
raise TypeError("int_as_string_bitcount must be a positive integer")
|
| 342 |
+
|
| 343 |
+
def _encode_int(value):
|
| 344 |
+
skip_quoting = (
|
| 345 |
+
_int_as_string_bitcount is None
|
| 346 |
+
or
|
| 347 |
+
_int_as_string_bitcount < 1
|
| 348 |
+
)
|
| 349 |
+
if (
|
| 350 |
+
skip_quoting or
|
| 351 |
+
(-1 << _int_as_string_bitcount)
|
| 352 |
+
< value <
|
| 353 |
+
(1 << _int_as_string_bitcount)
|
| 354 |
+
):
|
| 355 |
+
return str(value)
|
| 356 |
+
return '"' + str(value) + '"'
|
| 357 |
+
|
| 358 |
+
def _iterencode_list(lst, _current_indent_level):
|
| 359 |
+
if not lst:
|
| 360 |
+
yield '[]'
|
| 361 |
+
return
|
| 362 |
+
if markers is not None:
|
| 363 |
+
markerid = id(lst)
|
| 364 |
+
if markerid in markers:
|
| 365 |
+
raise ValueError("Circular reference detected")
|
| 366 |
+
markers[markerid] = lst
|
| 367 |
+
buf = '['
|
| 368 |
+
if _indent is not None:
|
| 369 |
+
_current_indent_level += 1
|
| 370 |
+
newline_indent = '\n' + (_indent * _current_indent_level)
|
| 371 |
+
separator = _item_separator + newline_indent
|
| 372 |
+
buf += newline_indent
|
| 373 |
+
else:
|
| 374 |
+
newline_indent = None
|
| 375 |
+
separator = _item_separator
|
| 376 |
+
first = True
|
| 377 |
+
for value in lst:
|
| 378 |
+
if first:
|
| 379 |
+
first = False
|
| 380 |
+
else:
|
| 381 |
+
buf = separator
|
| 382 |
+
yield buf
|
| 383 |
+
|
| 384 |
+
for chunk in _iterencode(value, _current_indent_level):
|
| 385 |
+
yield chunk
|
| 386 |
+
|
| 387 |
+
if newline_indent is not None:
|
| 388 |
+
_current_indent_level -= 1
|
| 389 |
+
yield '\n' + (_indent * _current_indent_level)
|
| 390 |
+
yield ']'
|
| 391 |
+
if markers is not None:
|
| 392 |
+
del markers[markerid]
|
| 393 |
+
|
| 394 |
+
def _stringify_key(key):
|
| 395 |
+
if isinstance(key, string_types): # pragma: no cover
|
| 396 |
+
pass
|
| 397 |
+
elif isinstance(key, binary_type):
|
| 398 |
+
key = key.decode(_encoding)
|
| 399 |
+
elif isinstance(key, float):
|
| 400 |
+
key = _floatstr(key)
|
| 401 |
+
elif key is True:
|
| 402 |
+
key = 'true'
|
| 403 |
+
elif key is False:
|
| 404 |
+
key = 'false'
|
| 405 |
+
elif key is None:
|
| 406 |
+
key = 'null'
|
| 407 |
+
elif isinstance(key, integer_types):
|
| 408 |
+
key = str(key)
|
| 409 |
+
elif _use_decimal and isinstance(key, Decimal):
|
| 410 |
+
key = str(key)
|
| 411 |
+
elif _skipkeys:
|
| 412 |
+
key = None
|
| 413 |
+
else:
|
| 414 |
+
raise TypeError("key " + repr(key) + " is not a string")
|
| 415 |
+
return key
|
| 416 |
+
|
| 417 |
+
def _iterencode_dict(dct, _current_indent_level):
|
| 418 |
+
if not dct:
|
| 419 |
+
yield '{}'
|
| 420 |
+
return
|
| 421 |
+
if markers is not None:
|
| 422 |
+
markerid = id(dct)
|
| 423 |
+
if markerid in markers:
|
| 424 |
+
raise ValueError("Circular reference detected")
|
| 425 |
+
markers[markerid] = dct
|
| 426 |
+
yield '{'
|
| 427 |
+
if _indent is not None:
|
| 428 |
+
_current_indent_level += 1
|
| 429 |
+
newline_indent = '\n' + (_indent * _current_indent_level)
|
| 430 |
+
item_separator = _item_separator + newline_indent
|
| 431 |
+
yield newline_indent
|
| 432 |
+
else:
|
| 433 |
+
newline_indent = None
|
| 434 |
+
item_separator = _item_separator
|
| 435 |
+
first = True
|
| 436 |
+
if _PY3:
|
| 437 |
+
iteritems = dct.items()
|
| 438 |
+
else:
|
| 439 |
+
iteritems = dct.iteritems()
|
| 440 |
+
if _item_sort_key:
|
| 441 |
+
items = []
|
| 442 |
+
for k, v in dct.items():
|
| 443 |
+
if not isinstance(k, string_types):
|
| 444 |
+
k = _stringify_key(k)
|
| 445 |
+
if k is None:
|
| 446 |
+
continue
|
| 447 |
+
items.append((k, v))
|
| 448 |
+
items.sort(key=_item_sort_key)
|
| 449 |
+
else:
|
| 450 |
+
items = iteritems
|
| 451 |
+
for key, value in items:
|
| 452 |
+
if not (_item_sort_key or isinstance(key, string_types)):
|
| 453 |
+
key = _stringify_key(key)
|
| 454 |
+
if key is None:
|
| 455 |
+
# _skipkeys must be True
|
| 456 |
+
continue
|
| 457 |
+
if first:
|
| 458 |
+
first = False
|
| 459 |
+
else:
|
| 460 |
+
yield item_separator
|
| 461 |
+
yield _encoder(key)
|
| 462 |
+
yield _key_separator
|
| 463 |
+
|
| 464 |
+
for chunk in _iterencode(value, _current_indent_level):
|
| 465 |
+
yield chunk
|
| 466 |
+
|
| 467 |
+
if newline_indent is not None:
|
| 468 |
+
_current_indent_level -= 1
|
| 469 |
+
yield '\n' + (_indent * _current_indent_level)
|
| 470 |
+
yield '}'
|
| 471 |
+
if markers is not None:
|
| 472 |
+
del markers[markerid]
|
| 473 |
+
|
| 474 |
+
def _iterencode(o, _current_indent_level):
|
| 475 |
+
if (isinstance(o, string_types) or
|
| 476 |
+
(_PY3 and isinstance(o, binary_type))):
|
| 477 |
+
yield _encoder(o)
|
| 478 |
+
elif o is None:
|
| 479 |
+
yield 'null'
|
| 480 |
+
elif o is True:
|
| 481 |
+
yield 'true'
|
| 482 |
+
elif o is False:
|
| 483 |
+
yield 'false'
|
| 484 |
+
elif isinstance(o, integer_types):
|
| 485 |
+
yield _encode_int(o)
|
| 486 |
+
elif isinstance(o, float):
|
| 487 |
+
yield _floatstr(o)
|
| 488 |
+
else:
|
| 489 |
+
for_json = _for_json and getattr(o, 'for_json', None)
|
| 490 |
+
if for_json and callable(for_json):
|
| 491 |
+
for chunk in _iterencode(for_json(), _current_indent_level):
|
| 492 |
+
yield chunk
|
| 493 |
+
elif isinstance(o, list):
|
| 494 |
+
for chunk in _iterencode_list(o, _current_indent_level):
|
| 495 |
+
yield chunk
|
| 496 |
+
else:
|
| 497 |
+
_asdict = _namedtuple_as_object and getattr(o, '_asdict', None)
|
| 498 |
+
if _asdict and callable(_asdict):
|
| 499 |
+
for chunk in _iterencode_dict(_asdict(), _current_indent_level):
|
| 500 |
+
yield chunk
|
| 501 |
+
elif (_tuple_as_array and isinstance(o, tuple)):
|
| 502 |
+
for chunk in _iterencode_list(o, _current_indent_level):
|
| 503 |
+
yield chunk
|
| 504 |
+
elif isinstance(o, dict):
|
| 505 |
+
for chunk in _iterencode_dict(o, _current_indent_level):
|
| 506 |
+
yield chunk
|
| 507 |
+
elif _use_decimal and isinstance(o, Decimal):
|
| 508 |
+
yield str(o)
|
| 509 |
+
else:
|
| 510 |
+
if markers is not None:
|
| 511 |
+
markerid = id(o)
|
| 512 |
+
if markerid in markers:
|
| 513 |
+
raise ValueError("Circular reference detected")
|
| 514 |
+
markers[markerid] = o
|
| 515 |
+
o = _default(o)
|
| 516 |
+
for chunk in _iterencode(o, _current_indent_level):
|
| 517 |
+
yield chunk
|
| 518 |
+
if markers is not None:
|
| 519 |
+
del markers[markerid]
|
| 520 |
+
|
| 521 |
+
return _iterencode
|
lib/python3.12/site-packages/hjson/encoderH.py
ADDED
|
@@ -0,0 +1,552 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Implementation of HjsonEncoder
|
| 2 |
+
"""
|
| 3 |
+
from __future__ import absolute_import
|
| 4 |
+
import re
|
| 5 |
+
from operator import itemgetter
|
| 6 |
+
from decimal import Decimal
|
| 7 |
+
from .compat import u, unichr, binary_type, string_types, integer_types, PY3
|
| 8 |
+
from .decoder import PosInf
|
| 9 |
+
|
| 10 |
+
# This is required because u() will mangle the string and ur'' isn't valid
|
| 11 |
+
# python3 syntax
|
| 12 |
+
ESCAPE = re.compile(u'[\\x00-\\x1f\\\\"\\b\\f\\n\\r\\t\u2028\u2029\uffff]')
|
| 13 |
+
ESCAPE_ASCII = re.compile(r'([\\"]|[^\ -~])')
|
| 14 |
+
HAS_UTF8 = re.compile(r'[\x80-\xff]')
|
| 15 |
+
ESCAPE_DCT = {
|
| 16 |
+
'\\': '\\\\',
|
| 17 |
+
'"': '\\"',
|
| 18 |
+
'\b': '\\b',
|
| 19 |
+
'\f': '\\f',
|
| 20 |
+
'\n': '\\n',
|
| 21 |
+
'\r': '\\r',
|
| 22 |
+
'\t': '\\t',
|
| 23 |
+
}
|
| 24 |
+
for i in range(0x20):
|
| 25 |
+
#ESCAPE_DCT.setdefault(chr(i), '\\u{0:04x}'.format(i))
|
| 26 |
+
ESCAPE_DCT.setdefault(chr(i), '\\u%04x' % (i,))
|
| 27 |
+
for i in [0x2028, 0x2029, 0xffff]:
|
| 28 |
+
ESCAPE_DCT.setdefault(unichr(i), '\\u%04x' % (i,))
|
| 29 |
+
|
| 30 |
+
COMMONRANGE=u'\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff'
|
| 31 |
+
|
| 32 |
+
# NEEDSESCAPE tests if the string can be written without escapes
|
| 33 |
+
NEEDSESCAPE = re.compile(u'[\\\"\x00-\x1f'+COMMONRANGE+']')
|
| 34 |
+
# NEEDSQUOTES tests if the string can be written as a quoteless string (like needsEscape but without \\ and \")
|
| 35 |
+
NEEDSQUOTES = re.compile(u'^\\s|^"|^\'|^#|^\\/\\*|^\\/\\/|^\\{|^\\}|^\\[|^\\]|^:|^,|\\s$|[\x00-\x1f'+COMMONRANGE+u']')
|
| 36 |
+
# NEEDSESCAPEML tests if the string can be written as a multiline string (like needsEscape but without \n, \r, \\, \", \t)
|
| 37 |
+
NEEDSESCAPEML = re.compile(u'\'\'\'|^[\\s]+$|[\x00-\x08\x0b\x0c\x0e-\x1f'+COMMONRANGE+u']')
|
| 38 |
+
|
| 39 |
+
WHITESPACE = ' \t\n\r'
|
| 40 |
+
STARTSWITHNUMBER = re.compile(r'^[\t ]*(-?(?:0|[1-9]\d*))(\.\d+)?([eE][-+]?\d+)?\s*((,|\]|\}|#|\/\/|\/\*).*)?$');
|
| 41 |
+
STARTSWITHKEYWORD = re.compile(r'^(true|false|null)\s*((,|\]|\}|#|\/\/|\/\*).*)?$');
|
| 42 |
+
NEEDSESCAPENAME = re.compile(r'[,\{\[\}\]\s:#"\']|\/\/|\/\*|'+"'''")
|
| 43 |
+
|
| 44 |
+
FLOAT_REPR = repr
|
| 45 |
+
|
| 46 |
+
def encode_basestring(s, _PY3=PY3, _q=u('"')):
|
| 47 |
+
"""Return a JSON representation of a Python string
|
| 48 |
+
|
| 49 |
+
"""
|
| 50 |
+
if _PY3:
|
| 51 |
+
if isinstance(s, binary_type):
|
| 52 |
+
s = s.decode('utf-8')
|
| 53 |
+
else:
|
| 54 |
+
if isinstance(s, str) and HAS_UTF8.search(s) is not None:
|
| 55 |
+
s = s.decode('utf-8')
|
| 56 |
+
def replace(match):
|
| 57 |
+
return ESCAPE_DCT[match.group(0)]
|
| 58 |
+
return _q + ESCAPE.sub(replace, s) + _q
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
def encode_basestring_ascii(s, _PY3=PY3):
|
| 62 |
+
"""Return an ASCII-only JSON representation of a Python string
|
| 63 |
+
|
| 64 |
+
"""
|
| 65 |
+
if _PY3:
|
| 66 |
+
if isinstance(s, binary_type):
|
| 67 |
+
s = s.decode('utf-8')
|
| 68 |
+
else:
|
| 69 |
+
if isinstance(s, str) and HAS_UTF8.search(s) is not None:
|
| 70 |
+
s = s.decode('utf-8')
|
| 71 |
+
def replace(match):
|
| 72 |
+
s = match.group(0)
|
| 73 |
+
try:
|
| 74 |
+
return ESCAPE_DCT[s]
|
| 75 |
+
except KeyError:
|
| 76 |
+
n = ord(s)
|
| 77 |
+
if n < 0x10000:
|
| 78 |
+
#return '\\u{0:04x}'.format(n)
|
| 79 |
+
return '\\u%04x' % (n,)
|
| 80 |
+
else:
|
| 81 |
+
# surrogate pair
|
| 82 |
+
n -= 0x10000
|
| 83 |
+
s1 = 0xd800 | ((n >> 10) & 0x3ff)
|
| 84 |
+
s2 = 0xdc00 | (n & 0x3ff)
|
| 85 |
+
#return '\\u{0:04x}\\u{1:04x}'.format(s1, s2)
|
| 86 |
+
return '\\u%04x\\u%04x' % (s1, s2)
|
| 87 |
+
return '"' + str(ESCAPE_ASCII.sub(replace, s)) + '"'
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
class HjsonEncoder(object):
|
| 91 |
+
"""Extensible JSON <http://json.org> encoder for Python data structures.
|
| 92 |
+
|
| 93 |
+
Supports the following objects and types by default:
|
| 94 |
+
|
| 95 |
+
+-------------------+---------------+
|
| 96 |
+
| Python | JSON |
|
| 97 |
+
+===================+===============+
|
| 98 |
+
| dict, namedtuple | object |
|
| 99 |
+
+-------------------+---------------+
|
| 100 |
+
| list, tuple | array |
|
| 101 |
+
+-------------------+---------------+
|
| 102 |
+
| str, unicode | string |
|
| 103 |
+
+-------------------+---------------+
|
| 104 |
+
| int, long, float | number |
|
| 105 |
+
+-------------------+---------------+
|
| 106 |
+
| True | true |
|
| 107 |
+
+-------------------+---------------+
|
| 108 |
+
| False | false |
|
| 109 |
+
+-------------------+---------------+
|
| 110 |
+
| None | null |
|
| 111 |
+
+-------------------+---------------+
|
| 112 |
+
|
| 113 |
+
To extend this to recognize other objects, subclass and implement a
|
| 114 |
+
``.default()`` method with another method that returns a serializable
|
| 115 |
+
object for ``o`` if possible, otherwise it should call the superclass
|
| 116 |
+
implementation (to raise ``TypeError``).
|
| 117 |
+
|
| 118 |
+
"""
|
| 119 |
+
|
| 120 |
+
def __init__(self, skipkeys=False, ensure_ascii=True,
|
| 121 |
+
check_circular=True, sort_keys=False,
|
| 122 |
+
indent=' ', encoding='utf-8', default=None,
|
| 123 |
+
use_decimal=True, namedtuple_as_object=True,
|
| 124 |
+
tuple_as_array=True, bigint_as_string=False,
|
| 125 |
+
item_sort_key=None, for_json=False,
|
| 126 |
+
int_as_string_bitcount=None):
|
| 127 |
+
"""Constructor for HjsonEncoder, with sensible defaults.
|
| 128 |
+
|
| 129 |
+
If skipkeys is false, then it is a TypeError to attempt
|
| 130 |
+
encoding of keys that are not str, int, long, float or None. If
|
| 131 |
+
skipkeys is True, such items are simply skipped.
|
| 132 |
+
|
| 133 |
+
If ensure_ascii is true, the output is guaranteed to be str
|
| 134 |
+
objects with all incoming unicode characters escaped. If
|
| 135 |
+
ensure_ascii is false, the output will be unicode object.
|
| 136 |
+
|
| 137 |
+
If check_circular is true, then lists, dicts, and custom encoded
|
| 138 |
+
objects will be checked for circular references during encoding to
|
| 139 |
+
prevent an infinite recursion (which would cause an OverflowError).
|
| 140 |
+
Otherwise, no such check takes place.
|
| 141 |
+
|
| 142 |
+
If sort_keys is true, then the output of dictionaries will be
|
| 143 |
+
sorted by key; this is useful for regression tests to ensure
|
| 144 |
+
that JSON serializations can be compared on a day-to-day basis.
|
| 145 |
+
|
| 146 |
+
If indent is a string, then JSON array elements and object members
|
| 147 |
+
will be pretty-printed with a newline followed by that string repeated
|
| 148 |
+
for each level of nesting.
|
| 149 |
+
|
| 150 |
+
If specified, default is a function that gets called for objects
|
| 151 |
+
that can't otherwise be serialized. It should return a JSON encodable
|
| 152 |
+
version of the object or raise a ``TypeError``.
|
| 153 |
+
|
| 154 |
+
If encoding is not None, then all input strings will be
|
| 155 |
+
transformed into unicode using that encoding prior to JSON-encoding.
|
| 156 |
+
The default is UTF-8.
|
| 157 |
+
|
| 158 |
+
If use_decimal is true (not the default), ``decimal.Decimal`` will
|
| 159 |
+
be supported directly by the encoder. For the inverse, decode JSON
|
| 160 |
+
with ``parse_float=decimal.Decimal``.
|
| 161 |
+
|
| 162 |
+
If namedtuple_as_object is true (the default), objects with
|
| 163 |
+
``_asdict()`` methods will be encoded as JSON objects.
|
| 164 |
+
|
| 165 |
+
If tuple_as_array is true (the default), tuple (and subclasses) will
|
| 166 |
+
be encoded as JSON arrays.
|
| 167 |
+
|
| 168 |
+
If bigint_as_string is true (not the default), ints 2**53 and higher
|
| 169 |
+
or lower than -2**53 will be encoded as strings. This is to avoid the
|
| 170 |
+
rounding that happens in Javascript otherwise.
|
| 171 |
+
|
| 172 |
+
If int_as_string_bitcount is a positive number (n), then int of size
|
| 173 |
+
greater than or equal to 2**n or lower than or equal to -2**n will be
|
| 174 |
+
encoded as strings.
|
| 175 |
+
|
| 176 |
+
If specified, item_sort_key is a callable used to sort the items in
|
| 177 |
+
each dictionary. This is useful if you want to sort items other than
|
| 178 |
+
in alphabetical order by key.
|
| 179 |
+
|
| 180 |
+
If for_json is true (not the default), objects with a ``for_json()``
|
| 181 |
+
method will use the return value of that method for encoding as JSON
|
| 182 |
+
instead of the object.
|
| 183 |
+
|
| 184 |
+
"""
|
| 185 |
+
|
| 186 |
+
self.skipkeys = skipkeys
|
| 187 |
+
self.ensure_ascii = ensure_ascii
|
| 188 |
+
self.check_circular = check_circular
|
| 189 |
+
self.sort_keys = sort_keys
|
| 190 |
+
self.use_decimal = use_decimal
|
| 191 |
+
self.namedtuple_as_object = namedtuple_as_object
|
| 192 |
+
self.tuple_as_array = tuple_as_array
|
| 193 |
+
self.bigint_as_string = bigint_as_string
|
| 194 |
+
self.item_sort_key = item_sort_key
|
| 195 |
+
self.for_json = for_json
|
| 196 |
+
self.int_as_string_bitcount = int_as_string_bitcount
|
| 197 |
+
if indent is not None and not isinstance(indent, string_types):
|
| 198 |
+
indent = indent * ' '
|
| 199 |
+
elif indent is None:
|
| 200 |
+
indent = ' '
|
| 201 |
+
self.indent = indent
|
| 202 |
+
if default is not None:
|
| 203 |
+
self.default = default
|
| 204 |
+
self.encoding = encoding
|
| 205 |
+
|
| 206 |
+
def default(self, o):
|
| 207 |
+
"""Implement this method in a subclass such that it returns
|
| 208 |
+
a serializable object for ``o``, or calls the base implementation
|
| 209 |
+
(to raise a ``TypeError``).
|
| 210 |
+
|
| 211 |
+
For example, to support arbitrary iterators, you could
|
| 212 |
+
implement default like this::
|
| 213 |
+
|
| 214 |
+
def default(self, o):
|
| 215 |
+
try:
|
| 216 |
+
iterable = iter(o)
|
| 217 |
+
except TypeError:
|
| 218 |
+
pass
|
| 219 |
+
else:
|
| 220 |
+
return list(iterable)
|
| 221 |
+
return HjsonEncoder.default(self, o)
|
| 222 |
+
|
| 223 |
+
"""
|
| 224 |
+
raise TypeError(repr(o) + " is not JSON serializable")
|
| 225 |
+
|
| 226 |
+
def encode(self, o):
|
| 227 |
+
"""Return a JSON string representation of a Python data structure.
|
| 228 |
+
|
| 229 |
+
>>> from hjson import HjsonEncoder
|
| 230 |
+
>>> HjsonEncoder().encode({"foo": ["bar", "baz"]})
|
| 231 |
+
'{"foo": ["bar", "baz"]}'
|
| 232 |
+
|
| 233 |
+
"""
|
| 234 |
+
# This is for extremely simple cases and benchmarks.
|
| 235 |
+
if isinstance(o, binary_type):
|
| 236 |
+
_encoding = self.encoding
|
| 237 |
+
if (_encoding is not None and not (_encoding == 'utf-8')):
|
| 238 |
+
o = o.decode(_encoding)
|
| 239 |
+
|
| 240 |
+
# This doesn't pass the iterator directly to ''.join() because the
|
| 241 |
+
# exceptions aren't as detailed. The list call should be roughly
|
| 242 |
+
# equivalent to the PySequence_Fast that ''.join() would do.
|
| 243 |
+
chunks = self.iterencode(o, _one_shot=True)
|
| 244 |
+
if not isinstance(chunks, (list, tuple)):
|
| 245 |
+
chunks = list(chunks)
|
| 246 |
+
if self.ensure_ascii:
|
| 247 |
+
return ''.join(chunks)
|
| 248 |
+
else:
|
| 249 |
+
return u''.join(chunks)
|
| 250 |
+
|
| 251 |
+
def iterencode(self, o, _one_shot=False):
|
| 252 |
+
"""Encode the given object and yield each string
|
| 253 |
+
representation as available.
|
| 254 |
+
|
| 255 |
+
For example::
|
| 256 |
+
|
| 257 |
+
for chunk in HjsonEncoder().iterencode(bigobject):
|
| 258 |
+
mysocket.write(chunk)
|
| 259 |
+
|
| 260 |
+
"""
|
| 261 |
+
if self.check_circular:
|
| 262 |
+
markers = {}
|
| 263 |
+
else:
|
| 264 |
+
markers = None
|
| 265 |
+
if self.ensure_ascii:
|
| 266 |
+
_encoder = encode_basestring_ascii
|
| 267 |
+
else:
|
| 268 |
+
_encoder = encode_basestring
|
| 269 |
+
if self.encoding != 'utf-8':
|
| 270 |
+
def _encoder(o, _orig_encoder=_encoder, _encoding=self.encoding):
|
| 271 |
+
if isinstance(o, binary_type):
|
| 272 |
+
o = o.decode(_encoding)
|
| 273 |
+
return _orig_encoder(o)
|
| 274 |
+
|
| 275 |
+
def floatstr(o, _repr=FLOAT_REPR, _inf=PosInf, _neginf=-PosInf):
|
| 276 |
+
# Check for specials. Note that this type of test is processor
|
| 277 |
+
# and/or platform-specific, so do tests which don't depend on
|
| 278 |
+
# the internals.
|
| 279 |
+
|
| 280 |
+
if o != o or o == _inf or o == _neginf:
|
| 281 |
+
return 'null'
|
| 282 |
+
else:
|
| 283 |
+
return _repr(o)
|
| 284 |
+
|
| 285 |
+
key_memo = {}
|
| 286 |
+
int_as_string_bitcount = (
|
| 287 |
+
53 if self.bigint_as_string else self.int_as_string_bitcount)
|
| 288 |
+
_iterencode = _make_iterencode(
|
| 289 |
+
markers, self.default, _encoder, self.indent, floatstr,
|
| 290 |
+
self.sort_keys, self.skipkeys, _one_shot, self.use_decimal,
|
| 291 |
+
self.namedtuple_as_object, self.tuple_as_array,
|
| 292 |
+
int_as_string_bitcount,
|
| 293 |
+
self.item_sort_key, self.encoding, self.for_json,
|
| 294 |
+
Decimal=Decimal)
|
| 295 |
+
try:
|
| 296 |
+
return _iterencode(o, 0, True)
|
| 297 |
+
finally:
|
| 298 |
+
key_memo.clear()
|
| 299 |
+
|
| 300 |
+
|
| 301 |
+
def _make_iterencode(markers, _default, _encoder, _indent, _floatstr,
|
| 302 |
+
_sort_keys, _skipkeys, _one_shot,
|
| 303 |
+
_use_decimal, _namedtuple_as_object, _tuple_as_array,
|
| 304 |
+
_int_as_string_bitcount, _item_sort_key,
|
| 305 |
+
_encoding,_for_json,
|
| 306 |
+
## HACK: hand-optimized bytecode; turn globals into locals
|
| 307 |
+
_PY3=PY3,
|
| 308 |
+
ValueError=ValueError,
|
| 309 |
+
string_types=string_types,
|
| 310 |
+
Decimal=Decimal,
|
| 311 |
+
dict=dict,
|
| 312 |
+
float=float,
|
| 313 |
+
id=id,
|
| 314 |
+
integer_types=integer_types,
|
| 315 |
+
isinstance=isinstance,
|
| 316 |
+
list=list,
|
| 317 |
+
str=str,
|
| 318 |
+
tuple=tuple,
|
| 319 |
+
):
|
| 320 |
+
if _item_sort_key and not callable(_item_sort_key):
|
| 321 |
+
raise TypeError("item_sort_key must be None or callable")
|
| 322 |
+
elif _sort_keys and not _item_sort_key:
|
| 323 |
+
_item_sort_key = itemgetter(0)
|
| 324 |
+
|
| 325 |
+
if (_int_as_string_bitcount is not None and
|
| 326 |
+
(_int_as_string_bitcount <= 0 or
|
| 327 |
+
not isinstance(_int_as_string_bitcount, integer_types))):
|
| 328 |
+
raise TypeError("int_as_string_bitcount must be a positive integer")
|
| 329 |
+
|
| 330 |
+
def _encode_int(value):
|
| 331 |
+
return str(value)
|
| 332 |
+
|
| 333 |
+
def _stringify_key(key):
|
| 334 |
+
if isinstance(key, string_types): # pragma: no cover
|
| 335 |
+
pass
|
| 336 |
+
elif isinstance(key, binary_type):
|
| 337 |
+
key = key.decode(_encoding)
|
| 338 |
+
elif isinstance(key, float):
|
| 339 |
+
key = _floatstr(key)
|
| 340 |
+
elif key is True:
|
| 341 |
+
key = 'true'
|
| 342 |
+
elif key is False:
|
| 343 |
+
key = 'false'
|
| 344 |
+
elif key is None:
|
| 345 |
+
key = 'null'
|
| 346 |
+
elif isinstance(key, integer_types):
|
| 347 |
+
key = str(key)
|
| 348 |
+
elif _use_decimal and isinstance(key, Decimal):
|
| 349 |
+
key = str(key)
|
| 350 |
+
elif _skipkeys:
|
| 351 |
+
key = None
|
| 352 |
+
else:
|
| 353 |
+
raise TypeError("key " + repr(key) + " is not a string")
|
| 354 |
+
return key
|
| 355 |
+
|
| 356 |
+
def _encoder_key(name):
|
| 357 |
+
if not name: return '""'
|
| 358 |
+
|
| 359 |
+
# Check if we can insert this name without quotes
|
| 360 |
+
if NEEDSESCAPENAME.search(name):
|
| 361 |
+
return _encoder(name)
|
| 362 |
+
else:
|
| 363 |
+
# return without quotes
|
| 364 |
+
return name
|
| 365 |
+
|
| 366 |
+
def _encoder_str(str, _current_indent_level):
|
| 367 |
+
if not str: return '""'
|
| 368 |
+
|
| 369 |
+
# Check if we can insert this string without quotes
|
| 370 |
+
# see hjson syntax (must not parse as true, false, null or number)
|
| 371 |
+
|
| 372 |
+
first = str[0]
|
| 373 |
+
isNumber = False
|
| 374 |
+
if first == '-' or first >= '0' and first <= '9':
|
| 375 |
+
isNumber = STARTSWITHNUMBER.match(str) is not None
|
| 376 |
+
|
| 377 |
+
if (NEEDSQUOTES.search(str) or
|
| 378 |
+
isNumber or
|
| 379 |
+
STARTSWITHKEYWORD.match(str) is not None):
|
| 380 |
+
|
| 381 |
+
# If the string contains no control characters, no quote characters, and no
|
| 382 |
+
# backslash characters, then we can safely slap some quotes around it.
|
| 383 |
+
# Otherwise we first check if the string can be expressed in multiline
|
| 384 |
+
# format or we must replace the offending characters with safe escape
|
| 385 |
+
# sequences.
|
| 386 |
+
|
| 387 |
+
if not NEEDSESCAPE.search(str):
|
| 388 |
+
return '"' + str + '"'
|
| 389 |
+
elif not NEEDSESCAPEML.search(str):
|
| 390 |
+
return _encoder_str_ml(str, _current_indent_level + 1)
|
| 391 |
+
else:
|
| 392 |
+
return _encoder(str)
|
| 393 |
+
else:
|
| 394 |
+
# return without quotes
|
| 395 |
+
return str
|
| 396 |
+
|
| 397 |
+
def _encoder_str_ml(str, _current_indent_level):
|
| 398 |
+
|
| 399 |
+
a = str.replace('\r', '').split('\n')
|
| 400 |
+
# gap += indent;
|
| 401 |
+
|
| 402 |
+
if len(a) == 1:
|
| 403 |
+
# The string contains only a single line. We still use the multiline
|
| 404 |
+
# format as it avoids escaping the \ character (e.g. when used in a
|
| 405 |
+
# regex).
|
| 406 |
+
return "'''" + a[0] + "'''"
|
| 407 |
+
else:
|
| 408 |
+
gap = _indent * _current_indent_level
|
| 409 |
+
res = '\n' + gap + "'''"
|
| 410 |
+
for line in a:
|
| 411 |
+
res += '\n'
|
| 412 |
+
if line: res += gap + line
|
| 413 |
+
return res + '\n' + gap + "'''"
|
| 414 |
+
|
| 415 |
+
def _iterencode_dict(dct, _current_indent_level, _isRoot=False):
|
| 416 |
+
if not dct:
|
| 417 |
+
yield '{}'
|
| 418 |
+
return
|
| 419 |
+
if markers is not None:
|
| 420 |
+
markerid = id(dct)
|
| 421 |
+
if markerid in markers:
|
| 422 |
+
raise ValueError("Circular reference detected")
|
| 423 |
+
markers[markerid] = dct
|
| 424 |
+
|
| 425 |
+
if not _isRoot:
|
| 426 |
+
yield '\n' + (_indent * _current_indent_level)
|
| 427 |
+
|
| 428 |
+
_current_indent_level += 1
|
| 429 |
+
newline_indent = '\n' + (_indent * _current_indent_level)
|
| 430 |
+
|
| 431 |
+
yield '{'
|
| 432 |
+
|
| 433 |
+
if _PY3:
|
| 434 |
+
iteritems = dct.items()
|
| 435 |
+
else:
|
| 436 |
+
iteritems = dct.iteritems()
|
| 437 |
+
if _item_sort_key:
|
| 438 |
+
items = []
|
| 439 |
+
for k, v in dct.items():
|
| 440 |
+
if not isinstance(k, string_types):
|
| 441 |
+
k = _stringify_key(k)
|
| 442 |
+
if k is None:
|
| 443 |
+
continue
|
| 444 |
+
items.append((k, v))
|
| 445 |
+
items.sort(key=_item_sort_key)
|
| 446 |
+
else:
|
| 447 |
+
items = iteritems
|
| 448 |
+
for key, value in items:
|
| 449 |
+
if not (_item_sort_key or isinstance(key, string_types)):
|
| 450 |
+
key = _stringify_key(key)
|
| 451 |
+
if key is None:
|
| 452 |
+
# _skipkeys must be True
|
| 453 |
+
continue
|
| 454 |
+
|
| 455 |
+
yield newline_indent
|
| 456 |
+
yield _encoder_key(key)
|
| 457 |
+
|
| 458 |
+
first = True
|
| 459 |
+
for chunk in _iterencode(value, _current_indent_level):
|
| 460 |
+
if first:
|
| 461 |
+
first = False
|
| 462 |
+
if chunk[0 : 1] == '\n': yield ':'
|
| 463 |
+
else: yield ': '
|
| 464 |
+
yield chunk
|
| 465 |
+
|
| 466 |
+
if newline_indent is not None:
|
| 467 |
+
_current_indent_level -= 1
|
| 468 |
+
yield '\n' + (_indent * _current_indent_level)
|
| 469 |
+
yield '}'
|
| 470 |
+
if markers is not None:
|
| 471 |
+
del markers[markerid]
|
| 472 |
+
|
| 473 |
+
|
| 474 |
+
def _iterencode_list(lst, _current_indent_level, _isRoot=False):
|
| 475 |
+
if not lst:
|
| 476 |
+
yield '[]'
|
| 477 |
+
return
|
| 478 |
+
if markers is not None:
|
| 479 |
+
markerid = id(lst)
|
| 480 |
+
if markerid in markers:
|
| 481 |
+
raise ValueError("Circular reference detected")
|
| 482 |
+
markers[markerid] = lst
|
| 483 |
+
|
| 484 |
+
if not _isRoot:
|
| 485 |
+
yield '\n' + (_indent * _current_indent_level)
|
| 486 |
+
|
| 487 |
+
_current_indent_level += 1
|
| 488 |
+
newline_indent = '\n' + (_indent * _current_indent_level)
|
| 489 |
+
yield '['
|
| 490 |
+
|
| 491 |
+
for value in lst:
|
| 492 |
+
yield newline_indent
|
| 493 |
+
|
| 494 |
+
for chunk in _iterencode(value, _current_indent_level, True):
|
| 495 |
+
yield chunk
|
| 496 |
+
|
| 497 |
+
if newline_indent is not None:
|
| 498 |
+
_current_indent_level -= 1
|
| 499 |
+
yield '\n' + (_indent * _current_indent_level)
|
| 500 |
+
yield ']'
|
| 501 |
+
if markers is not None:
|
| 502 |
+
del markers[markerid]
|
| 503 |
+
|
| 504 |
+
|
| 505 |
+
def _iterencode(o, _current_indent_level, _isRoot=False):
|
| 506 |
+
if (isinstance(o, string_types) or
|
| 507 |
+
(_PY3 and isinstance(o, binary_type))):
|
| 508 |
+
yield _encoder_str(o, _current_indent_level)
|
| 509 |
+
elif o is None:
|
| 510 |
+
yield 'null'
|
| 511 |
+
elif o is True:
|
| 512 |
+
yield 'true'
|
| 513 |
+
elif o is False:
|
| 514 |
+
yield 'false'
|
| 515 |
+
elif isinstance(o, integer_types):
|
| 516 |
+
yield _encode_int(o)
|
| 517 |
+
elif isinstance(o, float):
|
| 518 |
+
yield _floatstr(o)
|
| 519 |
+
else:
|
| 520 |
+
for_json = _for_json and getattr(o, 'for_json', None)
|
| 521 |
+
if for_json and callable(for_json):
|
| 522 |
+
for chunk in _iterencode(for_json(), _current_indent_level, _isRoot):
|
| 523 |
+
yield chunk
|
| 524 |
+
elif isinstance(o, list):
|
| 525 |
+
for chunk in _iterencode_list(o, _current_indent_level, _isRoot):
|
| 526 |
+
yield chunk
|
| 527 |
+
else:
|
| 528 |
+
_asdict = _namedtuple_as_object and getattr(o, '_asdict', None)
|
| 529 |
+
if _asdict and callable(_asdict):
|
| 530 |
+
for chunk in _iterencode_dict(_asdict(), _current_indent_level, _isRoot):
|
| 531 |
+
yield chunk
|
| 532 |
+
elif (_tuple_as_array and isinstance(o, tuple)):
|
| 533 |
+
for chunk in _iterencode_list(o, _current_indent_level, _isRoot):
|
| 534 |
+
yield chunk
|
| 535 |
+
elif isinstance(o, dict):
|
| 536 |
+
for chunk in _iterencode_dict(o, _current_indent_level, _isRoot):
|
| 537 |
+
yield chunk
|
| 538 |
+
elif _use_decimal and isinstance(o, Decimal):
|
| 539 |
+
yield str(o)
|
| 540 |
+
else:
|
| 541 |
+
if markers is not None:
|
| 542 |
+
markerid = id(o)
|
| 543 |
+
if markerid in markers:
|
| 544 |
+
raise ValueError("Circular reference detected")
|
| 545 |
+
markers[markerid] = o
|
| 546 |
+
o = _default(o)
|
| 547 |
+
for chunk in _iterencode(o, _current_indent_level, _isRoot):
|
| 548 |
+
yield chunk
|
| 549 |
+
if markers is not None:
|
| 550 |
+
del markers[markerid]
|
| 551 |
+
|
| 552 |
+
return _iterencode
|
lib/python3.12/site-packages/hjson/ordered_dict.py
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Drop-in replacement for collections.OrderedDict by Raymond Hettinger
|
| 2 |
+
|
| 3 |
+
http://code.activestate.com/recipes/576693/
|
| 4 |
+
|
| 5 |
+
"""
|
| 6 |
+
from UserDict import DictMixin
|
| 7 |
+
|
| 8 |
+
# Modified from original to support Python 2.4, see
|
| 9 |
+
# http://code.google.com/p/simplejson/issues/detail?id=53
|
| 10 |
+
try:
|
| 11 |
+
all
|
| 12 |
+
except NameError:
|
| 13 |
+
def all(seq):
|
| 14 |
+
for elem in seq:
|
| 15 |
+
if not elem:
|
| 16 |
+
return False
|
| 17 |
+
return True
|
| 18 |
+
|
| 19 |
+
class OrderedDict(dict, DictMixin):
|
| 20 |
+
|
| 21 |
+
def __init__(self, *args, **kwds):
|
| 22 |
+
if len(args) > 1:
|
| 23 |
+
raise TypeError('expected at most 1 arguments, got %d' % len(args))
|
| 24 |
+
try:
|
| 25 |
+
self.__end
|
| 26 |
+
except AttributeError:
|
| 27 |
+
self.clear()
|
| 28 |
+
self.update(*args, **kwds)
|
| 29 |
+
|
| 30 |
+
def clear(self):
|
| 31 |
+
self.__end = end = []
|
| 32 |
+
end += [None, end, end] # sentinel node for doubly linked list
|
| 33 |
+
self.__map = {} # key --> [key, prev, next]
|
| 34 |
+
dict.clear(self)
|
| 35 |
+
|
| 36 |
+
def __setitem__(self, key, value):
|
| 37 |
+
if key not in self:
|
| 38 |
+
end = self.__end
|
| 39 |
+
curr = end[1]
|
| 40 |
+
curr[2] = end[1] = self.__map[key] = [key, curr, end]
|
| 41 |
+
dict.__setitem__(self, key, value)
|
| 42 |
+
|
| 43 |
+
def __delitem__(self, key):
|
| 44 |
+
dict.__delitem__(self, key)
|
| 45 |
+
key, prev, next = self.__map.pop(key)
|
| 46 |
+
prev[2] = next
|
| 47 |
+
next[1] = prev
|
| 48 |
+
|
| 49 |
+
def __iter__(self):
|
| 50 |
+
end = self.__end
|
| 51 |
+
curr = end[2]
|
| 52 |
+
while curr is not end:
|
| 53 |
+
yield curr[0]
|
| 54 |
+
curr = curr[2]
|
| 55 |
+
|
| 56 |
+
def __reversed__(self):
|
| 57 |
+
end = self.__end
|
| 58 |
+
curr = end[1]
|
| 59 |
+
while curr is not end:
|
| 60 |
+
yield curr[0]
|
| 61 |
+
curr = curr[1]
|
| 62 |
+
|
| 63 |
+
def popitem(self, last=True):
|
| 64 |
+
if not self:
|
| 65 |
+
raise KeyError('dictionary is empty')
|
| 66 |
+
# Modified from original to support Python 2.4, see
|
| 67 |
+
# http://code.google.com/p/simplejson/issues/detail?id=53
|
| 68 |
+
if last:
|
| 69 |
+
key = reversed(self).next()
|
| 70 |
+
else:
|
| 71 |
+
key = iter(self).next()
|
| 72 |
+
value = self.pop(key)
|
| 73 |
+
return key, value
|
| 74 |
+
|
| 75 |
+
def __reduce__(self):
|
| 76 |
+
items = [[k, self[k]] for k in self]
|
| 77 |
+
tmp = self.__map, self.__end
|
| 78 |
+
del self.__map, self.__end
|
| 79 |
+
inst_dict = vars(self).copy()
|
| 80 |
+
self.__map, self.__end = tmp
|
| 81 |
+
if inst_dict:
|
| 82 |
+
return (self.__class__, (items,), inst_dict)
|
| 83 |
+
return self.__class__, (items,)
|
| 84 |
+
|
| 85 |
+
def keys(self):
|
| 86 |
+
return list(self)
|
| 87 |
+
|
| 88 |
+
setdefault = DictMixin.setdefault
|
| 89 |
+
update = DictMixin.update
|
| 90 |
+
pop = DictMixin.pop
|
| 91 |
+
values = DictMixin.values
|
| 92 |
+
items = DictMixin.items
|
| 93 |
+
iterkeys = DictMixin.iterkeys
|
| 94 |
+
itervalues = DictMixin.itervalues
|
| 95 |
+
iteritems = DictMixin.iteritems
|
| 96 |
+
|
| 97 |
+
def __repr__(self):
|
| 98 |
+
if not self:
|
| 99 |
+
return '%s()' % (self.__class__.__name__,)
|
| 100 |
+
return '%s(%r)' % (self.__class__.__name__, self.items())
|
| 101 |
+
|
| 102 |
+
def copy(self):
|
| 103 |
+
return self.__class__(self)
|
| 104 |
+
|
| 105 |
+
@classmethod
|
| 106 |
+
def fromkeys(cls, iterable, value=None):
|
| 107 |
+
d = cls()
|
| 108 |
+
for key in iterable:
|
| 109 |
+
d[key] = value
|
| 110 |
+
return d
|
| 111 |
+
|
| 112 |
+
def __eq__(self, other):
|
| 113 |
+
if isinstance(other, OrderedDict):
|
| 114 |
+
return len(self)==len(other) and \
|
| 115 |
+
all(p==q for p, q in zip(self.items(), other.items()))
|
| 116 |
+
return dict.__eq__(self, other)
|
| 117 |
+
|
| 118 |
+
def __ne__(self, other):
|
| 119 |
+
return not self == other
|
lib/python3.12/site-packages/hjson/scanner.py
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""JSON token scanner
|
| 2 |
+
"""
|
| 3 |
+
import re
|
| 4 |
+
|
| 5 |
+
__all__ = ['HjsonDecodeError']
|
| 6 |
+
|
| 7 |
+
class HjsonDecodeError(ValueError):
|
| 8 |
+
"""Subclass of ValueError with the following additional properties:
|
| 9 |
+
|
| 10 |
+
msg: The unformatted error message
|
| 11 |
+
doc: The JSON document being parsed
|
| 12 |
+
pos: The start index of doc where parsing failed
|
| 13 |
+
end: The end index of doc where parsing failed (may be None)
|
| 14 |
+
lineno: The line corresponding to pos
|
| 15 |
+
colno: The column corresponding to pos
|
| 16 |
+
endlineno: The line corresponding to end (may be None)
|
| 17 |
+
endcolno: The column corresponding to end (may be None)
|
| 18 |
+
|
| 19 |
+
"""
|
| 20 |
+
# Note that this exception is used from _speedups
|
| 21 |
+
def __init__(self, msg, doc, pos, end=None):
|
| 22 |
+
ValueError.__init__(self, errmsg(msg, doc, pos, end=end))
|
| 23 |
+
self.msg = msg
|
| 24 |
+
self.doc = doc
|
| 25 |
+
self.pos = pos
|
| 26 |
+
self.end = end
|
| 27 |
+
self.lineno, self.colno = linecol(doc, pos)
|
| 28 |
+
if end is not None:
|
| 29 |
+
self.endlineno, self.endcolno = linecol(doc, end)
|
| 30 |
+
else:
|
| 31 |
+
self.endlineno, self.endcolno = None, None
|
| 32 |
+
|
| 33 |
+
def __reduce__(self):
|
| 34 |
+
return self.__class__, (self.msg, self.doc, self.pos, self.end)
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
def linecol(doc, pos):
|
| 38 |
+
lineno = doc.count('\n', 0, pos) + 1
|
| 39 |
+
if lineno == 1:
|
| 40 |
+
colno = pos + 1
|
| 41 |
+
else:
|
| 42 |
+
colno = pos - doc.rindex('\n', 0, pos)
|
| 43 |
+
return lineno, colno
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def errmsg(msg, doc, pos, end=None):
|
| 47 |
+
lineno, colno = linecol(doc, pos)
|
| 48 |
+
msg = msg.replace('%r', repr(doc[pos:pos + 1]))
|
| 49 |
+
if end is None:
|
| 50 |
+
fmt = '%s: line %d column %d (char %d)'
|
| 51 |
+
return fmt % (msg, lineno, colno, pos)
|
| 52 |
+
endlineno, endcolno = linecol(doc, end)
|
| 53 |
+
fmt = '%s: line %d column %d - line %d column %d (char %d - %d)'
|
| 54 |
+
return fmt % (msg, lineno, colno, endlineno, endcolno, pos, end)
|
| 55 |
+
|
| 56 |
+
|
lib/python3.12/site-packages/hjson/tests/__init__.py
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import absolute_import
|
| 2 |
+
import unittest
|
| 3 |
+
import doctest
|
| 4 |
+
import sys
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
def additional_tests(suite=None):
|
| 8 |
+
import hjson
|
| 9 |
+
import hjson.encoder
|
| 10 |
+
import hjson.decoder
|
| 11 |
+
if suite is None:
|
| 12 |
+
suite = unittest.TestSuite()
|
| 13 |
+
for mod in (hjson, hjson.encoder, hjson.decoder):
|
| 14 |
+
suite.addTest(doctest.DocTestSuite(mod))
|
| 15 |
+
return suite
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
def all_tests_suite():
|
| 19 |
+
def get_suite():
|
| 20 |
+
return additional_tests(
|
| 21 |
+
unittest.TestLoader().loadTestsFromNames([
|
| 22 |
+
'hjson.tests.test_hjson',
|
| 23 |
+
'hjson.tests.test_bitsize_int_as_string',
|
| 24 |
+
'hjson.tests.test_bigint_as_string',
|
| 25 |
+
'hjson.tests.test_check_circular',
|
| 26 |
+
'hjson.tests.test_decode',
|
| 27 |
+
'hjson.tests.test_default',
|
| 28 |
+
'hjson.tests.test_dump',
|
| 29 |
+
'hjson.tests.test_encode_basestring_ascii',
|
| 30 |
+
'hjson.tests.test_errors',
|
| 31 |
+
'hjson.tests.test_fail',
|
| 32 |
+
'hjson.tests.test_float',
|
| 33 |
+
'hjson.tests.test_indent',
|
| 34 |
+
'hjson.tests.test_pass1',
|
| 35 |
+
'hjson.tests.test_pass2',
|
| 36 |
+
'hjson.tests.test_pass3',
|
| 37 |
+
'hjson.tests.test_recursion',
|
| 38 |
+
'hjson.tests.test_scanstring',
|
| 39 |
+
'hjson.tests.test_separators',
|
| 40 |
+
'hjson.tests.test_unicode',
|
| 41 |
+
'hjson.tests.test_decimal',
|
| 42 |
+
'hjson.tests.test_tuple',
|
| 43 |
+
'hjson.tests.test_namedtuple',
|
| 44 |
+
#'hjson.tests.test_tool', # fails on windows
|
| 45 |
+
'hjson.tests.test_for_json',
|
| 46 |
+
]))
|
| 47 |
+
suite = get_suite()
|
| 48 |
+
return suite
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
def main():
|
| 52 |
+
runner = unittest.TextTestRunner(verbosity=1 + sys.argv.count('-v'))
|
| 53 |
+
suite = all_tests_suite()
|
| 54 |
+
raise SystemExit(not runner.run(suite).wasSuccessful())
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
if __name__ == '__main__':
|
| 58 |
+
import os
|
| 59 |
+
import sys
|
| 60 |
+
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
|
| 61 |
+
main()
|
lib/python3.12/site-packages/hjson/tests/__pycache__/__init__.cpython-312.pyc
ADDED
|
Binary file (2.84 kB). View file
|
|
|
lib/python3.12/site-packages/hjson/tests/__pycache__/test_bigint_as_string.cpython-312.pyc
ADDED
|
Binary file (3.23 kB). View file
|
|
|
lib/python3.12/site-packages/hjson/tests/__pycache__/test_bitsize_int_as_string.cpython-312.pyc
ADDED
|
Binary file (3.78 kB). View file
|
|
|
lib/python3.12/site-packages/hjson/tests/__pycache__/test_check_circular.cpython-312.pyc
ADDED
|
Binary file (2.41 kB). View file
|
|
|
lib/python3.12/site-packages/hjson/tests/__pycache__/test_decimal.cpython-312.pyc
ADDED
|
Binary file (4.57 kB). View file
|
|
|
lib/python3.12/site-packages/hjson/tests/__pycache__/test_decode.cpython-312.pyc
ADDED
|
Binary file (8.26 kB). View file
|
|
|
lib/python3.12/site-packages/hjson/tests/__pycache__/test_default.cpython-312.pyc
ADDED
|
Binary file (819 Bytes). View file
|
|
|
lib/python3.12/site-packages/hjson/tests/__pycache__/test_dump.cpython-312.pyc
ADDED
|
Binary file (8.24 kB). View file
|
|
|
lib/python3.12/site-packages/hjson/tests/__pycache__/test_encode_basestring_ascii.cpython-312.pyc
ADDED
|
Binary file (2.49 kB). View file
|
|
|
lib/python3.12/site-packages/hjson/tests/__pycache__/test_errors.cpython-312.pyc
ADDED
|
Binary file (3.49 kB). View file
|
|
|
lib/python3.12/site-packages/hjson/tests/__pycache__/test_fail.cpython-312.pyc
ADDED
|
Binary file (4.02 kB). View file
|
|
|