pax_global_header00006660000000000000000000000064147506264050014523gustar00rootroot0000000000000052 comment=daad7e4a15569321d3cf118f8083a71a65d7e044 dacite-1.9.2-fix/000077500000000000000000000000001475062640500135515ustar00rootroot00000000000000dacite-1.9.2-fix/.github/000077500000000000000000000000001475062640500151115ustar00rootroot00000000000000dacite-1.9.2-fix/.github/ISSUE_TEMPLATE/000077500000000000000000000000001475062640500172745ustar00rootroot00000000000000dacite-1.9.2-fix/.github/ISSUE_TEMPLATE/bug_report.md000066400000000000000000000004771475062640500217760ustar00rootroot00000000000000--- name: Bug report about: Create a report to help us improve dacite title: '' labels: bug assignees: '' --- **Describe the bug** ... **To Reproduce** ... **Expected behavior** ... **Environment** - Python version: ... - `dacite` version: ... **Additional context** Add any other context about the problem here. dacite-1.9.2-fix/.github/ISSUE_TEMPLATE/feature_request.md000066400000000000000000000011261475062640500230210ustar00rootroot00000000000000--- name: Feature request about: Suggest an idea for dacite title: '' labels: enhancement assignees: '' --- **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] **Describe the solution you'd like** A clear and concise description of what you want to happen. **Describe alternatives you've considered** A clear and concise description of any alternative solutions or features you've considered. **Additional context** Add any other context or screenshots about the feature request here. dacite-1.9.2-fix/.github/workflows/000077500000000000000000000000001475062640500171465ustar00rootroot00000000000000dacite-1.9.2-fix/.github/workflows/ci_master.yaml000066400000000000000000000003451475062640500220020ustar00rootroot00000000000000name: CI Master on: push: branches: - master workflow_dispatch: jobs: ci: uses: ./.github/workflows/code_check.yaml secrets: inherit with: publish_performance: true store_benchmark: true dacite-1.9.2-fix/.github/workflows/ci_pr.yaml000066400000000000000000000003301475062640500211220ustar00rootroot00000000000000name: CI Pull Request on: pull_request: workflow_dispatch: jobs: ci: uses: ./.github/workflows/code_check.yaml secrets: inherit with: publish_performance: false store_benchmark: false dacite-1.9.2-fix/.github/workflows/code_check.yaml000066400000000000000000000026711475062640500221070ustar00rootroot00000000000000name: Code Check on: workflow_call: inputs: publish_performance: required: true type: boolean store_benchmark: required: true type: boolean jobs: check: runs-on: ubuntu-22.04 strategy: matrix: python-version: [ '3.7', '3.8', '3.9', '3.10', '3.11', '3.12', '3.13'] name: Python ${{ matrix.python-version }} steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} architecture: x64 - name: Install dependencies run: pip install -e ".[dev]" - name: Testing Check run: pytest --cov=dacite - name: Formatting Check run: black --check . - name: Typing Check run: mypy dacite - name: Linting Check run: pylint dacite - name: Performance Check uses: benchmark-action/github-action-benchmark@v1 with: tool: 'pytest' output-file-path: benchmark.json save-data-file: ${{ inputs.store_benchmark }} github-token: ${{ secrets.GITHUB_TOKEN }} auto-push: ${{ inputs.publish_performance }} benchmark-data-dir-path: performance/${{ matrix.python-version }} comment-always: false alert-threshold: '130%' comment-on-alert: true fail-on-alert: false dacite-1.9.2-fix/.github/workflows/publish.yaml000066400000000000000000000021431475062640500215000ustar00rootroot00000000000000name: Publish on: workflow_dispatch: release: types: [published] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: '3.11' architecture: x64 - name: Build package run: | python -m pip install --upgrade build python -m build - name: Store the distribution packages uses: actions/upload-artifact@v4 with: name: python-package-distributions path: dist/ publish: name: Publish to PyPI needs: - build runs-on: ubuntu-latest environment: name: pypi url: https://pypi.org/p/dacite permissions: id-token: write # IMPORTANT: mandatory for trusted publishing steps: - name: Download dist uses: actions/download-artifact@v4 with: name: python-package-distributions path: dist/ - name: Publish package to PyPI uses: pypa/gh-action-pypi-publish@release/v1 with: password: ${{ secrets.PYPI_API_TOKEN }} dacite-1.9.2-fix/.gitignore000066400000000000000000000001431475062640500155370ustar00rootroot00000000000000.pytest_cache/ *.pyc dist/ *.egg-info/ build/ .idea venv* dacite-env* .benchmarks benchmark.json dacite-1.9.2-fix/.pre-commit-config.yaml000066400000000000000000000001341475062640500200300ustar00rootroot00000000000000repos: - repo: https://github.com/psf/black rev: 22.12.0 hooks: - id: black dacite-1.9.2-fix/.pylintrc000066400000000000000000000427071475062640500154300ustar00rootroot00000000000000[MASTER] # A comma-separated list of package or module names from where C extensions may # be loaded. Extensions are loading into the active Python interpreter and may # run arbitrary code. extension-pkg-whitelist= # Add files or directories to the blacklist. They should be base names, not # paths. ignore=CVS # Add files or directories matching the regex patterns to the blacklist. The # regex matches against base names, not paths. ignore-patterns= # Python code to execute, usually for sys.path manipulation such as # pygtk.require(). #init-hook= # Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the # number of processors available to use. jobs=1 # Control the amount of potential inferred values when inferring a single # object. This can help the performance when dealing with large functions or # complex, nested conditions. limit-inference-results=100 # List of plugins (as comma separated values of python modules names) to load, # usually to register additional checkers. load-plugins= # Pickle collected data for later comparisons. persistent=yes # Specify a configuration file. #rcfile= # When enabled, pylint would attempt to guess common misconfiguration and emit # user-friendly hints instead of false-positive error messages. suggestion-mode=yes # Allow loading of arbitrary C extensions. Extensions are imported into the # active Python interpreter and may run arbitrary code. unsafe-load-any-extension=no [MESSAGES CONTROL] # Only show warnings with the listed confidence levels. Leave empty to show # all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED. confidence= # Disable the message, report, category or checker with the given id(s). You # can either give multiple identifiers separated by comma (,) or put this # option multiple times (only on the command line, not in the configuration # file where it should appear only once). You can also use "--disable=all" to # disable everything first and then reenable specific checks. For example, if # you want to run only the similarities checker, you can use "--disable=all # --enable=similarities". If you want to run only the classes checker, but have # no Warning level messages displayed, use "--disable=all --enable=classes # --disable=W". disable=print-statement, parameter-unpacking, unpacking-in-except, old-raise-syntax, backtick, long-suffix, old-ne-operator, old-octal-literal, import-star-module-level, non-ascii-bytes-literal, raw-checker-failed, bad-inline-option, locally-disabled, file-ignored, suppressed-message, useless-suppression, deprecated-pragma, use-symbolic-message-instead, apply-builtin, basestring-builtin, buffer-builtin, cmp-builtin, coerce-builtin, execfile-builtin, file-builtin, long-builtin, raw_input-builtin, reduce-builtin, standarderror-builtin, unicode-builtin, xrange-builtin, coerce-method, delslice-method, getslice-method, setslice-method, no-absolute-import, old-division, dict-iter-method, dict-view-method, next-method-called, metaclass-assignment, indexing-exception, raising-string, reload-builtin, oct-method, hex-method, nonzero-method, cmp-method, input-builtin, round-builtin, intern-builtin, unichr-builtin, map-builtin-not-iterating, zip-builtin-not-iterating, range-builtin-not-iterating, filter-builtin-not-iterating, using-cmp-argument, eq-without-hash, div-method, idiv-method, rdiv-method, exception-message-attribute, invalid-str-codec, sys-max-int, bad-python3-import, deprecated-string-function, deprecated-str-translate-call, deprecated-itertools-function, deprecated-types-field, next-method-defined, dict-items-not-iterating, dict-keys-not-iterating, dict-values-not-iterating, deprecated-operator-function, deprecated-urllib-function, xreadlines-attribute, deprecated-sys-function, exception-escape, comprehension-escape, missing-docstring, no-self-use, unidiomatic-typecheck, no-else-return, too-many-return-statements, too-many-branches, protected-access, bad-continuation, import-outside-toplevel, isinstance-second-argument-not-valid-type, unsubscriptable-object, comparison-with-callable # Enable the message, report, category or checker with the given id(s). You can # either give multiple identifier separated by comma (,) or put this option # multiple time (only on the command line, not in the configuration file where # it should appear only once). See also the "--disable" option for examples. enable=c-extension-no-member [REPORTS] # Python expression which should return a note less than 10 (10 is the highest # note). You have access to the variables errors warning, statement which # respectively contain the number of errors / warnings messages and the total # number of statements analyzed. This is used by the global evaluation report # (RP0004). evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) # Template used to display messages. This is a python new-style format string # used to format the message information. See doc for all details. #msg-template= # Set the output format. Available formats are text, parseable, colorized, json # and msvs (visual studio). You can also give a reporter class, e.g. # mypackage.mymodule.MyReporterClass. output-format=text # Tells whether to display a full report or only the messages. reports=no # Activate the evaluation score. score=yes [REFACTORING] # Maximum number of nested blocks for function / method body max-nested-blocks=5 # Complete name of functions that never returns. When checking for # inconsistent-return-statements if a never returning function is called then # it will be considered as an explicit return statement and no message will be # printed. never-returning-functions=sys.exit [LOGGING] # Format style used to check logging format string. `old` means using % # formatting, while `new` is for `{}` formatting. logging-format-style=old # Logging modules to check that the string format arguments are in logging # function parameter format. logging-modules=logging [SPELLING] # Limits count of emitted suggestions for spelling mistakes. max-spelling-suggestions=4 # Spelling dictionary name. Available dictionaries: none. To make it working # install python-enchant package.. spelling-dict= # List of comma separated words that should not be checked. spelling-ignore-words= # A path to a file that contains private dictionary; one word per line. spelling-private-dict-file= # Tells whether to store unknown words to indicated private dictionary in # --spelling-private-dict-file option instead of raising a message. spelling-store-unknown-words=no [MISCELLANEOUS] # List of note tags to take in consideration, separated by a comma. notes=FIXME, XXX, TODO [TYPECHECK] # List of decorators that produce context managers, such as # contextlib.contextmanager. Add to this list to register other decorators that # produce valid context managers. contextmanager-decorators=contextlib.contextmanager # List of members which are set dynamically and missed by pylint inference # system, and so shouldn't trigger E1101 when accessed. Python regular # expressions are accepted. generated-members= # Tells whether missing members accessed in mixin class should be ignored. A # mixin class is detected if its name ends with "mixin" (case insensitive). ignore-mixin-members=yes # Tells whether to warn about missing members when the owner of the attribute # is inferred to be None. ignore-none=yes # This flag controls whether pylint should warn about no-member and similar # checks whenever an opaque object is returned when inferring. The inference # can return multiple potential results while evaluating a Python object, but # some branches might not be evaluated, which results in partial inference. In # that case, it might be useful to still emit no-member and other checks for # the rest of the inferred objects. ignore-on-opaque-inference=yes # List of class names for which member attributes should not be checked (useful # for classes with dynamically set attributes). This supports the use of # qualified names. ignored-classes=optparse.Values,thread._local,_thread._local # List of module names for which member attributes should not be checked # (useful for modules/projects where namespaces are manipulated during runtime # and thus existing member attributes cannot be deduced by static analysis. It # supports qualified module names, as well as Unix pattern matching. ignored-modules= # Show a hint with possible names when a member name was not found. The aspect # of finding the hint is based on edit distance. missing-member-hint=yes # The minimum edit distance a name should have in order to be considered a # similar match for a missing member name. missing-member-hint-distance=1 # The total number of similar names that should be taken in consideration when # showing a hint for a missing member. missing-member-max-choices=1 [VARIABLES] # List of additional names supposed to be defined in builtins. Remember that # you should avoid defining new builtins when possible. additional-builtins= # Tells whether unused global variables should be treated as a violation. allow-global-unused-variables=yes # List of strings which can identify a callback function by name. A callback # name must start or end with one of those strings. callbacks=cb_, _cb # A regular expression matching the name of dummy variables (i.e. expected to # not be used). dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ # Argument names that match this expression will be ignored. Default to name # with leading underscore. ignored-argument-names=_.*|^ignored_|^unused_ # Tells whether we should check for unused import in __init__ files. init-import=no # List of qualified module names which can have objects that can redefine # builtins. redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io [FORMAT] # Expected format of line ending, e.g. empty (any line ending), LF or CRLF. expected-line-ending-format= # Regexp for a line that is allowed to be longer than the limit. ignore-long-lines=^\s*(# )??$ # Number of spaces of indent required inside a hanging or continued line. indent-after-paren=4 # String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 # tab). indent-string=' ' # Maximum number of characters on a single line. max-line-length=120 # Maximum number of lines in a module. max-module-lines=1000 # List of optional constructs for which whitespace checking is disabled. `dict- # separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}. # `trailing-comma` allows a space between comma and closing bracket: (a, ). # `empty-line` allows space-only lines. no-space-check=trailing-comma, dict-separator # Allow the body of a class to be on the same line as the declaration if body # contains single statement. single-line-class-stmt=no # Allow the body of an if to be on the same line as the test if there is no # else. single-line-if-stmt=no [SIMILARITIES] # Ignore comments when computing similarities. ignore-comments=yes # Ignore docstrings when computing similarities. ignore-docstrings=yes # Ignore imports when computing similarities. ignore-imports=no # Minimum lines number of a similarity. min-similarity-lines=4 [BASIC] # Naming style matching correct argument names. argument-naming-style=snake_case # Regular expression matching correct argument names. Overrides argument- # naming-style. #argument-rgx= # Naming style matching correct attribute names. attr-naming-style=snake_case # Regular expression matching correct attribute names. Overrides attr-naming- # style. #attr-rgx= # Bad variable names which should always be refused, separated by a comma. bad-names=foo, bar, baz, toto, tutu, tata # Naming style matching correct class attribute names. class-attribute-naming-style=any # Regular expression matching correct class attribute names. Overrides class- # attribute-naming-style. #class-attribute-rgx= # Naming style matching correct class names. class-naming-style=PascalCase # Regular expression matching correct class names. Overrides class-naming- # style. #class-rgx= # Naming style matching correct constant names. const-naming-style=UPPER_CASE # Regular expression matching correct constant names. Overrides const-naming- # style. #const-rgx= # Minimum line length for functions/classes that require docstrings, shorter # ones are exempt. docstring-min-length=-1 # Naming style matching correct function names. function-naming-style=snake_case # Regular expression matching correct function names. Overrides function- # naming-style. #function-rgx= # Good variable names which should always be accepted, separated by a comma. good-names=i, j, k, ex, Run, _, T # Include a hint for the correct naming format with invalid-name. include-naming-hint=no # Naming style matching correct inline iteration names. inlinevar-naming-style=any # Regular expression matching correct inline iteration names. Overrides # inlinevar-naming-style. #inlinevar-rgx= # Naming style matching correct method names. method-naming-style=snake_case # Regular expression matching correct method names. Overrides method-naming- # style. #method-rgx= # Naming style matching correct module names. module-naming-style=snake_case # Regular expression matching correct module names. Overrides module-naming- # style. #module-rgx= # Colon-delimited sets of names that determine each other's naming style when # the name regexes allow several styles. name-group= # Regular expression which should only match function or class names that do # not require a docstring. no-docstring-rgx=^_ # List of decorators that produce properties, such as abc.abstractproperty. Add # to this list to register other decorators that produce valid properties. # These decorators are taken in consideration only for invalid-name. property-classes=abc.abstractproperty # Naming style matching correct variable names. variable-naming-style=snake_case # Regular expression matching correct variable names. Overrides variable- # naming-style. #variable-rgx= [STRING] # This flag controls whether the implicit-str-concat-in-sequence should # generate a warning on implicit string concatenation in sequences defined over # several lines. check-str-concat-over-line-jumps=no [IMPORTS] # Allow wildcard imports from modules that define __all__. allow-wildcard-with-all=no # Analyse import fallback blocks. This can be used to support both Python 2 and # 3 compatible code, which means that the block might have code that exists # only in one or another interpreter, leading to false positives when analysed. analyse-fallback-blocks=no # Deprecated modules which should not be used, separated by a comma. deprecated-modules=optparse,tkinter.tix # Create a graph of external dependencies in the given file (report RP0402 must # not be disabled). ext-import-graph= # Create a graph of every (i.e. internal and external) dependencies in the # given file (report RP0402 must not be disabled). import-graph= # Create a graph of internal dependencies in the given file (report RP0402 must # not be disabled). int-import-graph= # Force import order to recognize a module as part of the standard # compatibility libraries. known-standard-library= # Force import order to recognize a module as part of a third party library. known-third-party=enchant [CLASSES] # List of method names used to declare (i.e. assign) instance attributes. defining-attr-methods=__init__, __new__, setUp # List of member names, which should be excluded from the protected access # warning. exclude-protected=_asdict, _fields, _replace, _source, _make # List of valid names for the first argument in a class method. valid-classmethod-first-arg=cls # List of valid names for the first argument in a metaclass class method. valid-metaclass-classmethod-first-arg=cls [DESIGN] # Maximum number of arguments for function / method. max-args=5 # Maximum number of attributes for a class (see R0902). max-attributes=7 # Maximum number of boolean expressions in an if statement. max-bool-expr=5 # Maximum number of branch for function / method body. max-branches=12 # Maximum number of locals for function / method body. max-locals=15 # Maximum number of parents for a class (see R0901). max-parents=7 # Maximum number of public methods for a class (see R0904). max-public-methods=20 # Maximum number of return / yield for function / method body. max-returns=6 # Maximum number of statements in function / method body. max-statements=50 # Minimum number of public methods for a class (see R0903). min-public-methods=2 [EXCEPTIONS] # Exceptions that will emit a warning when being caught. Defaults to # "BaseException, Exception". overgeneral-exceptions=BaseException, Exception dacite-1.9.2-fix/CHANGELOG.md000066400000000000000000000077171475062640500153760ustar00rootroot00000000000000# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] ... ## [1.9.2] - 2025-02-05 - Clean up the fix for readonly attribute `__args__` (introduced in v1.9.1) - Improve class `Data(Protocol)` typing to avoid pyright errors ## [1.9.1] - 2025-01-29 ### Fixed - Fix `AttributeError` when trying to concretize `list` or `tuple`. Introduced in 1.9.0. ## [1.9.0] - 2025-01-29 ### Added - [Support generics](https://github.com/konradhalas/dacite/pull/272) - Change type definition for `Data` in order to be more permissive ### Fixed - [Fix union matches using .popitem() when no matches are found](https://github.com/konradhalas/dacite/pull/233) - [Supress unnecessary context in some stack trackes](https://github.com/konradhalas/dacite/pull/265) - Fix issues with caching internal function calls ## [1.8.1] - 2023-05-12 ### Fixed - Fix value creation for a field with a default factory - Suppress context in dacite ForwardReferenceError and MissingValueError ## [1.8.0] - 2023-01-26 ### Added - Performance improvements (multiple small changes) - Introduce cache functionality ### Fixed - Fix set's casting - Use `localns` for `get_type_hints` instead of `globalns` ## [1.7.0] - 2022-12-22 ### Added - Add explicit `__all__` configuration - Add Python 3.10 and 3.11 support - Support [PEP 604] unions through `types.UnionType` [PEP 604]: https://peps.python.org/pep-0604/ ### Fixed - Do not suppress `KeyError` in a type hook - Handle tuple as a `Sequence` - Handle a default value for a non-init field within a frozen dataclass - Run type hooks / cast for `InitVar` fields - Fix optional unions ## [1.6.0] - 2020-11-30 ### Added - Support `Type` fields ### Fixed - Handle generic collections with implicit `Any` inner types in Python 3.9 - Fix `InitVar` with inner data classes - Improve support for fixed length and undefined length tuples ## [1.5.1] - 2020-07-03 ### Added - Python 3.9 support ## [1.5.0] - 2020-05-02 ### Added - Add `strict_unions_match` config parameter - Support `Tuple` ### Fixed - The order of run type hooks in `Union` ## [1.4.0] - 2020-04-10 ### Added - Support `InitVar` ### Fixed - Fix `Union` type hooks ## [1.3.0] - 2020-03-14 ### Added - Support `Literal` - Support [PEP 561](https://www.python.org/dev/peps/pep-0561/) ## [1.2.1] - 2020-03-02 ### Fixed - Fix problem with a "numeric tower" and optional/new type ## [1.2.0] - 2020-01-05 ### Added - Support base classes in `cast` - Support collections in `cast` ### Changed - Handle "numeric tower" as described in [PEP 484](https://www.python.org/dev/peps/pep-0484/#the-numeric-tower) ## [1.1.0] - 2019-11-27 ### Added - Python 3.8 support - `cast` config parameter ### Changed - Validate type for generic collection fields [Unreleased]: https://github.com/konradhalas/dacite/compare/v1.9.2...HEAD [1.9.2]: https://github.com/konradhalas/dacite/compare/v1.9.1...v1.9.2 [1.9.1]: https://github.com/konradhalas/dacite/compare/v1.9.0...v1.9.1 [1.9.0]: https://github.com/konradhalas/dacite/compare/v1.8.1...v1.9.0 [1.8.1]: https://github.com/konradhalas/dacite/compare/v1.8.0...v1.8.1 [1.8.0]: https://github.com/konradhalas/dacite/compare/v1.7.0...v1.8.0 [1.7.0]: https://github.com/konradhalas/dacite/compare/v1.6.0...v1.7.0 [1.6.0]: https://github.com/konradhalas/dacite/compare/v1.5.1...v1.6.0 [1.5.1]: https://github.com/konradhalas/dacite/compare/v1.5.0...v1.5.1 [1.5.0]: https://github.com/konradhalas/dacite/compare/v1.4.0...v1.5.0 [1.4.0]: https://github.com/konradhalas/dacite/compare/v1.3.0...v1.4.0 [1.3.0]: https://github.com/konradhalas/dacite/compare/v1.2.1...v1.3.0 [1.2.1]: https://github.com/konradhalas/dacite/compare/v1.2.0...v1.2.1 [1.2.0]: https://github.com/konradhalas/dacite/compare/v1.1.0...v1.2.0 [1.1.0]: https://github.com/konradhalas/dacite/compare/v1.0.2...v1.1.0 dacite-1.9.2-fix/LICENSE000066400000000000000000000020551475062640500145600ustar00rootroot00000000000000MIT License Copyright (c) 2018 Konrad Hałas Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.dacite-1.9.2-fix/README.md000066400000000000000000000366301475062640500150400ustar00rootroot00000000000000![](https://user-images.githubusercontent.com/1078369/212840759-174c0f2b-d446-4c3a-b97c-67a0b912e7f6.png) # dacite [![Build Status](https://travis-ci.org/konradhalas/dacite.svg?branch=master)](https://travis-ci.org/konradhalas/dacite) [![Coverage Status](https://coveralls.io/repos/github/konradhalas/dacite/badge.svg?branch=master)](https://coveralls.io/github/konradhalas/dacite?branch=master) [![License](https://img.shields.io/pypi/l/dacite.svg)](https://pypi.python.org/pypi/dacite/) [![Version](https://img.shields.io/pypi/v/dacite.svg)](https://pypi.python.org/pypi/dacite/) [![Python versions](https://img.shields.io/pypi/pyversions/dacite.svg)](https://pypi.python.org/pypi/dacite/) [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black) This module simplifies creation of data classes ([PEP 557][pep-557]) from dictionaries. ## Installation To install dacite, simply use `pip`: ``` $ pip install dacite ``` ## Requirements Minimum Python version supported by `dacite` is 3.7. ## Quick start ```python from dataclasses import dataclass from dacite import from_dict @dataclass class User: name: str age: int is_active: bool data = { 'name': 'John', 'age': 30, 'is_active': True, } user = from_dict(data_class=User, data=data) assert user == User(name='John', age=30, is_active=True) ``` ## Features Dacite supports following features: - nested structures - (basic) type checking - optional fields (i.e. `typing.Optional`) - unions - generics - forward references - collections - custom type hooks - case conversion ## Motivation Passing plain dictionaries as a data container between your functions or methods isn't a good practice. Of course you can always create your custom class instead, but this solution is an overkill if you only want to merge a few fields within a single object. Fortunately Python has a good solution to this problem - data classes. Thanks to `@dataclass` decorator you can easily create a new custom type with a list of given fields in a declarative manner. Data classes support type hints by design. However, even if you are using data classes, you have to create their instances somehow. In many such cases, your input is a dictionary - it can be a payload from a HTTP request or a raw data from a database. If you want to convert those dictionaries into data classes, `dacite` is your best friend. This library was originally created to simplify creation of type hinted data transfer objects (DTO) which can cross the boundaries in the application architecture. It's important to mention that `dacite` is not a data validation library. There are dozens of awesome data validation projects and it doesn't make sense to duplicate this functionality within `dacite`. If you want to validate your data first, you should combine `dacite` with one of data validation library. Please check [Use Case](#use-case) section for a real-life example. ## Usage Dacite is based on a single function - `dacite.from_dict`. This function takes 3 parameters: - `data_class` - data class type - `data` - dictionary of input data - `config` (optional) - configuration of the creation process, instance of `dacite.Config` class Configuration is a (data) class with following fields: - `type_hooks` - `cast` - `forward_references` - `check_types` - `strict` - `strict_unions_match` - `convert_key` The examples below show all features of `from_dict` function and usage of all `Config` parameters. ### Nested structures You can pass a data with nested dictionaries and it will create a proper result. ```python @dataclass class A: x: str y: int @dataclass class B: a: A data = { 'a': { 'x': 'test', 'y': 1, } } result = from_dict(data_class=B, data=data) assert result == B(a=A(x='test', y=1)) ``` ### Optional fields Whenever your data class has a `Optional` field and you will not provide input data for this field, it will take the `None` value. ```python from typing import Optional @dataclass class A: x: str y: Optional[int] data = { 'x': 'test', } result = from_dict(data_class=A, data=data) assert result == A(x='test', y=None) ``` ### Unions If your field can accept multiple types, you should use `Union`. Dacite will try to match data with provided types one by one. If none will match, it will raise `UnionMatchError` exception. ```python from typing import Union @dataclass class A: x: str @dataclass class B: y: int @dataclass class C: u: Union[A, B] data = { 'u': { 'y': 1, }, } result = from_dict(data_class=C, data=data) assert result == C(u=B(y=1)) ``` ### Collections Dacite supports fields defined as collections. It works for both - basic types and data classes. ```python @dataclass class A: x: str y: int @dataclass class B: a_list: List[A] data = { 'a_list': [ { 'x': 'test1', 'y': 1, }, { 'x': 'test2', 'y': 2, } ], } result = from_dict(data_class=B, data=data) assert result == B(a_list=[A(x='test1', y=1), A(x='test2', y=2)]) ``` ### Generics Dacite supports generics: (multi-)generic dataclasses, but also dataclasses that inherit from a generic dataclass, or dataclasses that have a generic dataclass field. ```python T = TypeVar('T') U = TypeVar('U') @dataclass class X: a: str @dataclass class A(Generic[T, U]): x: T y: list[U] data = { 'x': { 'a': 'foo', }, 'y': [1, 2, 3] } result = from_dict(data_class=A[X, int], data=data) assert result == A(x=X(a='foo'), y=[1,2,3]) @dataclass class B(A[X, int]): z: str data = { 'x': { 'a': 'foo', }, 'y': [1, 2, 3], 'z': 'bar' } result = from_dict(data_class=B, data=data) assert result == B(x=X(a='foo'), y=[1,2,3], z='bar') @dataclass class C: z: A[X, int] data = { 'z': { 'x': { 'a': 'foo', }, 'y': [1, 2, 3], } } result = from_dict(data_class=C, data=data) assert result == C(z=A(x=X(a='foo'), y=[1,2,3])) ``` ### Type hooks You can use `Config.type_hooks` argument if you want to transform the input data of a data class field with given type into the new value. You have to pass a following mapping: `{Type: callable}`, where `callable` is a `Callable[[Any], Any]`. ```python @dataclass class A: x: str data = { 'x': 'TEST', } result = from_dict(data_class=A, data=data, config=Config(type_hooks={str: str.lower})) assert result == A(x='test') ``` If a data class field type is a `Optional[T]` you can pass both - `Optional[T]` or just `T` - as a key in `type_hooks`. The same with generic collections, e.g. when a field has type `List[T]` you can use `List[T]` to transform whole collection or `T` to transform each item. ### Casting It's a very common case that you want to create an instance of a field type from the input data with just calling your type with the input value. Of course you can use `type_hooks={T: T}` to achieve this goal but `cast=[T]` is an easier and more expressive way. It also works with base classes - if `T` is a base class of type `S`, all fields of type `S` will be also "casted". ```python from enum import Enum class E(Enum): X = 'x' Y = 'y' Z = 'z' @dataclass class A: e: E data = { 'e': 'x', } result = from_dict(data_class=A, data=data, config=Config(cast=[E])) # or result = from_dict(data_class=A, data=data, config=Config(cast=[Enum])) assert result == A(e=E.X) ``` ### Forward References Definition of forward references can be passed as a `{'name': Type}` mapping to `Config.forward_references`. This dict is passed to `typing.get_type_hints()` as the `globalns` param when evaluating each field's type. ```python @dataclass class X: y: "Y" @dataclass class Y: s: str data = from_dict(X, {"y": {"s": "text"}}, Config(forward_references={"Y": Y})) assert data == X(Y("text")) ``` ### Type checking If you want to trade-off type checking for speed, you can disabled type checking by setting `check_types` to `False`. ```python @dataclass class A: x: str # won't throw an error even though the type is wrong from_dict(A, {'x': 4}, config=Config(check_types=False)) ``` ### Strict mode By default `from_dict` ignores additional keys (not matching data class field) in the input data. If you want change this behaviour set `Config.strict` to `True`. In case of unexpected key `from_dict` will raise `UnexpectedDataError` exception. ### Strict unions match `Union` allows to define multiple possible types for a given field. By default `dacite` is trying to find the first matching type for a provided data and it returns instance of this type. It means that it's possible that there are other matching types further on the `Union` types list. With `strict_unions_match` only a single match is allowed, otherwise `dacite` raises `StrictUnionMatchError`. ## Convert key You can pass a callable to the `convert_key` configuration parameter to convert camelCase to snake_case. ```python def to_camel_case(key: str) -> str: first_part, *remaining_parts = key.split('_') return first_part + ''.join(part.title() for part in remaining_parts) @dataclass class Person: first_name: str last_name: str data = { 'firstName': 'John', 'lastName': 'Doe' } result = from_dict(Person, data, Config(convert_key=to_camel_case)) assert result == Person(first_name='John', last_name='Doe') ``` ## Exceptions Whenever something goes wrong, `from_dict` will raise adequate exception. There are a few of them: - `WrongTypeError` - raised when a type of a input value does not match with a type of a data class field - `MissingValueError` - raised when you don't provide a value for a required field - `UnionMatchError` - raised when provided data does not match any type of `Union` - `ForwardReferenceError` - raised when undefined forward reference encountered in dataclass - `UnexpectedDataError` - raised when `strict` mode is enabled and the input data has not matching keys - `StrictUnionMatchError` - raised when `strict_unions_match` mode is enabled and the input data has ambiguous `Union` match ## Development First of all - if you want to submit your pull request, thank you very much! I really appreciate your support. Please remember that every new feature, bug fix or improvement should be tested. 100% code coverage is a must-have. We are using a few static code analysis tools to increase the code quality (`black`, `mypy`, `pylint`). Please make sure that you are not generating any errors/warnings before you submit your PR. You can find current configuration in `.github/*` directory. Last but not least, if you want to introduce new feature, please discuss it first within an issue. ### How to start Clone `dacite` repository: ```bash git clone git@github.com:konradhalas/dacite.git ``` Create and activate virtualenv in the way you like: ```bash python3 -m venv dacite-env source dacite-env/bin/activate ``` Install all `dacite` dependencies: ```bash pip install -e .[dev] ``` And, optionally but recommended, install pre-commit hook for black: ```bash pre-commit install ``` To run tests you just have to fire: ```bash pytest ``` ### Performance testing `dacite` is a small library, but its use is potentially very extensive. Thus, it is crucial to ensure good performance of the library. We achieve that with the help of `pytest-benchmark` library, and a suite of dedicated performance tests which can be found in the `tests/performance` directory. The CI process runs these tests automatically, but they can also be helpful locally, while developing the library. Whenever you run `pytest` command, a new benchmark report is saved to `/.benchmarks` directory. You can easily compare these reports by running: `pytest-benchmark compare`, which will load all the runs and display them in a table, where you can compare the performance of each run. You can even specify which particular runs you want to compare, e.g. `pytest-benchmark compare 0001 0003 0005`. ## Use case There are many cases when we receive "raw" data (Python dicts) as a input to our system. HTTP request payload is a very common use case. In most web frameworks we receive request data as a simple dictionary. Instead of passing this dict down to your "business" code, it's a good idea to create something more "robust". Following example is a simple `flask` app - it has single `/products` endpoint. You can use this endpoint to "create" product in your system. Our core `create_product` function expects data class as a parameter. Thanks to `dacite` we can easily build such data class from `POST` request payload. ```python from dataclasses import dataclass from typing import List from flask import Flask, request, Response import dacite app = Flask(__name__) @dataclass class ProductVariantData: code: str description: str = '' stock: int = 0 @dataclass class ProductData: name: str price: float variants: List[ProductVariantData] def create_product(product_data: ProductData) -> None: pass # your business logic here @app.route("/products", methods=['POST']) def products(): product_data = dacite.from_dict( data_class=ProductData, data=request.get_json(), ) create_product(product_data=product_data) return Response(status=201) ``` What if we want to validate our data (e.g. check if `code` has 6 characters)? Such features are out of scope of `dacite` but we can easily combine it with one of data validation library. Let's try with [marshmallow](https://marshmallow.readthedocs.io). First of all we have to define our data validation schemas: ```python from marshmallow import Schema, fields, ValidationError def validate_code(code): if len(code) != 6: raise ValidationError('Code must have 6 characters.') class ProductVariantDataSchema(Schema): code = fields.Str(required=True, validate=validate_code) description = fields.Str(required=False) stock = fields.Int(required=False) class ProductDataSchema(Schema): name = fields.Str(required=True) price = fields.Decimal(required=True) variants = fields.Nested(ProductVariantDataSchema(many=True)) ``` And use them within our endpoint: ```python @app.route("/products", methods=['POST']) def products(): schema = ProductDataSchema() result, errors = schema.load(request.get_json()) if errors: return Response( response=json.dumps(errors), status=400, mimetype='application/json', ) product_data = dacite.from_dict( data_class=ProductData, data=result, ) create_product(product_data=product_data) return Response(status=201) ``` Still `dacite` helps us to create data class from "raw" dict with validated data. ### Cache `dacite` uses some LRU caching to improve its performance where possible. To use the caching utility: ```python from dacite import set_cache_size, get_cache_size, clear_cache get_cache_size() # outputs the current LRU max_size, default is 2048 set_cache_size(4096) # set LRU max_size to 4096 set_cache_size(None) # set LRU max_size to None clear_cache() # Clear the cache ``` The caching is completely transparent from the interface perspective. ## Changelog Follow `dacite` updates in [CHANGELOG][changelog]. ## Authors Created by [Konrad Hałas][halas-homepage]. [pep-557]: https://www.python.org/dev/peps/pep-0557/ [halas-homepage]: https://konradhalas.pl [changelog]: https://github.com/konradhalas/dacite/blob/master/CHANGELOG.md dacite-1.9.2-fix/dacite/000077500000000000000000000000001475062640500150025ustar00rootroot00000000000000dacite-1.9.2-fix/dacite/__init__.py000066400000000000000000000012231475062640500171110ustar00rootroot00000000000000from dacite.cache import set_cache_size, get_cache_size, clear_cache from dacite.config import Config from dacite.core import from_dict from dacite.exceptions import ( DaciteError, DaciteFieldError, WrongTypeError, MissingValueError, UnionMatchError, StrictUnionMatchError, ForwardReferenceError, UnexpectedDataError, ) __all__ = [ "set_cache_size", "get_cache_size", "clear_cache", "Config", "from_dict", "DaciteError", "DaciteFieldError", "WrongTypeError", "MissingValueError", "UnionMatchError", "StrictUnionMatchError", "ForwardReferenceError", "UnexpectedDataError", ] dacite-1.9.2-fix/dacite/cache.py000066400000000000000000000011351475062640500164170ustar00rootroot00000000000000from functools import lru_cache from typing import TypeVar, Callable, Optional T = TypeVar("T", bound=Callable) __MAX_SIZE: Optional[int] = 2048 @lru_cache(maxsize=None) def cache(function: T) -> T: return lru_cache(maxsize=get_cache_size(), typed=True)(function) # type: ignore def set_cache_size(size: Optional[int]) -> None: global __MAX_SIZE # pylint: disable=global-statement __MAX_SIZE = size def get_cache_size() -> Optional[int]: global __MAX_SIZE # pylint: disable=global-variable-not-assigned return __MAX_SIZE def clear_cache() -> None: cache.cache_clear() dacite-1.9.2-fix/dacite/config.py000066400000000000000000000017261475062640500166270ustar00rootroot00000000000000import sys from dataclasses import dataclass, field from typing import Dict, Any, Callable, Optional, Type, List from dacite.frozen_dict import FrozenDict if sys.version_info >= (3, 8): from functools import cached_property # type: ignore # pylint: disable=no-name-in-module else: # Remove when we drop support for Python<3.8 cached_property = property # type: ignore # pylint: disable=invalid-name @dataclass class Config: type_hooks: Dict[Type, Callable[[Any], Any]] = field(default_factory=dict) cast: List[Type] = field(default_factory=list) forward_references: Optional[Dict[str, Any]] = None check_types: bool = True strict: bool = False strict_unions_match: bool = False convert_key: Callable[[str], str] = field(default_factory=lambda: lambda x: x) @cached_property def hashable_forward_references(self) -> Optional[FrozenDict]: return FrozenDict(self.forward_references) if self.forward_references else None dacite-1.9.2-fix/dacite/core.py000066400000000000000000000135761475062640500163200ustar00rootroot00000000000000from dataclasses import is_dataclass from itertools import zip_longest from typing import TypeVar, Type, Optional, Mapping, Any, Collection, MutableMapping from dacite.cache import cache from dacite.config import Config from dacite.data import Data from dacite.dataclasses import ( get_default_value_for_field, DefaultValueNotFoundError, is_frozen, ) from dacite.exceptions import ( ForwardReferenceError, WrongTypeError, DaciteError, UnionMatchError, MissingValueError, DaciteFieldError, UnexpectedDataError, StrictUnionMatchError, ) from dacite.types import ( is_instance, is_generic_collection, is_union, extract_generic, is_optional, extract_origin_collection, is_init_var, extract_init_var, is_subclass, ) from dacite.generics import get_concrete_type_hints, get_fields, orig T = TypeVar("T") def from_dict(data_class: Type[T], data: Data, config: Optional[Config] = None) -> T: """Create a data class instance from a dictionary. :param data_class: a data class type :param data: a dictionary of a input data :param config: a configuration of the creation process :return: an instance of a data class """ init_values: MutableMapping[str, Any] = {} post_init_values: MutableMapping[str, Any] = {} config = config or Config() try: data_class_hints = cache(get_concrete_type_hints)(data_class, localns=config.hashable_forward_references) except NameError as error: raise ForwardReferenceError(str(error)) from None data_class_fields = cache(get_fields)(data_class) if config.strict: extra_fields = set(data.keys()) - {f.name for f in data_class_fields} if extra_fields: raise UnexpectedDataError(keys=extra_fields) for field in data_class_fields: field_type = data_class_hints[field.name] key = config.convert_key(field.name) if key in data: try: value = _build_value(type_=field_type, data=data[key], config=config) except DaciteFieldError as error: error.update_path(field.name) raise if config.check_types and not is_instance(value, field_type): raise WrongTypeError(field_path=field.name, field_type=field_type, value=value) else: try: value = get_default_value_for_field(field, field_type) except DefaultValueNotFoundError: if not field.init: continue raise MissingValueError(field.name) from None if field.init: init_values[field.name] = value elif not is_frozen(data_class): post_init_values[field.name] = value instance = data_class(**init_values) for key, value in post_init_values.items(): setattr(instance, key, value) return instance def _build_value(type_: Type, data: Any, config: Config) -> Any: if is_init_var(type_): type_ = extract_init_var(type_) if type_ in config.type_hooks: data = config.type_hooks[type_](data) if is_optional(type_) and data is None: return data if is_union(type_): data = _build_value_for_union(union=type_, data=data, config=config) elif is_generic_collection(type_): data = _build_value_for_collection(collection=type_, data=data, config=config) elif cache(is_dataclass)(orig(type_)) and isinstance(data, Mapping): data = from_dict(data_class=type_, data=data, config=config) for cast_type in config.cast: if is_subclass(type_, cast_type): if is_generic_collection(type_): data = extract_origin_collection(type_)(data) else: data = type_(data) break return data def _build_value_for_union(union: Type, data: Any, config: Config) -> Any: types = extract_generic(union) if is_optional(union) and len(types) == 2: return _build_value(type_=types[0], data=data, config=config) union_matches = {} for inner_type in types: try: # noinspection PyBroadException try: value = _build_value(type_=inner_type, data=data, config=config) except Exception: # pylint: disable=broad-except continue if is_instance(value, inner_type): if config.strict_unions_match: union_matches[inner_type] = value else: return value except DaciteError: pass if config.strict_unions_match and union_matches: if len(union_matches) > 1: raise StrictUnionMatchError(union_matches) return union_matches.popitem()[1] if not config.check_types: return data raise UnionMatchError(field_type=union, value=data) def _build_value_for_collection(collection: Type, data: Any, config: Config) -> Any: data_type = data.__class__ if isinstance(data, Mapping) and is_subclass(collection, Mapping): item_type = extract_generic(collection, defaults=(Any, Any))[1] return data_type((key, _build_value(type_=item_type, data=value, config=config)) for key, value in data.items()) elif isinstance(data, tuple) and is_subclass(collection, tuple): if not data: return data_type() types = extract_generic(collection) if len(types) == 2 and types[1] == Ellipsis: return data_type(_build_value(type_=types[0], data=item, config=config) for item in data) return data_type( _build_value(type_=type_, data=item, config=config) for item, type_ in zip_longest(data, types) ) elif isinstance(data, Collection) and is_subclass(collection, Collection): item_type = extract_generic(collection, defaults=(Any,))[0] return data_type(_build_value(type_=item_type, data=item, config=config) for item in data) return data dacite-1.9.2-fix/dacite/data.py000066400000000000000000000005121475062640500162630ustar00rootroot00000000000000try: from typing import Protocol, Any # type: ignore except ImportError: from typing_extensions import Protocol, Any # type: ignore # fmt: off class Data(Protocol): def keys(self) -> Any: ... def __getitem__(self, *args, **kwargs) -> Any: ... def __contains__(self, *args, **kwargs) -> bool: ... # fmt: on dacite-1.9.2-fix/dacite/dataclasses.py000066400000000000000000000016351475062640500176500ustar00rootroot00000000000000from dataclasses import Field, MISSING, _FIELDS, _FIELD, _FIELD_INITVAR # type: ignore from typing import Type, Any, TypeVar, List from dacite.cache import cache from dacite.types import is_optional T = TypeVar("T", bound=Any) class DefaultValueNotFoundError(Exception): pass def get_default_value_for_field(field: Field, type_: Type) -> Any: if field.default != MISSING: return field.default elif field.default_factory != MISSING: # type: ignore return field.default_factory() # type: ignore elif is_optional(type_): return None raise DefaultValueNotFoundError() @cache def get_fields(data_class: Type[T]) -> List[Field]: fields = getattr(data_class, _FIELDS) return [f for f in fields.values() if f._field_type is _FIELD or f._field_type is _FIELD_INITVAR] @cache def is_frozen(data_class: Type[T]) -> bool: return data_class.__dataclass_params__.frozen dacite-1.9.2-fix/dacite/exceptions.py000066400000000000000000000051251475062640500175400ustar00rootroot00000000000000from typing import Any, Type, Optional, Set, Dict from dacite.types import is_union def _name(type_: Type) -> str: return type_.__name__ if hasattr(type_, "__name__") and not is_union(type_) else str(type_) class DaciteError(Exception): pass class DaciteFieldError(DaciteError): def __init__(self, field_path: Optional[str] = None): super().__init__() self.field_path = field_path def update_path(self, parent_field_path: str) -> None: if self.field_path: self.field_path = f"{parent_field_path}.{self.field_path}" else: self.field_path = parent_field_path class WrongTypeError(DaciteFieldError): def __init__(self, field_type: Type, value: Any, field_path: Optional[str] = None) -> None: super().__init__(field_path=field_path) self.field_type = field_type self.value = value def __str__(self) -> str: return ( f'wrong value type for field "{self.field_path}" - should be "{_name(self.field_type)}" ' f'instead of value "{self.value}" of type "{_name(type(self.value))}"' ) class MissingValueError(DaciteFieldError): def __init__(self, field_path: Optional[str] = None): super().__init__(field_path=field_path) def __str__(self) -> str: return f'missing value for field "{self.field_path}"' class UnionMatchError(WrongTypeError): def __str__(self) -> str: return ( f'can not match type "{_name(type(self.value))}" to any type ' f'of "{self.field_path}" union: {_name(self.field_type)}' ) class StrictUnionMatchError(DaciteFieldError): def __init__(self, union_matches: Dict[Type, Any], field_path: Optional[str] = None) -> None: super().__init__(field_path=field_path) self.union_matches = union_matches def __str__(self) -> str: conflicting_types = ", ".join(_name(type_) for type_ in self.union_matches) return f'can not choose between possible Union matches for field "{self.field_path}": {conflicting_types}' class ForwardReferenceError(DaciteError): def __init__(self, message: str) -> None: super().__init__() self.message = message def __str__(self) -> str: return f"can not resolve forward reference: {self.message}" class UnexpectedDataError(DaciteError): def __init__(self, keys: Set[str]) -> None: super().__init__() self.keys = keys def __str__(self) -> str: formatted_keys = ", ".join(f'"{key}"' for key in self.keys) return f"can not match {formatted_keys} to any data class field" dacite-1.9.2-fix/dacite/frozen_dict.py000066400000000000000000000015071475062640500176650ustar00rootroot00000000000000from collections.abc import Mapping class FrozenDict(Mapping): dict_cls = dict def __init__(self, *args, **kwargs): self._dict = self.dict_cls(*args, **kwargs) self._hash = None def __getitem__(self, key): return self._dict[key] def __contains__(self, key): return key in self._dict def copy(self, **add_or_replace): return self.__class__(self, **add_or_replace) def __iter__(self): return iter(self._dict) def __len__(self): return len(self._dict) def __repr__(self): return f"<{self.__class__.__name__} {repr(self._dict)}>" def __hash__(self): if self._hash is None: self._hash = 0 for key, value in self._dict.items(): self._hash ^= hash((key, value)) return self._hash dacite-1.9.2-fix/dacite/generics.py000066400000000000000000000101451475062640500171540ustar00rootroot00000000000000import sys from dataclasses import Field, is_dataclass from typing import Any, Dict, Generic, List, Tuple, Type, TypeVar, Union, get_type_hints from dacite.exceptions import DaciteError try: from typing import get_args, get_origin, Literal # type: ignore except ImportError: from typing_extensions import get_args, get_origin, Literal # type: ignore from .dataclasses import get_fields as dataclasses_get_fields def __add_generics(type_origin: Any, type_args: Tuple, generics: Dict[TypeVar, Type]) -> None: """Adds (type var, concrete type) entries derived from a type's origin and args to the provided generics dict.""" if type_origin and type_args and hasattr(type_origin, "__parameters__"): for param, arg in zip(type_origin.__parameters__, type_args): if isinstance(param, TypeVar): if param in generics and generics[param] != arg: raise DaciteError(f"Ambiguous TypeVar: {generics[param]} != {arg}") generics[param] = arg def __dereference(type_name: str, data_class: Type) -> Type: """ Try to find the class belonging to the reference in the provided module and, if not found, iteratively look in parent modules. """ if data_class.__class__.__name__ == type_name: return data_class module_name = data_class.__module__ parts = module_name.split(".") for i in range(len(parts)): try: module = sys.modules[".".join(parts[:-i]) if i else module_name] return getattr(module, type_name) except AttributeError: pass raise AttributeError("Could not find reference.") def __concretize( hint: Union[Type, TypeVar, str], generics: Dict[TypeVar, Type], data_class: Type ) -> Union[Type, TypeVar]: """Recursively replace type vars and forward references by concrete types.""" if isinstance(hint, str): return __dereference(hint, data_class) if isinstance(hint, TypeVar): # Fall back on the original TypeVar if the generics dict does not contain it. # Setting config.check_types=False will in some cases still make from_dict work, albeit not type checked ofc. return generics.get(hint, hint) hint_origin = get_origin(hint) hint_args = get_args(hint) if hint_origin and hint_args and hint_origin is not Literal: concrete_hint_args = tuple(__concretize(a, generics, data_class) for a in hint_args) if concrete_hint_args != hint_args: if sys.version_info >= (3, 9): return hint_origin[concrete_hint_args] # It's generally not a good practice to overwrite __args__, # and it even has become impossible starting from python 3.13 (read-only), # but changing the output of get_type_hints is harmless (see unit test) # and at least this way, we get it working for python 3.8. hint.__args__ = concrete_hint_args return hint def orig(data_class: Type) -> Any: if is_dataclass(data_class): return data_class return get_origin(data_class) def get_concrete_type_hints(data_class: Type, *args, **kwargs) -> Dict[str, Any]: """ An overwrite of typing.get_type_hints supporting generics and forward references, i.e. substituting concrete types in type vars and references. """ generics: Dict[TypeVar, Type] = {} dc_origin = get_origin(data_class) dc_args = get_args(data_class) __add_generics(dc_origin, dc_args, generics) if hasattr(data_class, "__orig_bases__"): for base in data_class.__orig_bases__: base_origin = get_origin(base) base_args = get_args(base) if base_origin is not Generic: __add_generics(base_origin, base_args, generics) data_class = orig(data_class) hints = get_type_hints(data_class, *args, **kwargs) for key, hint in hints.copy().items(): hints[key] = __concretize(hint, generics, data_class) return hints def get_fields(data_class: Type) -> List[Field]: """An overwrite of dacite.dataclasses.get_fields supporting generics.""" return dataclasses_get_fields(orig(data_class)) dacite-1.9.2-fix/dacite/py.typed000066400000000000000000000000001475062640500164670ustar00rootroot00000000000000dacite-1.9.2-fix/dacite/types.py000066400000000000000000000121271475062640500165230ustar00rootroot00000000000000from dataclasses import InitVar, is_dataclass from typing import Type, Any, Optional, Union, Collection, TypeVar, Mapping, Tuple, cast as typing_cast try: from typing import get_origin # type: ignore except ImportError: from typing_extensions import get_origin # type: ignore from dacite.cache import cache T = TypeVar("T", bound=Any) @cache def extract_origin_collection(collection: Type) -> Type: try: return collection.__extra__ except AttributeError: return collection.__origin__ @cache def is_optional(type_: Type) -> bool: return is_union(type_) and type(None) in extract_generic(type_) @cache def extract_optional(optional: Type[Optional[T]]) -> T: other_members = [member for member in extract_generic(optional) if member is not type(None)] if other_members: return typing_cast(T, Union[tuple(other_members)]) else: raise ValueError("can not find not-none value") @cache def is_generic(type_: Type) -> bool: return hasattr(type_, "__origin__") @cache def is_union(type_: Type) -> bool: if is_generic(type_) and type_.__origin__ == Union: return True try: from types import UnionType # type: ignore return isinstance(type_, UnionType) except ImportError: return False @cache def is_tuple(type_: Type) -> bool: return is_subclass(type_, tuple) @cache def is_literal(type_: Type) -> bool: try: from typing import Literal # type: ignore return is_generic(type_) and type_.__origin__ == Literal except ImportError: return False @cache def is_new_type(type_: Type) -> bool: return hasattr(type_, "__supertype__") @cache def extract_new_type(type_: Type) -> Type: return type_.__supertype__ @cache def is_init_var(type_: Type) -> bool: return isinstance(type_, InitVar) or type_ is InitVar @cache def extract_init_var(type_: Type) -> Union[Type, Any]: try: return type_.type except AttributeError: return Any @cache def is_generic_collection(type_: Type) -> bool: if not is_generic(type_): return False origin = extract_origin_collection(type_) try: return bool(origin and issubclass(origin, Collection)) except (TypeError, AttributeError): return False @cache def extract_generic(type_: Type, defaults: Tuple = ()) -> tuple: try: if getattr(type_, "_special", False): return defaults if type_.__args__ == (): return (type_.__args__,) return type_.__args__ or defaults # type: ignore except AttributeError: return defaults @cache def is_subclass(sub_type: Type, base_type: Type) -> bool: if is_generic_collection(sub_type): sub_type = extract_origin_collection(sub_type) try: return issubclass(sub_type, base_type) except TypeError: return False @cache def is_type_generic(type_: Type) -> bool: try: return type_.__origin__ in (type, Type) except AttributeError: return False @cache def is_generic_dataclass(type_: Type) -> bool: return is_dataclass(get_origin(type_)) def is_instance(value: Any, type_: Type) -> bool: try: # As described in PEP 484 - section: "The numeric tower" if (type_ in [float, complex] and isinstance(value, (int, float))) or isinstance(value, type_): return True except TypeError: pass if type_ == Any: return True if is_union(type_): return any(is_instance(value, t) for t in extract_generic(type_)) if is_generic_collection(type_): origin = extract_origin_collection(type_) if not isinstance(value, origin): return False if not extract_generic(type_): return True if isinstance(value, tuple) and is_tuple(type_): tuple_types = extract_generic(type_) if len(tuple_types) == 1 and tuple_types[0] == (): return len(value) == 0 if len(tuple_types) == 2 and tuple_types[1] is ...: return all(is_instance(item, tuple_types[0]) for item in value) if len(tuple_types) != len(value): return False return all(is_instance(item, item_type) for item, item_type in zip(value, tuple_types)) if isinstance(value, Mapping): key_type, val_type = extract_generic(type_, defaults=(Any, Any)) for key, val in value.items(): if not is_instance(key, key_type) or not is_instance(val, val_type): return False return True return all(is_instance(item, extract_generic(type_, defaults=(Any,))[0]) for item in value) if is_new_type(type_): return is_instance(value, extract_new_type(type_)) if is_literal(type_): return value in extract_generic(type_) if is_init_var(type_): return is_instance(value, extract_init_var(type_)) if is_type_generic(type_): return is_subclass(value, extract_generic(type_)[0]) if is_generic_dataclass(type_): return isinstance(value, get_origin(type_)) # type: ignore[arg-type] return False dacite-1.9.2-fix/pyproject.toml000066400000000000000000000002031475062640500164600ustar00rootroot00000000000000[tool.black] line-length = 120 [tool.pytest.ini_options] addopts = "--benchmark-autosave --benchmark-json=benchmark.json" dacite-1.9.2-fix/setup.py000066400000000000000000000025631475062640500152710ustar00rootroot00000000000000from setuptools import setup setup( name="dacite", version="1.9.2", description="Simple creation of data classes from dictionaries.", long_description=open("README.md").read(), long_description_content_type="text/markdown", author="Konrad Hałas", author_email="halas.konrad@gmail.com", url="https://github.com/konradhalas/dacite", license="MIT", classifiers=[ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Topic :: Software Development :: Libraries :: Python Modules", ], python_requires=">=3.7", keywords="dataclasses", packages=["dacite"], package_data={"dacite": ["py.typed"]}, install_requires=['dataclasses;python_version<"3.7"'], extras_require={ "dev": ["pytest>=5", "pytest-benchmark", "pytest-cov", "coveralls", "black", "mypy", "pylint", "pre-commit"] }, ) dacite-1.9.2-fix/tests/000077500000000000000000000000001475062640500147135ustar00rootroot00000000000000dacite-1.9.2-fix/tests/__init__.py000066400000000000000000000000001475062640500170120ustar00rootroot00000000000000dacite-1.9.2-fix/tests/common.py000066400000000000000000000006331475062640500165570ustar00rootroot00000000000000import sys import pytest literal_support = init_var_type_support = pytest.mark.skipif(sys.version_info < (3, 8), reason="requires Python 3.8") pep_604_support = pytest.mark.skipif(sys.version_info < (3, 10), reason="requires Python 3.10") type_hints_with_generic_collections_support = type_hinting_using_standard_collections = pytest.mark.skipif( sys.version_info < (3, 9), reason="requires Python 3.9" ) dacite-1.9.2-fix/tests/core/000077500000000000000000000000001475062640500156435ustar00rootroot00000000000000dacite-1.9.2-fix/tests/core/__init__.py000066400000000000000000000000001475062640500177420ustar00rootroot00000000000000dacite-1.9.2-fix/tests/core/test_base.py000066400000000000000000000110451475062640500201670ustar00rootroot00000000000000from dataclasses import dataclass, field from typing import Any, NewType, Optional, List import pytest from dacite import from_dict, MissingValueError, WrongTypeError from tests.common import type_hinting_using_standard_collections def test_from_dict_iterables_with_typing_list(): @dataclass class Foo: bar: List[str] result = from_dict(Foo, {"bar": ["foo", "bar"]}) assert result == Foo(bar=["foo", "bar"]) @type_hinting_using_standard_collections def test_from_dict_iterables(): @dataclass class Foo: bar: list[str] result = from_dict(Foo, {"bar": ["foo", "bar"]}) assert result == Foo(bar=["foo", "bar"]) def test_from_dict_with_correct_data(): @dataclass class X: s: str i: int f: float result = from_dict(X, {"s": "test", "i": 1, "f": 1.0}) assert result == X(s="test", i=1, f=1.0) def test_from_dict_with_default_value(): @dataclass class X: s: str i: int = 0 result = from_dict(X, {"s": "test"}) assert result == X(s="test", i=0) def test_from_dict_with_default_factory(): @dataclass class X: s: str i: int = field(default_factory=lambda: 42) result = from_dict(X, {"s": "test"}) assert result == X(s="test", i=42) def test_from_dict_with_wrong_type(): @dataclass class X: s: str i: int with pytest.raises(WrongTypeError) as exception_info: from_dict(X, {"s": "test", "i": "wrong"}) assert ( str(exception_info.value) == 'wrong value type for field "i" - should be "int" instead of value "wrong" of type "str"' ) assert exception_info.value.field_path == "i" assert exception_info.value.field_type == int assert exception_info.value.value == "wrong" def test_from_dict_with_missing_value(): @dataclass class X: s: str i: int with pytest.raises(MissingValueError) as exception_info: from_dict(X, {"s": "test"}) assert str(exception_info.value) == 'missing value for field "i"' assert exception_info.value.field_path == "i" assert exception_info._excinfo[1].__suppress_context__ def test_from_dict_with_nested_data_class(): @dataclass class X: i: int @dataclass class Y: s: str x: X result = from_dict(Y, {"s": "test", "x": {"i": 1}}) assert result == Y(s="test", x=X(i=1)) def test_from_dict_with_missing_value_of_nested_data_class(): @dataclass class X: i: int @dataclass class Y: x: X with pytest.raises(MissingValueError) as exception_info: from_dict(Y, {"x": {}}) assert exception_info.value.field_path == "x.i" def test_from_dict_with_additional_values(): @dataclass class X: i: int result = from_dict(X, {"i": 1, "s": "extra"}) assert result == X(i=1) def test_from_dict_with_any(): @dataclass class X: i: Any result = from_dict(X, {"i": 1}) assert result == X(i=1) def test_from_dict_with_nested_data_classes_and_default_factory(): @dataclass class X: i: int @dataclass class Y: x: X = field(default_factory=lambda: X(i=42)) result = from_dict(Y, {}) assert result == Y(x=X(i=42)) def test_from_dict_with_post_init(): @dataclass class X: s: str = field(init=False) x = X() x.s = "test" result = from_dict(X, {"s": "test"}) assert result == x def test_from_dict_with_post_init_missing_value(): @dataclass class X: s: str = field(init=False) result = from_dict(X, {}) assert not hasattr(result, "s") def test_from_dict_with_optional_non_init_field(): @dataclass class X: s: Optional[str] = field(init=False) x = X() x.s = None result = from_dict(X, {}) assert result == x def test_from_dict_with_non_init_field_with_default_value_and_frozen_dataclass(): @dataclass(frozen=True) class X: s: str = field(init=False, default="test") result = from_dict(X, {}) assert result == X() def test_from_dict_with_new_type(): MyStr = NewType("MyStr", str) @dataclass class X: s: MyStr result = from_dict(X, {"s": "test"}) assert result == X(s=MyStr("test")) def test_dataclass_default_factory_identity(): # https://github.com/konradhalas/dacite/issues/215 @dataclass class A: name: str items: List[str] = field(default_factory=list) a1 = from_dict(A, {"name": "a1"}) a2 = from_dict(A, {"name": "a2"}) assert a1.items is not a2.items dacite-1.9.2-fix/tests/core/test_collection.py000066400000000000000000000125371475062640500214170ustar00rootroot00000000000000from dataclasses import dataclass from typing import List, Set, Union, Dict, Collection, Tuple, Sequence import pytest from dacite import from_dict, WrongTypeError, Config def test_from_dict_with_generic_collection(): @dataclass class X: l: List[int] result = from_dict(X, {"l": [1]}) assert result == X(l=[1]) def test_from_dict_with_generic_collection_of_data_classes(): @dataclass class X: i: int @dataclass class Y: x_list: List[X] result = from_dict(Y, {"x_list": [{"i": 1}, {"i": 2}]}) assert result == Y(x_list=[X(i=1), X(i=2)]) def test_from_dict_with_generic_collection_of_unions(): @dataclass class X: i: int @dataclass class Y: l: List[Union[int, X]] result = from_dict(Y, {"l": [1, {"i": 2}]}) assert result == Y(l=[1, X(i=2)]) def test_from_dict_with_nested_generic_collection(): @dataclass class X: i: int @dataclass class Y: l: List[List[X]] result = from_dict(Y, {"l": [[{"i": 2}]]}) assert result == Y(l=[[X(i=2)]]) def test_from_dict_with_set(): @dataclass class X: i_set: Set[int] result = from_dict(X, {"i_set": {1, 2}}) assert result == X(i_set={1, 2}) def test_from_dict_with_set_strings(): @dataclass class X: i_set: Set[str] result = from_dict(X, {"i_set": {"a", "b"}}) assert result == X(i_set={"a", "b"}) def test_from_dict_with_cast_of_both_collection_and_inner_type(): @dataclass class X: set_int: Set[int] data = {"set_int": ["1", "2"]} result = from_dict(data_class=X, data=data, config=Config(cast=[set, int])) assert result == X(set_int={1, 2}) def test_from_dict_with_set_of_dataclasses(): @dataclass(frozen=True) class A: i: int @dataclass class X: set_a: Set[A] data = {"set_a": [{"i": 1}, {"i": 2}]} result = from_dict(data_class=X, data=data, config=Config(cast=[set])) assert result == X(set_a={A(i=1), A(i=2)}) def test_from_dict_with_dict(): @dataclass class X: d: Dict[str, int] result = from_dict(X, {"d": {"a": 1, "b": 2}}) assert result == X(d={"a": 1, "b": 2}) def test_from_dict_with_dict_of_data_classes(): @dataclass class X: i: int @dataclass class Y: d: Dict[str, X] result = from_dict(Y, {"d": {"a": {"i": 42}, "b": {"i": 37}}}) assert result == Y(d={"a": X(i=42), "b": X(i=37)}) def test_from_dict_with_already_created_data_class_instances(): @dataclass class X: i: int @dataclass class Y: x: X x_list: List[X] result = from_dict(Y, {"x": X(i=37), "x_list": [X(i=42)]}) assert result == Y(x=X(i=37), x_list=[X(i=42)]) def test_from_dict_with_generic_abstract_collection(): @dataclass class X: l: Collection[int] result = from_dict(X, {"l": [1]}) assert result == X(l=[1]) def test_from_dict_with_wrong_type_of_collection_item(): @dataclass class X: l: List[int] with pytest.raises(WrongTypeError) as exception_info: from_dict(X, {"l": ["1"]}) assert exception_info.value.field_path == "l" assert exception_info.value.field_type == List[int] def test_from_dict_with_wrong_type_of_dict_value(): @dataclass class X: d: Dict[str, int] with pytest.raises(WrongTypeError) as exception_info: from_dict(X, {"d": {"a": "test"}}) assert exception_info.value.field_path == "d" assert exception_info.value.field_type == Dict[str, int] def test_from_dict_with_dict_and_implicit_any_types(): @dataclass class X: d: Dict result = from_dict(X, {"d": {"a": 1}}) assert result == X(d={"a": 1}) def test_from_dict_with_list_and_implicit_any_types(): @dataclass class X: l: List result = from_dict(X, {"l": [1]}) assert result == X(l=[1]) def test_from_dict_with_tuple_of_defined_length(): @dataclass class X: a: int @dataclass class Y: b: int @dataclass class Z: t: Tuple[X, Y] result = from_dict(Z, {"t": ({"a": 1}, {"b": 2})}) assert result == Z(t=(X(a=1), Y(b=2))) def test_from_dict_with_tuple_of_undefined_length(): @dataclass class X: a: int @dataclass class Y: t: Tuple[X, ...] result = from_dict(Y, {"t": ({"a": 1}, {"a": 2})}) assert result == Y(t=(X(a=1), X(a=2))) def test_from_dict_with_tuple_and_wrong_length(): @dataclass class X: a: int @dataclass class Y: b: int @dataclass class Z: t: Tuple[X, Y] with pytest.raises(WrongTypeError) as exception_info: from_dict(Z, {"t": ({"a": 1}, {"b": 2}, {"c": 3})}) assert exception_info.value.field_path == "t" assert exception_info.value.field_type == Tuple[X, Y] def test_from_dict_with_tuple_and_implicit_any_types(): @dataclass class X: t: Tuple result = from_dict(X, {"t": (1, 2, 3)}) assert result == X(t=(1, 2, 3)) def test_from_dict_with_sequence_and_tuple(): @dataclass class X: s: Sequence[int] result = from_dict(X, {"s": (1, 2, 3)}) assert result == X(s=(1, 2, 3)) def test_from_dict_with_sequence_and_empty_tuple(): @dataclass class X: s: Sequence[int] result = from_dict(X, {"s": ()}) assert result == X(s=()) dacite-1.9.2-fix/tests/core/test_config.py000066400000000000000000000111241475062640500205200ustar00rootroot00000000000000from dataclasses import dataclass from enum import Enum from typing import Optional, List, Union import pytest from dacite import ( from_dict, Config, ForwardReferenceError, UnexpectedDataError, StrictUnionMatchError, ) def test_from_dict_with_type_hooks(): @dataclass class X: s: str result = from_dict(X, {"s": "TEST"}, Config(type_hooks={str: str.lower})) assert result == X(s="test") def test_from_dict_with_type_hooks_and_optional(): @dataclass class X: s: Optional[str] result = from_dict(X, {"s": "TEST"}, Config(type_hooks={str: str.lower})) assert result == X(s="test") def test_from_dict_with_type_hooks_and_optional_null_value(): @dataclass class X: s: Optional[str] result = from_dict(X, {"s": None}, Config(type_hooks={str: str.lower})) assert result == X(s=None) def test_from_dict_with_type_hooks_and_union(): @dataclass class X: s: Union[str, int] result = from_dict(X, {"s": "TEST"}, Config(type_hooks={str: str.lower})) assert result == X(s="test") def test_from_dict_with_cast(): @dataclass class X: s: str result = from_dict(X, {"s": 1}, Config(cast=[str])) assert result == X(s="1") def test_from_dict_with_base_class_cast(): class E(Enum): A = "a" @dataclass class X: e: E result = from_dict(X, {"e": "a"}, Config(cast=[Enum])) assert result == X(e=E.A) def test_from_dict_with_base_class_cast_and_optional(): class E(Enum): A = "a" @dataclass class X: e: Optional[E] result = from_dict(X, {"e": "a"}, Config(cast=[Enum])) assert result == X(e=E.A) def test_from_dict_with_cast_and_generic_collection(): @dataclass class X: s: List[int] result = from_dict(X, {"s": (1,)}, Config(cast=[List])) assert result == X(s=[1]) def test_from_dict_with_type_hooks_and_generic_sequence(): @dataclass class X: c: List[str] result = from_dict(X, {"c": ["TEST"]}, config=Config(type_hooks={str: str.lower})) assert result == X(c=["test"]) def test_from_dict_with_type_hook_exception(): @dataclass class X: i: int def raise_error(_): raise KeyError() with pytest.raises(KeyError): from_dict(X, {"i": 1}, config=Config(type_hooks={int: raise_error})) def test_from_dict_with_forward_reference(): @dataclass class X: y: "Y" @dataclass class Y: s: str data = from_dict(X, {"y": {"s": "text"}}, Config(forward_references={"Y": Y})) assert data == X(Y("text")) def test_from_dict_with_missing_forward_reference(): @dataclass class X: y: "Y" @dataclass class Y: s: str with pytest.raises(ForwardReferenceError) as exception_info: from_dict(X, {"y": {"s": "text"}}) assert str(exception_info.value) == "can not resolve forward reference: name 'Y' is not defined" assert exception_info._excinfo[1].__suppress_context__ def test_form_dict_with_disabled_type_checking(): @dataclass class X: i: int result = from_dict(X, {"i": "test"}, config=Config(check_types=False)) # noinspection PyTypeChecker assert result == X(i="test") def test_form_dict_with_disabled_type_checking_and_union(): @dataclass class X: i: Union[int, float] result = from_dict(X, {"i": "test"}, config=Config(check_types=False)) # noinspection PyTypeChecker assert result == X(i="test") def test_from_dict_with_strict(): @dataclass class X: s: str with pytest.raises(UnexpectedDataError) as exception_info: from_dict(X, {"s": "test", "i": 1}, Config(strict=True)) assert str(exception_info.value) == 'can not match "i" to any data class field' def test_from_dict_with_strict_unions_match_and_ambiguous_match(): @dataclass class X: i: int @dataclass class Y: i: int @dataclass class Z: u: Union[X, Y] data = { "u": {"i": 1}, } with pytest.raises(StrictUnionMatchError) as exception_info: from_dict(Z, data, Config(strict_unions_match=True)) assert str(exception_info.value) == 'can not choose between possible Union matches for field "u": X, Y' def test_from_dict_with_strict_unions_match_and_single_match(): @dataclass class X: f: str @dataclass class Y: f: int @dataclass class Z: u: Union[X, Y] data = { "u": {"f": 1}, } result = from_dict(Z, data, Config(strict_unions_match=True)) assert result == Z(u=Y(f=1)) dacite-1.9.2-fix/tests/core/test_convert_key.py000066400000000000000000000010351475062640500216030ustar00rootroot00000000000000from dataclasses import dataclass from dacite import Config, from_dict def test_convert_key(): def to_camel_case(key: str) -> str: first_part, *remaining_parts = key.split("_") return first_part + "".join(part.title() for part in remaining_parts) @dataclass class Person: first_name: str last_name: str data = {"firstName": "John", "lastName": "Doe"} result = from_dict(Person, data, Config(convert_key=to_camel_case)) assert result == Person(first_name="John", last_name="Doe") dacite-1.9.2-fix/tests/core/test_forward_reference.py000066400000000000000000000024421475062640500227400ustar00rootroot00000000000000from dataclasses import dataclass from typing import Generic, List, Optional, TypeVar from dacite import from_dict T = TypeVar("T") @dataclass class Person: name: str children: Optional[List["Person"]] = None @dataclass class Club: name: str members: List["Person"] @dataclass class Employee: name: str @dataclass class Team(Generic[T]): name: str members: Optional[List[T]] = None subteams: Optional[List["Team[T]"]] = None def test_self_reference(): data = {"name": "John Doe", "children": [{"name": "Jane Doe"}]} result = from_dict(Person, data) assert result == Person(name="John Doe", children=[Person(name="Jane Doe")]) def test_other_reference(): data = {"name": "FooBar", "members": [{"name": "John Doe", "children": [{"name": "Jane Doe"}]}]} result = from_dict(Club, data) assert result == Club(name="FooBar", members=[Person(name="John Doe", children=[Person(name="Jane Doe")])]) def test_generic_self_reference(): data = {"name": "foo", "members": [{"name": "John"}], "subteams": [{"name": "bar", "members": [{"name": "Jane"}]}]} result = from_dict(Team[Employee], data) assert result == Team( name="foo", members=[Employee(name="John")], subteams=[Team(name="bar", members=[Employee(name="Jane")])] ) dacite-1.9.2-fix/tests/core/test_generics.py000066400000000000000000000023321475062640500210530ustar00rootroot00000000000000from dataclasses import dataclass from typing import Generic, List, TypeVar, get_type_hints from dacite import from_dict T = TypeVar("T") U = TypeVar("U") @dataclass class X: a: str @dataclass class A(Generic[T, U]): x: T y: List[U] def test_multi_generic(): data = { "x": { "a": "foo", }, "y": [1, 2, 3], } result = from_dict(data_class=A[X, int], data=data) assert result == A(x=X(a="foo"), y=[1, 2, 3]) # assert that the typing object hasn't been modified in-place hints = get_type_hints(A) assert hints["x"].__class__ is TypeVar def test_generic_parent(): @dataclass class B(A[X, int]): z: str data = { "x": { "a": "foo", }, "y": [1, 2, 3], "z": "bar", } result = from_dict(data_class=B, data=data) assert result == B(x=X(a="foo"), y=[1, 2, 3], z="bar") def test_generic_field(): @dataclass class C: z: A[X, int] data = { "z": { "x": { "a": "foo", }, "y": [1, 2, 3], } } result = from_dict(data_class=C, data=data) assert result == C(z=A(x=X(a="foo"), y=[1, 2, 3])) dacite-1.9.2-fix/tests/core/test_init_var.py000066400000000000000000000020761475062640500210740ustar00rootroot00000000000000from dataclasses import dataclass, InitVar, field from dacite import from_dict, Config from tests.common import init_var_type_support def test_from_dict_with_init_var(): @dataclass class X: a: InitVar[int] b: int = field(init=False) def __post_init__(self, a: int) -> None: self.b = 2 * a result = from_dict(X, {"a": 2}) assert result.b == 4 @init_var_type_support def test_from_dict_with_init_var_and_data_class(): @dataclass class X: i: int @dataclass class Y: a: InitVar[X] b: X = field(init=False) def __post_init__(self, a: X) -> None: self.b = X(i=2 * a.i) result = from_dict(Y, {"a": {"i": 2}}) assert result.b == X(i=4) @init_var_type_support def test_from_dict_with_init_var_and_cast(): @dataclass class X: a: InitVar[int] b: int = field(init=False) def __post_init__(self, a: int) -> None: self.b = 2 * a result = from_dict(X, {"a": "2"}, config=Config(cast=[int])) assert result.b == 4 dacite-1.9.2-fix/tests/core/test_literal.py000066400000000000000000000021711475062640500207110ustar00rootroot00000000000000from dataclasses import dataclass from typing import Optional import pytest from dacite import from_dict from dacite.exceptions import WrongTypeError from tests.common import literal_support @literal_support def test_from_dict_with_literal(): from typing import Literal @dataclass class X: l: Literal["A", "B"] result = from_dict(X, {"l": "A"}) assert result == X(l="A") @literal_support def test_from_dict_with_literal_and_wrong_value(): from typing import Literal @dataclass class X: l: Literal["A", "B"] with pytest.raises(WrongTypeError) as exception_info: from_dict(X, {"l": "C"}) @literal_support def test_from_dict_with_optional_literal_and_none(): from typing import Literal @dataclass class X: l: Optional[Literal["A", "B"]] result = from_dict(X, {"l": None}) assert result == X(l=None) @literal_support def test_from_dict_with_optional_literal_and_not_none(): from typing import Literal @dataclass class X: l: Optional[Literal["A", "B"]] result = from_dict(X, {"l": "A"}) assert result == X(l="A") dacite-1.9.2-fix/tests/core/test_optional.py000066400000000000000000000067461475062640500211160ustar00rootroot00000000000000from dataclasses import dataclass from typing import Optional, Union, List, NewType import pytest from dacite import from_dict, MissingValueError, WrongTypeError def test_from_dict_with_missing_optional_value(): @dataclass class X: s: Optional[str] i: int result = from_dict(X, {"i": 1}) assert result == X(s=None, i=1) def test_from_dict_with_existing_optional_value(): @dataclass class X: s: Optional[str] i: int result = from_dict(X, {"s": "test", "i": 1}) assert result == X(s="test", i=1) def test_from_dict_with_missing_optional_value_for_union(): @dataclass class X: i: Optional[Union[int, str]] result = from_dict(X, {}) assert result == X(i=None) def test_from_dict_with_none_optional_value_for_union(): @dataclass class X: i: Optional[Union[int, str]] result = from_dict(X, {"i": None}) assert result == X(i=None) def test_from_dict_with_none_as_optional_value(): @dataclass class X: s: Optional[str] i: int result = from_dict(X, {"s": None, "i": 1}) assert result == X(s=None, i=1) def test_from_dict_with_wrong_type_of_optional_value(): @dataclass class X: s: Optional[str] i: int with pytest.raises(WrongTypeError) as exception_info: from_dict(X, {"s": 1, "i": 1}) assert exception_info.value.field_path == "s" assert exception_info.value.field_type == Optional[str] def test_from_dict_with_missing_optional_nested_data_class(): @dataclass class X: i: int @dataclass class Y: x: Optional[X] result = from_dict(Y, {}) assert result == Y(x=None) def test_from_dict_with_optional_nested_data_class(): @dataclass class X: i: int @dataclass class Y: x: Optional[X] result = from_dict(Y, {"x": {"i": 1}}) assert result == Y(x=X(i=1)) def test_from_dict_with_optional_nested_data_class_and_missing_value(): @dataclass class X: i: int j: int @dataclass class Y: x: Optional[X] with pytest.raises(MissingValueError) as exception_info: from_dict(Y, {"x": {"i": 1}}) assert exception_info.value.field_path == "x.j" def test_from_dict_with_null_as_optional_value_for_nested_data_class(): @dataclass class X: i: int @dataclass class Y: x: Optional[X] result = from_dict(Y, {"x": None}) assert result == Y(x=None) def test_from_dict_with_none_for_non_optional_field(): @dataclass class X: s: str with pytest.raises(WrongTypeError) as exception_info: from_dict(X, {"s": None}) assert exception_info.value.field_path == "s" assert exception_info.value.field_type == str assert exception_info.value.value is None def test_from_dict_with_optional_generic_collection_of_data_classes(): @dataclass class X: i: int @dataclass class Y: x_list: Optional[List[X]] result = from_dict(Y, {"x_list": [{"i": 1}, {"i": 2}]}) assert result == Y(x_list=[X(i=1), X(i=2)]) def test_from_dict_with_optional_field_and_default_value(): @dataclass class X: i: Optional[int] = 1 result = from_dict(X, {}) assert result == X(i=1) def test_from_dict_with_optional_new_type(): MyStr = NewType("MyStr", str) @dataclass class X: s: Optional[MyStr] result = from_dict(X, {"s": MyStr("test")}) assert result == X(s=MyStr("test")) dacite-1.9.2-fix/tests/core/test_type.py000066400000000000000000000003671475062640500202430ustar00rootroot00000000000000from dataclasses import dataclass from typing import Type from dacite import from_dict def test_from_dict_with_type_field(): @dataclass class X: t: Type[int] result = from_dict(X, {"t": int}) assert result == X(t=int) dacite-1.9.2-fix/tests/core/test_union.py000066400000000000000000000076301475062640500204120ustar00rootroot00000000000000from dataclasses import dataclass from typing import Optional, List, Union, Dict import pytest from dacite import from_dict, UnionMatchError def test_from_dict_with_union_of_builtin_types(): @dataclass class X: i: Union[int, str] result = from_dict(X, {"i": "s"}) assert result == X(i="s") def test_from_dict_with_union_of_data_classes(): @dataclass class X: i: int @dataclass class Y: s: str @dataclass class Z: x_or_y: Union[X, Y] result = from_dict(Z, {"x_or_y": {"s": "test"}}) assert result == Z(x_or_y=Y(s="test")) def test_from_dict_with_union_and_wrong_data(): @dataclass class X: i: Union[int, str] with pytest.raises(UnionMatchError) as exception_info: from_dict(X, {"i": 1.0}) assert str(exception_info.value) == 'can not match type "float" to any type of "i" union: typing.Union[int, str]' assert exception_info.value.field_path == "i" assert exception_info.value.field_type == Union[int, str] assert exception_info.value.value == 1.0 def test_from_dict_with_union_of_data_classes_and_wrong_data(): @dataclass class X: i: int @dataclass class Y: s: str @dataclass class Z: x_or_y: Union[X, Y] with pytest.raises(UnionMatchError) as exception_info: from_dict(Z, {"x_or_y": {"f": 2.0}}) assert exception_info.value.field_path == "x_or_y" assert exception_info.value.field_type == Union[X, Y] assert exception_info.value.value == {"f": 2.0} def test_from_dict_with_union_of_generic_collecionts_of_data_classes(): @dataclass class X: i: int @dataclass class Y: s: str @dataclass class Z: x_or_y: Union[List[X], List[Y]] result = from_dict(Z, {"x_or_y": [{"s": "test"}]}) assert result == Z(x_or_y=[Y(s="test")]) def test_from_dict_with_union_and_optional(): @dataclass class X: i: Union[int, Optional[str]] result = from_dict(X, {"i": "s"}) assert result == X(i="s") def test_from_dict_with_union_and_optional_and_missing_value(): @dataclass class X: i: Union[int, Optional[str]] result = from_dict(X, {}) assert result == X(i=None) def test_from_dict_with_union_and_optional_and_none_value(): @dataclass class X: i: Union[int, Optional[str]] result = from_dict(X, {"i": None}) assert result == X(i=None) def test_from_dict_with_union_and_optional_and_wrong_value(): @dataclass class X: i: Union[int, Optional[str]] with pytest.raises(UnionMatchError) as exception_info: from_dict(X, {"i": 1.0}) assert exception_info.value.field_path == "i" assert exception_info.value.field_type == Union[int, str, None] assert exception_info.value.value == 1.0 def test_from_dict_with_union_of_mixed_types_and_builtin_type_as_a_result(): @dataclass class X: i: int @dataclass class Y: u: Union[X, List[X], str] result = from_dict(Y, {"u": "test"}) assert result == Y(u="test") def test_from_dict_with_union_of_mixed_types_and_data_class_as_a_result(): @dataclass class X: i: int @dataclass class Y: u: Union[str, List[X], X] result = from_dict(Y, {"u": {"i": 1}}) assert result == Y(u=X(i=1)) def test_from_dict_with_union_of_mixed_types_and_collection_of_data_classes_as_a_result(): @dataclass class X: i: int @dataclass class Y: u: Union[str, X, List[X]] result = from_dict(Y, {"u": [{"i": 1}]}) assert result == Y(u=[X(i=1)]) def test_from_dict_with_union_of_mixed_types_and_dict_of_data_classes_as_a_result(): @dataclass class X: i: int @dataclass class Y: d: Union[int, List[X], Dict[str, X]] result = from_dict(Y, {"d": {"x": {"i": 42}, "z": {"i": 37}}}) assert result == Y(d={"x": X(i=42), "z": X(i=37)}) dacite-1.9.2-fix/tests/performance/000077500000000000000000000000001475062640500172145ustar00rootroot00000000000000dacite-1.9.2-fix/tests/performance/__init__.py000066400000000000000000000000001475062640500213130ustar00rootroot00000000000000dacite-1.9.2-fix/tests/performance/fixtures/000077500000000000000000000000001475062640500210655ustar00rootroot00000000000000dacite-1.9.2-fix/tests/performance/fixtures/__init__.py000066400000000000000000000000001475062640500231640ustar00rootroot00000000000000dacite-1.9.2-fix/tests/performance/fixtures/classes.py000066400000000000000000000016431475062640500231000ustar00rootroot00000000000000from dataclasses import dataclass from enum import Enum from typing import Optional, List, Set, Union, Dict @dataclass class ForwardRef: inner: "LongUnion" @dataclass class Simple: attr_string: str attr_int: int attr_list: List[str] attr_set: Set[float] attr_string_opt: Optional[str] = None @dataclass class Nested: nested1: Simple nested2: Simple nested_list: List[Simple] nested_dict: Dict[str, Simple] @dataclass class LongUnion: simple_type_union: Union[set, dict, tuple, str, bool] unrealistically_complex_union: Union[int, str, bool, set, list, Simple, Nested] unrealistically_complex_union2: Union[int, str, bool, set, Simple, Nested, List[Nested]] @dataclass class UnionCollection: collection: List[Union[int, str, bool, set, list, dict, Simple, Nested, List[Nested]]] class E(Enum): X = "x" Y = "y" Z = "z" @dataclass class Cast: e: E dacite-1.9.2-fix/tests/performance/fixtures/data.py000066400000000000000000000013671475062640500223570ustar00rootroot00000000000000simple_data = { "attr_string": "some string", "attr_string_opt": None, "attr_int": 12, "attr_list": ["some", "string", "list"], "attr_set": {10.2, 365.0, 12.345}, } nested_data = { "nested1": simple_data, "nested2": simple_data, "nested_list": [simple_data, simple_data, simple_data], "nested_dict": {"a": simple_data, "b": simple_data, "c": simple_data}, } long_union_data = { "simple_type_union": True, "unrealistically_complex_union": nested_data, "unrealistically_complex_union2": [nested_data, nested_data, nested_data], } union_collection_data = {"collection": [1, nested_data, [nested_data, nested_data], "string", {"k": "v"}]} forward_ref_data = {"inner": long_union_data} cast_data = {"e": "z"} dacite-1.9.2-fix/tests/performance/test_from_dict.py000066400000000000000000000060311475062640500225730ustar00rootroot00000000000000from typing import List from dacite import from_dict, Config from .fixtures import data, classes def test_basic_scenario(benchmark): result = benchmark(from_dict, classes.Simple, data.simple_data) assert result == classes.Simple( attr_string=data.simple_data["attr_string"], attr_string_opt=data.simple_data["attr_string_opt"], attr_int=data.simple_data["attr_int"], attr_set=data.simple_data["attr_set"], attr_list=data.simple_data["attr_list"], ) def test_union_matching(benchmark): result = benchmark(from_dict, classes.LongUnion, data.long_union_data) assert isinstance(result, classes.LongUnion) assert result.simple_type_union assert isinstance(result.unrealistically_complex_union, classes.Nested) assert len(result.unrealistically_complex_union2) == 3 for r in result.unrealistically_complex_union2: assert isinstance(r, classes.Nested) def test_strict_unions_match(benchmark): result = benchmark(from_dict, classes.LongUnion, data.long_union_data, config=Config(strict_unions_match=True)) assert isinstance(result, classes.LongUnion) assert result.simple_type_union assert isinstance(result.unrealistically_complex_union, classes.Nested) assert len(result.unrealistically_complex_union2) == 3 for r in result.unrealistically_complex_union2: assert isinstance(r, classes.Nested) def test_collection_of_union(benchmark): result = benchmark(from_dict, classes.UnionCollection, data.union_collection_data) assert result == classes.UnionCollection(collection=data.union_collection_data["collection"]) def test_type_hooks(benchmark): result = benchmark( from_dict, classes.LongUnion, data.long_union_data, config=Config(type_hooks={str: str.upper, float: lambda v: v * 123.123, list: lambda v: v * 2}), ) assert isinstance(result, classes.LongUnion) assert result.simple_type_union assert isinstance(result.unrealistically_complex_union, classes.Nested) assert len(result.unrealistically_complex_union2) == 3 for r in result.unrealistically_complex_union2: assert isinstance(r, classes.Nested) def test_casting(benchmark): result = benchmark(from_dict, data_class=classes.Cast, data=data.cast_data, config=Config(cast=[classes.E])) assert result == classes.Cast(e=classes.E.Z) def test_forward_references(benchmark): result = benchmark( from_dict, classes.ForwardRef, data.forward_ref_data, config=Config(forward_references={"LongUnion": classes.LongUnion}), ) assert isinstance(result, classes.ForwardRef) assert isinstance(result.inner, classes.LongUnion) def test_parsing_multiple_items(benchmark): def parse_iterable(data_class, elements: List[dict]): return [from_dict(data_class=data_class, data=item) for item in elements] result = benchmark(parse_iterable, classes.LongUnion, [data.long_union_data] * 25) assert len(result) == 25 for r in result: assert isinstance(r, classes.LongUnion) dacite-1.9.2-fix/tests/test_cache.py000066400000000000000000000011541475062640500173700ustar00rootroot00000000000000from unittest.mock import Mock, call from dacite.cache import set_cache_size, get_cache_size, cache def test_cache_size(): set_cache_size(4321) assert get_cache_size() == 4321 function = Mock() cache(function)(get_cache_size()) set_cache_size(8765) cache(function)(get_cache_size()) set_cache_size(None) cache(function)(get_cache_size()) assert function.call_count == 3 assert function.mock_calls == [call(4321), call(8765), call(None)] def test_cache_from_function(): function = Mock() cache(function)() cache(function)() function.assert_called_once() dacite-1.9.2-fix/tests/test_dataclasses.py000066400000000000000000000030041475062640500206100ustar00rootroot00000000000000from dataclasses import dataclass, fields, field from typing import Optional import pytest from dacite.dataclasses import get_default_value_for_field, DefaultValueNotFoundError, is_frozen def test_get_default_value_for_field_with_default_value(): @dataclass class X: i: int = 1 dataclass_field = fields(X)[0] value = get_default_value_for_field(field=dataclass_field, type_=dataclass_field.type) assert value == 1 def test_get_default_value_for_field_with_default_factory(): @dataclass class X: i: int = field(default_factory=lambda: 1) dataclass_field = fields(X)[0] value = get_default_value_for_field(field=dataclass_field, type_=dataclass_field.type) assert value == 1 def test_get_default_value_for_optional_field(): @dataclass class X: i: Optional[int] dataclass_field = fields(X)[0] value = get_default_value_for_field(field=dataclass_field, type_=dataclass_field.type) assert value is None def test_get_default_value_for_field_without_default_value(): @dataclass class X: i: int dataclass_field = fields(X)[0] with pytest.raises(DefaultValueNotFoundError): get_default_value_for_field(field=dataclass_field, type_=dataclass_field.type) def test_is_frozen_with_frozen_dataclass(): @dataclass(frozen=True) class X: pass assert is_frozen(X) def test_is_frozen_with_non_frozen_dataclass(): @dataclass(frozen=False) class X: pass assert not is_frozen(X) dacite-1.9.2-fix/tests/test_types.py000066400000000000000000000237611475062640500175010ustar00rootroot00000000000000from dataclasses import InitVar from sys import version_info from typing import Optional, Union, List, Any, Dict, NewType, TypeVar, Generic, Collection, Tuple, Type from unittest.mock import patch, Mock import pytest from dacite.types import ( is_optional, extract_optional, is_generic, is_union, is_generic_collection, extract_origin_collection, is_instance, extract_generic, is_new_type, extract_new_type, is_literal, is_init_var, extract_init_var, is_type_generic, is_tuple, ) from tests.common import ( literal_support, init_var_type_support, pep_604_support, type_hints_with_generic_collections_support, ) def test_is_union_with_union(): assert is_union(Union[int, float]) def test_is_union_with_non_union(): assert not is_union(int) def test_is_union_with_import_error(): with patch("builtins.__import__") as mock_import: mock_import.side_effect = ImportError() assert not is_union(str) def test_is_tuple_with_tuple(): assert is_tuple(Tuple[int, float, str]) def test_is_tuple_with_variable_length_tuple(): assert is_tuple(Tuple[int, ...]) def test_is_tuple_with_not_parametrized_tuple(): assert is_tuple(Tuple) def test_is_tuple_with_tuple_class_object(): assert is_tuple(tuple) @type_hints_with_generic_collections_support def test_is_tuple_with_tuple_generic(): assert is_tuple(tuple[int, float, str]) @type_hints_with_generic_collections_support def test_is_tuple_with_variable_length_tuple_generic(): assert is_tuple(tuple[int, ...]) def test_is_tuple_with_non_tuple(): assert not is_tuple(int) @pep_604_support def test_is_union_with_pep_604_union(): assert is_union(int | float) @literal_support def test_is_literal_with_literal(): from typing import Literal assert is_literal(Literal["A", "B"]) def test_is_literal_with_non_literal(): assert not is_literal(int) def test_is_literal_with_import_error(): with patch("builtins.__import__") as mock_import: mock_import.side_effect = ImportError() assert not is_literal(str) def test_is_init_var_with_init_var(): assert is_init_var(InitVar[int]) def test_is_init_var_with_non_init_var(): assert not is_init_var(int) def test_is_optional_with_optional(): assert is_optional(Optional[int]) def test_is_optional_with_non_optional(): assert not is_optional(int) def test_is_optional_with_optional_of_union(): assert is_optional(Optional[Union[int, float]]) @pep_604_support def test_is_optional_with_pep_604_union(): assert is_optional(int | float | None) @pep_604_support def test_is_optional_with_non_optional_pep_604_union(): assert not is_optional(int | float) def test_extract_optional(): assert extract_optional(Optional[int]) == int def test_extract_optional_with_wrong_type(): with pytest.raises(ValueError): extract_optional(List[None]) def test_extract_optional_with_optional_of_union(): assert extract_optional(Optional[Union[int, str]]) == Union[int, str] def test_is_generic_with_generic(): assert is_generic(Optional[int]) def test_is_generic_with_non_generic(): assert not is_generic(int) def test_is_generic_collection_with_generic_collection(): assert is_generic_collection(List[int]) def test_is_generic_collection_with_non_generic_collection(): assert not is_generic_collection(list) def test_is_generic_collection_with_union(): assert not is_generic_collection(Union[int, str]) def test_extract_generic_collection(): assert extract_origin_collection(List[int]) == list def test_is_new_type_with_new_type(): assert is_new_type(NewType("NewType", int)) def test_is_new_type_with_non_new_type(): assert not is_new_type(int) def test_extract_new_type(): assert extract_new_type(NewType("NewType", int)) == int def test_is_instance_with_built_in_type_and_matching_value_type(): assert is_instance(1, int) def test_is_instance_with_built_in_type_and_not_matching_value_type(): assert not is_instance("test", int) def test_is_instance_with_union_and_matching_value_type(): assert is_instance(1, Union[int, float]) def test_is_instance_with_union_and_not_matching_value_type(): assert not is_instance("test", Union[int, float]) def test_is_instance_with_union_and_matching_generic_collection(): assert is_instance(["test"], Union[int, List[str]]) def test_is_instance_with_union_and_not_matching_generic_collection(): assert not is_instance(["test"], Union[int, List[int]]) def test_is_instance_with_optional_and_matching_value_type(): assert is_instance(1, Optional[int]) def test_is_instance_with_optional_and_not_matching_value_type(): assert not is_instance(1, Optional[str]) def test_is_instance_with_generic_collection_and_matching_value_type(): assert is_instance([1], List[int]) def test_is_instance_with_generic_collection_and_not_matching_item_type(): assert not is_instance(["test"], List[int]) def test_is_instance_with_nested_generic_collection_and_matching_value_type(): assert is_instance([[1]], List[List[int]]) def test_is_instance_with_nested_generic_collection_and_not_matching_item_type(): assert not is_instance([["test"]], List[List[int]]) def test_is_instance_with_generic_collection_without_specified_inner_types_and_matching_value_type(): assert is_instance([1], List) def test_is_instance_with_generic_collection_without_specified_inner_types_and_not_matching_value_type(): assert not is_instance([1], Dict) def test_is_instance_with_generic_abstract_collection_and_matching_value_type(): assert is_instance([1], Collection[int]) def test_is_instance_with_generic_collection_and_not_matching_value_type(): assert not is_instance({1}, List[int]) def test_is_instance_with_any_type(): assert is_instance(1, Any) def test_is_instance_with_new_type_and_matching_value_type(): assert is_instance("test", NewType("MyStr", str)) def test_is_instance_with_new_type_and_not_matching_value_type(): assert not is_instance(1, NewType("MyStr", str)) @init_var_type_support def test_is_instance_with_init_var_and_matching_value_type(): assert is_instance(1, InitVar[int]) @init_var_type_support def test_is_instance_with_init_var_and_not_matching_value_type(): assert not is_instance(1, InitVar[str]) def test_is_instance_with_with_type_and_matching_value_type(): assert is_instance(str, Type[str]) def test_is_instance_with_with_type_and_not_matching_value_type(): assert not is_instance(1, Type[str]) def test_is_instance_with_not_supported_generic_types(): T = TypeVar("T") class X(Generic[T]): pass assert not is_instance(X[str](), X[str]) def test_is_instance_with_generic_mapping_and_matching_value_type(): assert is_instance({"test": 1}, Dict[str, int]) def test_is_instance_with_generic_mapping_and_not_matching_mapping_key_type(): assert not is_instance({1: 1}, Dict[str, int]) def test_is_instance_with_generic_mapping_and_not_matching_mapping_value_type(): assert not is_instance({"test": "test"}, Dict[str, int]) def test_is_instance_with_numeric_tower(): assert is_instance(1, float) def test_is_instance_with_numeric_tower_and_optional(): assert is_instance(1, Optional[float]) def test_is_instance_with_numeric_tower_and_new_type(): assert is_instance(1, NewType("NewType", float)) @literal_support def test_is_instance_with_literal_and_matching_type(): from typing import Literal assert is_instance("A", Literal["A", "B"]) @literal_support def test_is_instance_with_literal_and_not_matching_type(): from typing import Literal assert not is_instance("C", Literal["A", "B"]) @literal_support def test_is_instance_with_optional_literal_and_matching_type(): from typing import Literal assert is_instance("A", Optional[Literal["A", "B"]]) @literal_support def test_is_instance_with_optional_literal_and_not_matching_type(): from typing import Literal assert not is_instance("C", Optional[Literal["A", "B"]]) @literal_support def test_is_instance_with_optional_literal_and_none(): from typing import Literal assert is_instance(None, Optional[Literal["A", "B"]]) def test_is_instance_with_tuple_and_matching_type(): assert is_instance((1, "test"), Tuple[int, str]) def test_is_instance_with_tuple_and_not_matching_type(): assert not is_instance((1, 2), Tuple[int, str]) def test_is_instance_with_tuple_and_wrong_length(): assert not is_instance((1, "test", 2), Tuple[int, str]) def test_is_instance_with_variable_length_tuple_and_matching_type(): assert is_instance((1, 2, 3), Tuple[int, ...]) def test_is_instance_with_variable_length_tuple_and_not_matching_type(): assert not is_instance((1, 2, "test"), Tuple[int, ...]) def test_is_instance_with_empty_tuple_and_matching_type(): assert is_instance((), Tuple[()]) def test_is_instance_with_empty_tuple_and_not_matching_type(): assert not is_instance((1, 2), Tuple[()]) def test_extract_generic(): assert extract_generic(List[int]) == (int,) def test_extract_generic_with_defaults(): assert extract_generic(List, defaults=(Any,)) == (Any,) @init_var_type_support def test_extract_init_var(): assert extract_init_var(InitVar[int]) == int def test_extract_init_var_with_attribute_error(): class FakeType: pass assert extract_init_var(FakeType) == Any def test_is_type_generic_with_matching_value(): assert is_type_generic(Type[int]) def test_is_type_generic_with_not_matching_value(): assert not is_type_generic(int) def test_extract_generic_special(): defaults = 1, 2 class FakeType: _special = True assert extract_generic(FakeType, defaults) == defaults @pytest.mark.skipif(version_info < (3, 10), reason="writing union types as X | Y requires Python 3.10") def test_optional_and_union_none_does_not_pollute_scope_via_caching(): is_generic(Optional[str]) @pep_604_support def test_optional_and_union_none_does_not_pollute_scope_via_caching_pep_604(): is_generic_collection(str | None)