My working unpac space for OCaml projects in development
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge git/patches/dag-cbor

+5051
+4
vendor/git/dag-cbor/.gitattributes
··· 1 + docs/* linguist-documentation 2 + htmlcov/* linguist-vendored 3 + test/* linguist-vendored 4 + *.bat -linguist-detectable
+41
vendor/git/dag-cbor/.github/workflows/python-pytest.yml
··· 1 + # This workflow will install Python dependencies, run tests and lint with a variety of Python versions 2 + # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions 3 + 4 + name: Python package 5 + 6 + on: 7 + push: 8 + branches: [ main ] 9 + pull_request: 10 + branches: [ main ] 11 + 12 + jobs: 13 + build: 14 + 15 + runs-on: ubuntu-latest 16 + strategy: 17 + fail-fast: false 18 + matrix: 19 + python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"] 20 + 21 + steps: 22 + - uses: actions/checkout@v2 23 + - name: Set up Python ${{ matrix.python-version }} 24 + uses: actions/setup-python@v2 25 + with: 26 + python-version: ${{ matrix.python-version }} 27 + - name: Install dependencies 28 + run: | 29 + python -m pip install --upgrade pip 30 + python -m pip install mypy pylint pytest cbor2==5.4.1 31 + if [ -f requirements.txt ]; then pip install -r requirements.txt; fi 32 + - name: Test with pytest 33 + run: | 34 + pytest 35 + - name: Lint with mypy 36 + run: | 37 + mypy --strict dag_cbor 38 + - name: Lint with pylint 39 + run: | 40 + pylint --errors-only --rcfile=.pylintrc dag_cbor 41 + pylint --exit-zero --rcfile=.pylintrc --disable=fixme dag_cbor
+132
vendor/git/dag-cbor/.gitignore
··· 1 + # Byte-compiled / optimized / DLL files 2 + __pycache__/ 3 + *.py[cod] 4 + *$py.class 5 + 6 + # C extensions 7 + *.so 8 + 9 + # Distribution / packaging 10 + .Python 11 + build/ 12 + develop-eggs/ 13 + dist/ 14 + downloads/ 15 + eggs/ 16 + .eggs/ 17 + lib/ 18 + lib64/ 19 + parts/ 20 + sdist/ 21 + var/ 22 + wheels/ 23 + pip-wheel-metadata/ 24 + share/python-wheels/ 25 + *.egg-info/ 26 + .installed.cfg 27 + *.egg 28 + MANIFEST 29 + 30 + # PyInstaller 31 + # Usually these files are written by a python script from a template 32 + # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 + *.manifest 34 + *.spec 35 + 36 + # Installer logs 37 + pip-log.txt 38 + pip-delete-this-directory.txt 39 + 40 + # Unit test / coverage reports 41 + htmlcov/ 42 + .tox/ 43 + .nox/ 44 + .coverage 45 + .coverage.* 46 + .cache 47 + nosetests.xml 48 + coverage.xml 49 + *.cover 50 + *.py,cover 51 + .hypothesis/ 52 + .pytest_cache/ 53 + 54 + # Translations 55 + *.mo 56 + *.pot 57 + 58 + # Django stuff: 59 + *.log 60 + local_settings.py 61 + db.sqlite3 62 + db.sqlite3-journal 63 + 64 + # Flask stuff: 65 + instance/ 66 + .webassets-cache 67 + 68 + # Scrapy stuff: 69 + .scrapy 70 + 71 + # Sphinx documentation 72 + docs/_build/ 73 + 74 + # PyBuilder 75 + target/ 76 + 77 + # Jupyter Notebook 78 + .ipynb_checkpoints 79 + 80 + # IPython 81 + profile_default/ 82 + ipython_config.py 83 + 84 + # pyenv 85 + .python-version 86 + 87 + # pipenv 88 + # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 + # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 + # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 + # install all needed dependencies. 92 + #Pipfile.lock 93 + 94 + # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 + __pypackages__/ 96 + 97 + # Celery stuff 98 + celerybeat-schedule 99 + celerybeat.pid 100 + 101 + # SageMath parsed files 102 + *.sage.py 103 + 104 + # Environments 105 + .env 106 + .venv 107 + env/ 108 + venv/ 109 + ENV/ 110 + env.bak/ 111 + venv.bak/ 112 + 113 + # Spyder project settings 114 + .spyderproject 115 + .spyproject 116 + 117 + # Rope project settings 118 + .ropeproject 119 + 120 + # mkdocs documentation 121 + /site 122 + 123 + # mypy 124 + .mypy_cache/ 125 + .dmypy.json 126 + dmypy.json 127 + 128 + # Pyre type checker 129 + .pyre/ 130 + 131 + # local README proof from readme_renderer 132 + README-PROOF.html
+13
vendor/git/dag-cbor/.pylintrc
··· 1 + [MESSAGES CONTROL] 2 + disable = too-many-ancestors, 3 + too-few-public-methods, 4 + invalid-name, 5 + unused-argument, 6 + redefined-outer-name, 7 + useless-super-delegation, 8 + isinstance-second-argument-not-valid-type, 9 + too-many-lines, 10 + protected-access, 11 + useless-import-alias 12 + [FORMAT] 13 + max-line-length=160
+64
vendor/git/dag-cbor/.readthedocs.yaml
··· 1 + # Read the Docs configuration file for Sphinx projects 2 + 3 + # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 4 + 5 + 6 + # Required 7 + 8 + version: 2 9 + 10 + 11 + # Set the OS, Python version and other tools you might need 12 + 13 + build: 14 + 15 + os: ubuntu-22.04 16 + 17 + tools: 18 + 19 + python: "3.12" 20 + 21 + # You can also specify other tool versions: 22 + 23 + # nodejs: "20" 24 + 25 + # rust: "1.70" 26 + 27 + # golang: "1.20" 28 + 29 + 30 + # Build documentation in the "docs/" directory with Sphinx 31 + 32 + sphinx: 33 + 34 + configuration: docs/conf.py 35 + 36 + # You can configure Sphinx to use a different builder, for instance use the dirhtml builder for simpler URLs 37 + 38 + # builder: "dirhtml" 39 + 40 + # Fail on all warnings to avoid broken references 41 + 42 + # fail_on_warning: true 43 + 44 + 45 + # Optionally build your docs in additional formats such as PDF and ePub 46 + 47 + # formats: 48 + 49 + # - pdf 50 + 51 + # - epub 52 + 53 + 54 + # Optional but recommended, declare the Python requirements required 55 + 56 + # to build your documentation 57 + 58 + # See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html 59 + 60 + python: 61 + 62 + install: 63 + 64 + - requirements: docs/requirements.txt
+4
vendor/git/dag-cbor/.vscode/settings.json
··· 1 + { 2 + "esbonio.sphinx.confDir": "${workspaceFolder}\\docs", 3 + "workbench.colorTheme": "Default Dark+" 4 + }
+128
vendor/git/dag-cbor/CODE_OF_CONDUCT.md
··· 1 + # Contributor Covenant Code of Conduct 2 + 3 + ## Our Pledge 4 + 5 + We as members, contributors, and leaders pledge to make participation in our 6 + community a harassment-free experience for everyone, regardless of age, body 7 + size, visible or invisible disability, ethnicity, sex characteristics, gender 8 + identity and expression, level of experience, education, socio-economic status, 9 + nationality, personal appearance, race, religion, or sexual identity 10 + and orientation. 11 + 12 + We pledge to act and interact in ways that contribute to an open, welcoming, 13 + diverse, inclusive, and healthy community. 14 + 15 + ## Our Standards 16 + 17 + Examples of behavior that contributes to a positive environment for our 18 + community include: 19 + 20 + * Demonstrating empathy and kindness toward other people 21 + * Being respectful of differing opinions, viewpoints, and experiences 22 + * Giving and gracefully accepting constructive feedback 23 + * Accepting responsibility and apologizing to those affected by our mistakes, 24 + and learning from the experience 25 + * Focusing on what is best not just for us as individuals, but for the 26 + overall community 27 + 28 + Examples of unacceptable behavior include: 29 + 30 + * The use of sexualized language or imagery, and sexual attention or 31 + advances of any kind 32 + * Trolling, insulting or derogatory comments, and personal or political attacks 33 + * Public or private harassment 34 + * Publishing others' private information, such as a physical or email 35 + address, without their explicit permission 36 + * Other conduct which could reasonably be considered inappropriate in a 37 + professional setting 38 + 39 + ## Enforcement Responsibilities 40 + 41 + Community leaders are responsible for clarifying and enforcing our standards of 42 + acceptable behavior and will take appropriate and fair corrective action in 43 + response to any behavior that they deem inappropriate, threatening, offensive, 44 + or harmful. 45 + 46 + Community leaders have the right and responsibility to remove, edit, or reject 47 + comments, commits, code, wiki edits, issues, and other contributions that are 48 + not aligned to this Code of Conduct, and will communicate reasons for moderation 49 + decisions when appropriate. 50 + 51 + ## Scope 52 + 53 + This Code of Conduct applies within all community spaces, and also applies when 54 + an individual is officially representing the community in public spaces. 55 + Examples of representing our community include using an official e-mail address, 56 + posting via an official social media account, or acting as an appointed 57 + representative at an online or offline event. 58 + 59 + ## Enforcement 60 + 61 + Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 + reported to the community leaders responsible for enforcement at 63 + community@hashberg.io. 64 + All complaints will be reviewed and investigated promptly and fairly. 65 + 66 + All community leaders are obligated to respect the privacy and security of the 67 + reporter of any incident. 68 + 69 + ## Enforcement Guidelines 70 + 71 + Community leaders will follow these Community Impact Guidelines in determining 72 + the consequences for any action they deem in violation of this Code of Conduct: 73 + 74 + ### 1. Correction 75 + 76 + **Community Impact**: Use of inappropriate language or other behavior deemed 77 + unprofessional or unwelcome in the community. 78 + 79 + **Consequence**: A private, written warning from community leaders, providing 80 + clarity around the nature of the violation and an explanation of why the 81 + behavior was inappropriate. A public apology may be requested. 82 + 83 + ### 2. Warning 84 + 85 + **Community Impact**: A violation through a single incident or series 86 + of actions. 87 + 88 + **Consequence**: A warning with consequences for continued behavior. No 89 + interaction with the people involved, including unsolicited interaction with 90 + those enforcing the Code of Conduct, for a specified period of time. This 91 + includes avoiding interactions in community spaces as well as external channels 92 + like social media. Violating these terms may lead to a temporary or 93 + permanent ban. 94 + 95 + ### 3. Temporary Ban 96 + 97 + **Community Impact**: A serious violation of community standards, including 98 + sustained inappropriate behavior. 99 + 100 + **Consequence**: A temporary ban from any sort of interaction or public 101 + communication with the community for a specified period of time. No public or 102 + private interaction with the people involved, including unsolicited interaction 103 + with those enforcing the Code of Conduct, is allowed during this period. 104 + Violating these terms may lead to a permanent ban. 105 + 106 + ### 4. Permanent Ban 107 + 108 + **Community Impact**: Demonstrating a pattern of violation of community 109 + standards, including sustained inappropriate behavior, harassment of an 110 + individual, or aggression toward or disparagement of classes of individuals. 111 + 112 + **Consequence**: A permanent ban from any sort of public interaction within 113 + the community. 114 + 115 + ## Attribution 116 + 117 + This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 + version 2.0, available at 119 + https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 + 121 + Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 + enforcement ladder](https://github.com/mozilla/diversity). 123 + 124 + [homepage]: https://www.contributor-covenant.org 125 + 126 + For answers to common questions about this code of conduct, see the FAQ at 127 + https://www.contributor-covenant.org/faq. Translations are available at 128 + https://www.contributor-covenant.org/translations.
+88
vendor/git/dag-cbor/CONTRIBUTING.md
··· 1 + 2 + # Contributing 3 + 4 + All contributions, big and small, are very appreciated! 5 + [File an issue](#file-an-issue) for bug reports, suggestions and questions, or [make a pull request](#make-a-pull-request) to actively contribute to the code or documentation. 6 + 7 + However you decide to help, please refer to our [code of conduct](CODE_OF_CONDUCT.md) for what we expect from our community. 8 + 9 + 10 + ## File an Issue 11 + 12 + Issues can be filed at https://github.com/hashberg-io/dag-cbor/issues. You can file an issue to: 13 + 14 + - report a bug (using the `bug` label) 15 + - suggest a new feature (using the `enhancement` label) 16 + - suggest improvements to our documentation (using the `documentation` label) 17 + - ask for information and start a discussion thread (using the `question` label) 18 + 19 + If you are reporting a bug, please include the following information: 20 + 21 + - project version (PyPI version number or commit number) 22 + - Python version 23 + - version of installed dependencies 24 + - how the bug manifests (e.g. what you expect to happen vs what actually happens) 25 + - how others can reproduce the bug 26 + 27 + Please try to be concise in your description, providing a minimal reproducible example whenever possible. 28 + 29 + If you're proposing a new feature, please describe it in detail, with a few examples of it might be implemented and of its intended usage. 30 + 31 + 32 + ## Make a Pull Request 33 + 34 + You can [make a pull request](https://docs.github.com/en/github/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests) to: 35 + 36 + - fix a bug which is reported and discussed in an open issue 37 + - implement a new feature which is suggested and discussed in an open issue 38 + - improve our documentation in ways suggested and discussed in an open issue 39 + 40 + You should [link your pull request to the issue(s)](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue) that it addresses. Once your pull request passes all continuous integration checks, we will review it and either: 41 + 42 + - approve the pull request and merge it 43 + - start a discussion on how to improve the pull request before it can be approved 44 + - reject the pull request, with an explanation as to why we don't consider it viable for improvement 45 + 46 + 47 + ### Continuous Integration 48 + 49 + You can perform continuous integration checks on all supported versions by running [tox](https://tox.readthedocs.io/en/latest/) in the main project folder: 50 + 51 + ``` 52 + tox 53 + ``` 54 + 55 + Continuous integration involves the following individual checks: 56 + 57 + 1. testing with [pytest](https://docs.pytest.org/): 58 + 59 + ``` 60 + pytest test 61 + ``` 62 + 63 + 2. static type-checking with [mypy](http://mypy-lang.org/): 64 + 65 + ``` 66 + mypy dag-cbor 67 + ``` 68 + 69 + 3. linting with [pylint](https://www.pylint.org/): 70 + 71 + ``` 72 + pylint dag-cbor 73 + ``` 74 + 75 + Whenever relevant, please consider contributing some additional tests pertaining to your implementation. 76 + 77 + 78 + ### Documentation 79 + 80 + The API documentation for this project is generated by [Sphinx](https://www.sphinx-doc.org/): please document any code changes and additions using [reST](https://www.sphinx-doc.org/en/master/usage/restructuredtext/index.html) docstrings. The documentation is generated by running the following commands in the [docs/](docs/) folder: 81 + 82 + ``` 83 + docs>make api 84 + docs>make clean 85 + docs>make html 86 + ``` 87 + 88 + The script `make-api-clean-html.bat` automates the procedure on Windows. If you edit the [readme page](README.rst), please conform to the [standard-readme](https://github.com/RichardLitt/standard-readme) specification.
+21
vendor/git/dag-cbor/LICENSE
··· 1 + MIT License 2 + 3 + Copyright (c) 2021 Hashberg Ltd 4 + 5 + Permission is hereby granted, free of charge, to any person obtaining a copy 6 + of this software and associated documentation files (the "Software"), to deal 7 + in the Software without restriction, including without limitation the rights 8 + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 + copies of the Software, and to permit persons to whom the Software is 10 + furnished to do so, subject to the following conditions: 11 + 12 + The above copyright notice and this permission notice shall be included in all 13 + copies or substantial portions of the Software. 14 + 15 + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 + SOFTWARE.
+110
vendor/git/dag-cbor/README.rst
··· 1 + dag-cbor: A Python implementation of the `DAG-CBOR codec <https://ipld.io/specs/codecs/dag-cbor/spec/>`_ 2 + ======================================================================================================== 3 + 4 + .. image:: https://img.shields.io/badge/python-3.7+-green.svg 5 + :target: https://docs.python.org/3.7/ 6 + :alt: Python versions 7 + 8 + .. image:: https://img.shields.io/pypi/v/dag-cbor.svg 9 + :target: https://pypi.python.org/pypi/dag-cbor/ 10 + :alt: PyPI version 11 + 12 + .. image:: https://img.shields.io/pypi/status/dag-cbor.svg 13 + :target: https://pypi.python.org/pypi/dag-cbor/ 14 + :alt: PyPI status 15 + 16 + .. image:: http://www.mypy-lang.org/static/mypy_badge.svg 17 + :target: https://github.com/python/mypy 18 + :alt: Checked with Mypy 19 + 20 + .. image:: https://readthedocs.org/projects/dag-cbor/badge/?version=latest 21 + :target: https://dag-cbor.readthedocs.io/en/latest/?badge=latest 22 + :alt: Documentation Status 23 + 24 + .. image:: https://github.com/hashberg-io/dag-cbor/actions/workflows/python-pytest.yml/badge.svg 25 + :target: https://github.com/hashberg-io/dag-cbor/actions/workflows/python-pytest.yml 26 + :alt: Python package status 27 + 28 + .. image:: https://img.shields.io/badge/readme%20style-standard-brightgreen.svg?style=flat-square 29 + :target: https://github.com/RichardLitt/standard-readme 30 + :alt: standard-readme compliant 31 + 32 + 33 + This is a fully compliant Python implementation of the `DAG-CBOR codec <https://ipld.io/specs/codecs/dag-cbor/spec/>`_, a subset of the `Concise Binary Object Representation (CBOR) <https://cbor.io/>`_ supporting the `IPLD Data Model <https://ipld.io/docs/data-model/>`_ and enforcing a unique (strict) encoded representation of items. 34 + 35 + 36 + .. contents:: 37 + 38 + 39 + Install 40 + ------- 41 + 42 + You can install the latest release from `PyPI <https://pypi.org/project/dag-cbor/>`_ as follows: 43 + 44 + .. code-block:: console 45 + 46 + $ pip install --upgrade dag-cbor 47 + 48 + 49 + Usage 50 + ----- 51 + 52 + We suggest you import DAG-CBOR as follows: 53 + 54 + >>> import dag_cbor 55 + 56 + Below are some basic usage examples, to get you started: for detailed documentation, see https://dag-cbor.readthedocs.io/ 57 + 58 + 59 + Encoding and decoding 60 + ^^^^^^^^^^^^^^^^^^^^^ 61 + 62 + >>> dag_cbor.encode({'a': 12, 'b': 'hello!'}) 63 + b'\xa2aa\x0cabfhello!' 64 + >>> dag_cbor.decode(b'\xa2aa\x0cabfhello!') 65 + {'a': 12, 'b': 'hello!'} 66 + 67 + 68 + Random DAG-CBOR data 69 + ^^^^^^^^^^^^^^^^^^^^ 70 + 71 + >>> import pprint # pretty-printing 72 + >>> custom_opts = dict(min_codepoint=0x41, max_codepoint=0x5a, include_cid=False) 73 + >>> with dag_cbor.random.options(**custom_opts): 74 + ... for d in dag_cbor.random.rand_dict(3): 75 + ... pprint.pp(d) 76 + ... 77 + {'BIQPMZ': b'\x85\x1f\x07/\xcc\x00\xfc\xaa', 78 + 'EJEYDTZI': {}, 79 + 'PLSG': {'G': 'JFG', 80 + 'HZE': -61.278, 81 + 'JWDRKRGZ': b'-', 82 + 'OCCKQPDJ': True, 83 + 'SJOCTZMK': False}, 84 + 'PRDLN': 39.129, 85 + 'TUGRP': None, 86 + 'WZTEJDXC': -69.933} 87 + {'GHAXI': 39.12, 88 + 'PVUWZLC': 4.523, 89 + 'TDPSU': 'TVCADUGT', 90 + 'ZHGVSNSI': [-57, 9, -78.312]} 91 + {'': 11, 'B': True, 'FWD': {}, 'GXZBVAR': 'BTDWMGI', 'TDICHC': 87} 92 + 93 + 94 + 95 + API 96 + --- 97 + 98 + For the full API documentation, see https://dag-cbor.readthedocs.io/ 99 + 100 + 101 + Contributing 102 + ------------ 103 + 104 + Please see `<CONTRIBUTING.md>`_. 105 + 106 + 107 + License 108 + ------- 109 + 110 + `MIT © Hashberg Ltd. <LICENSE>`_
+28
vendor/git/dag-cbor/SECURITY.md
··· 1 + # Security Policy 2 + 3 + We take security of this package very seriously. 4 + 5 + ## Supported Versions 6 + 7 + We will issue security updates for [PyPI releases](https://pypi.org/project/dag-cbor/) with the latest minor version number (regardless of micro version), by releasing a new minor version. 8 + 9 + If you find a vulnerability which is not in any of the PyPI releases with the latest minor version, you should instead report it as a bug by [filing an issue](https://github.com/hashberg-io/dag-cbor/issues). 10 + 11 + ## Reporting a Vulnerability 12 + 13 + To report a vulnerability, please send an email to security@hashberg.io with the following information: 14 + 15 + - how we can contact you privately 16 + - how you wish to be publicly identified for the purpose of credit when we disclose the vulnerability 17 + - which package releases are affected 18 + - the Python version (including OS, if relevant) and the versions of all dependencies that you used when confirming the vulnerability 19 + - detailed description of the vulnerability, including how we can reproduce it 20 + 21 + We will come back to you within 24 hours to acknowledge your report and we will provide a detailed response within 48 hours, including an initial assessment of how we intend to address the vulnerability you disclosed. If the fix requires a prolonged amount of time (> 1 week), we will send you weekly updates on our progress. 22 + 23 + ## Disclosure Process 24 + 25 + 1. Upon initial acknowledgment, we will assign a Unique ID `UID` to your security report, which we will reference in all our communications using the header `[security report #UID]`. 26 + 2. Fixes are prepared and held locally in a new branch, without pushing to the public repository. 27 + 3. When all fixes are ready to be pushed, an issue announcing the existence of a vulnerability is opened on GitHub: this includes package versions affected, security report UID and embargo date (typically 72 hours from the issue being opened), but no further information. 28 + 4. On the embargo date, the fix branch is pushed and merged into the main branch, closing the issue, and a new minor version is released on both PyPI and GitHub. The release notes on GitHub provide a detailed description of the vulnerability, including credit to the initial discloser(s), as well as a summary of how the vulnerability was patched.
+132
vendor/git/dag-cbor/_check_decode_error_messages.py
··· 1 + r""" 2 + Prints error messages for a variety of decoding failures, to check that the new detailed error messages look all right. 3 + """ 4 + # pylint: disable = all 5 + 6 + from typing import List 7 + from multiformats import varint 8 + from dag_cbor.random import rand_data 9 + from dag_cbor import encode, decode 10 + from dag_cbor.ipld import Kind 11 + from dag_cbor.encoding import CBOREncodingError 12 + from dag_cbor.decoding import CBORDecodingError 13 + 14 + import random 15 + 16 + random.seed(0) 17 + 18 + test_cases = [ 19 + # err._required_multicodec 20 + "00", 21 + "81e20301", 22 + # err._multiple_top_level_items 23 + "718301020301", 24 + # err._invalid_float 25 + "71fb7ff8000000000000", 26 + "71fb7ff0000000000000", 27 + "71fbfff0000000000000", 28 + # err._unexpected_eof 29 + "71", 30 + "71830102", 31 + "71fb3fb99999", 32 + "7146"+"7891bc", 33 + "7166"+b"hello".hex(), 34 + "71a1"+("66"+b"hello".hex()), 35 + # err._invalid_additional_info 36 + "715c", 37 + "71f9", 38 + # err._excessive_int_size 39 + "7119"+f"{156:0>4x}", 40 + "711a"+f"{156:0>8x}", 41 + "711a"+f"{32033:0>8x}", 42 + "711b"+f"{156:0>16x}", 43 + "711b"+f"{32033:0>16x}", 44 + "711b"+f"{2305067290:0>16x}", 45 + # err._unicode 46 + "7161"+b"\xe9".hex(), 47 + "7162"+b"\xe9\x80".hex(), 48 + "7162"+b"A\xe9".hex(), 49 + "7163"+b"AB\xe9".hex(), 50 + "7162"+b"\xe9Z".hex(), 51 + "7163"+b"\xe9YZ".hex(), 52 + "7164"+b"A\xe9YZ".hex(), 53 + "7165"+b"AB\xe9YZ".hex(), 54 + "7165"+b"AB\xe9\x80YZ".hex(), 55 + "7169"+b"ABCD\xe9\x80WXYZ".hex(), 56 + "7171"+b"ABCDEFGHIJKLMNO\xe9\x80".hex(), 57 + "71a1"+("63"+b"A\xe9Z".hex())+"01", 58 + # err._list_item 59 + "718401"+("1a"+f"{32033:0>8x}")+"0304", 60 + "718401"+("65"+b"A\xe9YZ".hex())+"0304", 61 + # err._dict_key_type 62 + "71a10101", 63 + "71a18301020301", 64 + # err._dict_item for a value 65 + "71a2"+("65"+b"hello".hex())+"01"+("63"+b"bye".hex())+"fb7ff0000000000000", 66 + # err._duplicate_dict_key for a value 67 + "71a3"+("65"+b"hello".hex())+"01"+("63"+b"bye".hex())+"02"+("65"+b"hello".hex())+"03", 68 + # err._dict_key_order 69 + "71a3"+("65"+b"hello".hex())+"01"+("66"+b"whatup".hex())+"02"+("63"+b"bye".hex())+"03", 70 + # err._invalid_tag 71 + "71d829"+"46"+"7891bc", 72 + # err._cid 73 + "71d82a"+"46"+"7891bc", 74 + # err._cid_bytes 75 + "71d82a"+"65"+b"hello".hex(), 76 + # err._cid_multibase 77 + "71d82a"+"450101030405", 78 + # err._simple_value 79 + "71f3" 80 + ] 81 + 82 + def create_embedding_obj(tag: str) -> Kind: 83 + for obj in rand_data(max_nesting=4): 84 + if not isinstance(obj, dict): 85 + continue 86 + if len(obj) < 4: 87 + continue 88 + list_values = [v for v in obj.values() if isinstance(v, list) and len(v) > 4] 89 + if not list_values: 90 + continue 91 + l = random.choice(list_values) 92 + l[random.randrange(0, len(l))] = tag 93 + return obj 94 + return tag 95 + 96 + def deep_embed(test_case: str) -> str: 97 + tag = "0xdeadbeef" 98 + obj = create_embedding_obj(tag) 99 + obj_bytes = encode(obj).hex() 100 + tag_bytes = encode(tag).hex() 101 + return "71"+obj_bytes.replace(tag_bytes, test_case[2:]) 102 + 103 + deep_test_cases = [ 104 + deep_embed(random.choice(test_cases)) 105 + for _ in range(10) 106 + ] 107 + def print_decode_error(test_case: str) -> bool: 108 + encoded_bytes = bytes.fromhex(test_case) 109 + encoded_bytes_str = encoded_bytes.hex() if encoded_bytes else "<NO BYTES>" 110 + print(f"> Error raised by decoding test case {idx: >2}:\n{encoded_bytes_str}") 111 + print() 112 + try: 113 + decode(encoded_bytes, require_multicodec=True) 114 + except CBORDecodingError as e: 115 + print(e) 116 + cause = e.__cause__ 117 + while cause is not None: 118 + print(cause) 119 + cause = cause.__cause__ 120 + print() 121 + return True 122 + return False 123 + 124 + if __name__ == "__main__": 125 + print("==== Shallow test cases ====") 126 + print() 127 + for idx, test_case in enumerate(test_cases): 128 + assert print_decode_error(test_case), f"Decoding of test case {idx} should have raised error." 129 + print("==== Deep test cases ====") 130 + print() 131 + for idx, test_case in enumerate(deep_test_cases): 132 + assert print_decode_error(test_case), f"Decoding of deep test case {idx} should have raised error."
+368
vendor/git/dag-cbor/autodoc_typehints.py
··· 1 + r""" 2 + Autodoc extension dealing with local type references and function signatures. 3 + """ 4 + 5 + from __future__ import annotations 6 + 7 + from collections.abc import Callable, Mapping 8 + from dataclasses import dataclass 9 + import inspect 10 + import re 11 + from types import FunctionType, ModuleType 12 + from typing import Any, Optional, Union 13 + from typing_extensions import Literal 14 + from sphinx.application import Sphinx 15 + 16 + @dataclass(frozen=True) 17 + class ParsedType: 18 + r""" Dataclass for a parsed type. """ 19 + name: str 20 + args: Union[None, str, tuple[ParsedType, ...]] = None 21 + variadic: bool = False 22 + 23 + def crossref(self, globalns: Optional[Mapping[str, Any]] = None) -> str: 24 + r""" Generates Sphinx cross-reference link for the given type, using local names. """ 25 + # pylint: disable = eval-used 26 + if globalns is None: 27 + globalns = {} 28 + name, args, variadic = self.name, self.args, self.variadic 29 + role = "obj" 30 + if name in globalns: 31 + obj = globalns[name] 32 + if isinstance(obj, ModuleType): 33 + role = "mod" 34 + elif isinstance(obj, property): 35 + role = "attr" 36 + elif isinstance(obj, type): 37 + role = "class" 38 + elif isinstance(obj, FunctionType): 39 + role = "func" 40 + name_crossref = f":{role}:`{name}`" 41 + if args is None: 42 + return name_crossref 43 + if isinstance(args, str): 44 + _args = eval(f"({args}, )") 45 + arg_crossrefs = ", ".join(f"``{repr(arg)}``" for arg in _args) 46 + else: 47 + arg_crossrefs = ", ".join((arg.crossref(globalns) for arg in args)) 48 + if variadic: 49 + arg_crossrefs += ", ..." 50 + return fr"{name_crossref}\ [{arg_crossrefs}]" 51 + 52 + def _find_closing_idx(s: str, c_open: str, c_close: str, idx_open: int = 0) -> int: 53 + r""" Finds the index where a bracketed/quoted range ends. """ 54 + assert len(c_open) == 1, c_open 55 + assert len(c_close) == 1, c_close 56 + assert idx_open < len(s), (idx_open, len(s)) 57 + assert s[idx_open] == c_open, (idx_open, s[idx_open]) 58 + lvl = 1 59 + idx_close: Optional[int] = None 60 + for idx in range(idx_open+1, len(s)): 61 + if s[idx] == c_close: 62 + lvl -= 1 63 + elif s[idx] == c_open: 64 + lvl += 1 65 + if lvl == 0: 66 + idx_close = idx 67 + break 68 + if idx_close is None: 69 + error_msg = f"Unbalanced opening symbol found while searching for first outermost {c_open}...{c_close} in {repr(s)}." 70 + raise ValueError(error_msg) 71 + return idx_close 72 + 73 + def _parse_type(annotation: str) -> tuple[Union[ParsedType, Literal["..."]], str]: 74 + r""" Parses an annotation into the first type appearing in it, together with the unparsed remainder of the annotation. """ 75 + quote_close_idx = None 76 + if annotation.startswith("'"): 77 + quote_close_idx = _find_closing_idx(annotation, "'", "'", 0) 78 + elif annotation.startswith('"'): 79 + quote_close_idx = _find_closing_idx(annotation, '"', '"', 0) 80 + if quote_close_idx is not None: 81 + if quote_close_idx == 1: 82 + raise ValueError(f"Cannot parse empty forward reference at start of annotation: annotation = {annotation}") 83 + annotation = annotation[1:quote_close_idx]+annotation[quote_close_idx+1:] 84 + try: 85 + b_open: Optional[int] = annotation.index("[") 86 + except ValueError: 87 + b_open = None 88 + try: 89 + c_idx = annotation.index(",") 90 + except ValueError: 91 + c_idx = None 92 + if b_open is not None and (c_idx is None or b_open < c_idx): 93 + b_close = _find_closing_idx(annotation, "[", "]", b_open) 94 + name = annotation[:b_open] 95 + args_str = annotation[b_open+1:b_close] 96 + res = annotation[b_close+1:] 97 + assert name, (name, args_str, res) 98 + if name.split(".")[-1] == "Literal": 99 + return ParsedType(name, args_str), res 100 + args: list[ParsedType] = [] 101 + variadic = False 102 + while args_str: 103 + arg, _args_str = _parse_type(args_str) 104 + if isinstance(arg, ParsedType): 105 + args.append(arg) 106 + else: 107 + assert arg == "...", arg 108 + variadic = True 109 + if _args_str: 110 + raise ValueError(f"Ellipsis argument encountered in parametric type, but not at the end of args lis: annotation = {annotation}, args_str = {args_str}") 111 + if not _args_str: 112 + break 113 + if not _args_str.startswith(", "): 114 + raise ValueError(f"Multiple type parameters must be separated by ', ': annotation = {annotation}, args_str = {args_str}, arg = {arg} _args_str = {_args_str}") 115 + args_str = _args_str[2:] 116 + return ParsedType(name, tuple(args), variadic), res 117 + if c_idx is not None: 118 + name = annotation[:c_idx] 119 + res = annotation[c_idx:] 120 + if name == "...": 121 + raise ValueError(f"Found ellipsis followed by comma: annotation = {annotation}") 122 + return ParsedType(name), res 123 + if "]" in annotation: 124 + raise ValueError(f"Encountered closing bracket ']' without any opening bracket: annotation = {annotation} ") 125 + name = annotation 126 + if name == "...": 127 + return "...", "" 128 + return ParsedType(name), "" 129 + 130 + def parse_type(annotation: str) -> ParsedType: 131 + r""" Parses an annotation into a type. """ 132 + parsed_type, residual_string = _parse_type(annotation) 133 + if residual_string: 134 + raise ValueError(f"Annotation was not entirely consumed by parsing: annotation = {annotation}, parsed_type = {parsed_type}, residual_string = {residual_string}") 135 + if not isinstance(parsed_type, ParsedType): 136 + raise ValueError(f"Cannot parse ellipsis on its own: annotation = {annotation}") 137 + return parsed_type 138 + 139 + def sigdoc(fun: FunctionType, lines: list[str]) -> None: 140 + r""" 141 + Returns doclines documenting the parameter and return type of the given function 142 + """ 143 + # pylint: disable = too-many-branches 144 + doc = "\n".join(lines) 145 + lines.append("") 146 + # FIXME: if an :rtype: line already exists, remove it here and re-append it after all param type lines. 147 + globalns = fun.__globals__ 148 + sig = inspect.signature(fun) 149 + for p in sig.parameters.values(): 150 + annotation = p.annotation 151 + if annotation == p.empty: 152 + continue 153 + if not isinstance(annotation, str): 154 + print(f"WARNING! Found non-string annotation: {repr(annotation)}. Did you forget to import annotation from __future__?.") 155 + annotation = str(annotation) 156 + t = parse_type(annotation) 157 + tx = t.crossref(globalns) 158 + default = p.default if p.default != p.empty else None 159 + is_args = p.kind == p.VAR_POSITIONAL 160 + is_kwargs = p.kind == p.VAR_KEYWORD 161 + if is_args: 162 + extra_info = "variadic positional" 163 + elif is_kwargs: 164 + extra_info = "variadic keyword" 165 + elif default is not None: 166 + extra_info = f"default = ``{default}``" 167 + else: 168 + extra_info = None 169 + if extra_info is None: 170 + line = f":type {p.name}: {tx}" 171 + else: 172 + line = f":type {p.name}: {tx}; {extra_info}" 173 + if f":param {p.name}:" not in doc: 174 + lines.append(f":param {p.name}:") 175 + if f":type {p.name}:" not in doc: 176 + lines.append(line) 177 + if sig.return_annotation == sig.empty: 178 + return 179 + t = parse_type(sig.return_annotation) 180 + tx = t.crossref(globalns) 181 + line = f":rtype: {tx}" 182 + if ":rtype:" not in doc: 183 + lines.append(line) 184 + 185 + def sigdoc_handler(app: Sphinx, what: str, fullname: str, obj: Any, options: Any, lines: list[str]) -> None: 186 + r""" 187 + Handler for Sphinx Autodoc's event 188 + `autodoc-process-docstring<https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html#event-autodoc-process-docstring>`_ 189 + which replaces cross-references specified in terms of module globals with their fully qualified version. 190 + """ 191 + # pylint: disable = too-many-arguments 192 + if what not in ("function", "method", "property"): 193 + return 194 + if what == "property": 195 + fun: FunctionType = obj.fget 196 + else: 197 + fun = obj 198 + sigdoc(fun, lines) 199 + 200 + def simple_crossref_pattern(name: str) -> re.Pattern[str]: 201 + r""" 202 + Pattern for simple imports: 203 + 204 + .. code-block :: python 205 + 206 + f":{role}:`{name}`" # e.g. ":class:`MyClass`" 207 + f":{role}:`~{name}`" # e.g. ":class:`~MyClass`" 208 + f":{role}:`{name}{tail}`" # e.g. ":attr:`MyClass.my_property.my_subproperty`" 209 + f":{role}:`~{name}{tail}`" # e.g. ":attr:`~MyClass.my_property.my_subproperty`" 210 + 211 + """ 212 + return re.compile(rf":([a-z]+):`(~)?{name}(\.[\.a-zA-Z0-9_]+)?`") 213 + 214 + def simple_crossref_repl(name: str, fullname: str) -> Callable[[re.Match[str]], str]: 215 + r""" 216 + Replacement function for the pattern generated by :func:`simple_crossref_pattern`: 217 + 218 + .. code-block :: python 219 + 220 + f":{role}:`~{fullname}`" # e.g. ":class:`~mymod.mysubmod.MyClass`" 221 + f":{role}:`~{fullname}`" # e.g. ":class:`~mymod.mysubmod.MyClass`" 222 + f":{role}:`{name}{tail}<{fullname}{tail}>`" # e.g. ":attr:`MyClass.my_property.my_subproperty<mymod.mysubmod.MyClass.my_property.my_subproperty>`" 223 + f":{role}:`~{fullname}{tail}`" # e.g. ":attr:`~mymod.mysubmod.MyClass.my_property.my_subproperty`" 224 + 225 + """ 226 + def repl(match: re.Match[str]) -> str: 227 + role = match[1] 228 + short = match[2] is not None 229 + tail = match[3] 230 + if tail is None: 231 + return f":{role}:`~{fullname}`" 232 + if short: 233 + return f":{role}:`~{fullname}{tail}`" 234 + return f":{role}:`{name}{tail}<{fullname}{tail}>`" 235 + return repl 236 + 237 + def labelled_crossref_pattern(name: str) -> re.Pattern[str]: 238 + r""" 239 + Pattern for labelled imports: 240 + 241 + .. code-block :: python 242 + 243 + f":{role}:`{label}<{name}>`" # e.g. ":class:`my class<MyClass>`" 244 + f":{role}:`{label}<{name}{tail}>`" # e.g. ":attr:`my_property<MyClass.my_property>`" 245 + 246 + """ 247 + return re.compile(rf":([a-z]+):`([\.a-zA-Z0-9_]+)<{name}(\.[\.a-zA-Z0-9_]+)?>`") 248 + 249 + def labelled_crossref_repl(name: str, fullname: str) -> Callable[[re.Match[str]], str]: 250 + r""" 251 + Replacement function for the pattern generated by :func:`labelled_crossref_pattern`: 252 + 253 + .. code-block :: python 254 + 255 + f":{role}:`{label}<{fullname}>`" # e.g. ":class:`my class<mymod.mysubmod.MyClass>`" 256 + f":{role}:`{label}<{fullname}{tail}>`" # e.g. ":attr:`my_property<mymod.mysubmod.MyClass.my_property>`" 257 + 258 + """ 259 + def repl(match: re.Match[str]) -> str: 260 + role = match[1] 261 + label = match[2] 262 + tail = match[3] 263 + if tail is None: 264 + return f":{role}:`{label}<{fullname}>`" 265 + return f":{role}:`{label}<{fullname}{tail}>`" 266 + return repl 267 + 268 + _crossref_subs: list[tuple[Callable[[str], re.Pattern[str]], 269 + Callable[[str, str], Callable[[re.Match[str]], str]]]] = [ 270 + (simple_crossref_pattern, simple_crossref_repl), 271 + (labelled_crossref_pattern, labelled_crossref_repl), 272 + ] 273 + r""" 274 + Substitution patterns and replacement functions for various kinds of cross-reference scenarios. 275 + """ 276 + 277 + def _get_module_by_name(modname: str) -> ModuleType: 278 + r""" 279 + Gathers a module object by name. 280 + """ 281 + # pylint: disable = exec-used, eval-used 282 + exec(f"import {modname.split('.')[0]}") 283 + mod: ModuleType = eval(modname) 284 + if not isinstance(mod, ModuleType): 285 + return None 286 + return mod 287 + 288 + def _get_obj_mod(app: Sphinx, what: str, fullname: str, obj: Any) -> Optional[ModuleType]: 289 + r""" 290 + Gathers the containing module for the given ``obj``. 291 + """ 292 + autodoc_type_aliases = app.config.__dict__.get("autodoc_type_aliases") 293 + name = fullname.split(".")[-1] 294 + obj_mod: Optional[ModuleType] 295 + if autodoc_type_aliases is not None: 296 + if name in autodoc_type_aliases and fullname == autodoc_type_aliases[name]: 297 + modname = ".".join(fullname.split(".")[:-1]) 298 + obj_mod = _get_module_by_name(modname) 299 + return obj_mod 300 + if what == "module": 301 + obj_mod = obj 302 + elif what in ("function", "class", "method"): 303 + obj_mod = inspect.getmodule(obj) 304 + elif what == "property": 305 + obj_mod = inspect.getmodule(obj.fget) 306 + elif what == "data": 307 + modname = ".".join(fullname.split(".")[:-1]) 308 + obj_mod = _get_module_by_name(modname) 309 + elif what == "attribute": 310 + modname = ".".join(fullname.split(".")[:-2]) 311 + obj_mod = _get_module_by_name(modname) 312 + else: 313 + print(f"WARNING! Encountered unexpected value for what = {what} at fullname = {fullname}") 314 + obj_mod = None 315 + return obj_mod 316 + 317 + def _build_fullname_dict(app: Sphinx, fullname: str, obj_mod: Optional[ModuleType], ) -> dict[str, str]: 318 + r""" 319 + Builds a dictionary of substitutions from module global names to their fully qualified names, 320 + based on :func:`inspect.getmodule` and `autodoc_type_aliases` (if specified in the Sphinx app config). 321 + """ 322 + autodoc_type_aliases = app.config.__dict__.get("autodoc_type_aliases") 323 + fullname_dict: dict[str, str] = {} 324 + if obj_mod is not None: 325 + globalns = obj_mod.__dict__ 326 + for g_name, g_obj in globalns.items(): 327 + if isinstance(g_obj, (FunctionType, type)): 328 + g_mod = inspect.getmodule(g_obj) 329 + elif isinstance(g_obj, ModuleType): 330 + g_mod = g_obj 331 + else: 332 + g_mod = inspect.getmodule(g_obj) 333 + if g_mod is None or g_mod == obj_mod: 334 + continue 335 + if g_name not in g_mod.__dict__: 336 + continue 337 + g_modname = g_mod.__name__ 338 + fullname_dict[g_name] = f"{g_modname}.{g_name}" 339 + if autodoc_type_aliases is not None: 340 + for a_name, a_fullname in autodoc_type_aliases.items(): 341 + if a_name not in fullname_dict: 342 + fullname_dict[a_name] = a_fullname 343 + return fullname_dict 344 + 345 + def local_crossref_handler(app: Sphinx, what: str, fullname: str, obj: Any, options: Any, lines: list[str]) -> None: 346 + r""" 347 + Handler for Sphinx Autodoc's event 348 + `autodoc-process-docstring<https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html#event-autodoc-process-docstring>`_ 349 + which replaces cross-references specified in terms of module globals with their fully qualified version. 350 + """ 351 + # pylint: disable = too-many-arguments, too-many-locals 352 + obj_mod = _get_obj_mod(app, what, fullname, obj) 353 + fullname_dict = _build_fullname_dict(app, fullname, obj_mod) 354 + for sub_name, sub_fullname in fullname_dict.items(): 355 + for idx, line in enumerate(lines): 356 + for pattern_fun, repl_fun in _crossref_subs: 357 + pattern = pattern_fun(sub_name) 358 + repl = repl_fun(sub_name, sub_fullname) 359 + line = re.sub(pattern, repl, line) 360 + lines[idx] = line 361 + 362 + def setup(app: Sphinx) -> None: 363 + r""" 364 + Registers handlers for Sphinx Autodoc's event 365 + `autodoc-process-docstring<https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html#event-autodoc-process-docstring>`_ 366 + """ 367 + app.connect("autodoc-process-docstring", sigdoc_handler) 368 + app.connect("autodoc-process-docstring", local_crossref_handler)
+15
vendor/git/dag-cbor/dag_cbor/__init__.py
··· 1 + """ 2 + Python implementation of the `DAG-CBOR codec <https://ipld.io/specs/codecs/dag-cbor/spec/>`_ specification. 3 + """ 4 + 5 + from __future__ import annotations # See https://peps.python.org/pep-0563/ 6 + 7 + __version__ = "0.3.3" 8 + 9 + from .ipld import IPLDKind, IPLDScalarKind, IPLDObjPath 10 + from .encoding import encode 11 + from .decoding import decode 12 + 13 + # explicit re-exports 14 + __all__ = ["encode", "decode", "IPLDKind", "IPLDScalarKind", "IPLDObjPath"] 15 +
+324
vendor/git/dag-cbor/dag_cbor/decoding/__init__.py
··· 1 + """ 2 + Deconding function for DAG-CBOR codec. 3 + """ 4 + 5 + from __future__ import annotations # See https://peps.python.org/pep-0563/ 6 + 7 + from io import BufferedIOBase, BytesIO 8 + import math 9 + import struct 10 + import sys 11 + from typing import Any, Dict, Callable, List, Optional, Sequence, Tuple, Union, cast 12 + import unicodedata 13 + from typing_extensions import Literal, Protocol, TypedDict 14 + from typing_validation import validate 15 + 16 + from multiformats import multicodec, CID, varint 17 + 18 + from ..ipld import IPLDKind 19 + from ..encoding import _dag_cbor_code 20 + from .err import CBORDecodingError, DAGCBORDecodingError 21 + from . import _err 22 + from ._stream import Stream 23 + 24 + __all__ = ("CBORDecodingError", "DAGCBORDecodingError") 25 + 26 + class DecodeCallback(Protocol): 27 + r""" 28 + Type of optional callbacks for the :func:`decode` function. 29 + """ 30 + def __call__(self, value: IPLDKind, num_bytes_read: int) -> None: 31 + ... 32 + 33 + class _DecodeOptions(TypedDict, total=False): 34 + r""" Options passed around to decoding sub-routines. """ 35 + 36 + callback: "DecodeCallback" 37 + r""" An optional callback to be called on each decoded item. """ 38 + 39 + normalize_strings: Literal["NFC", "NFKC", "NFD", "NFKD"] 40 + r""" Optional Unicode normalization to be performed on decoded UTF-8 strings. """ 41 + 42 + def decode(stream_or_bytes: Union[BufferedIOBase, bytes], *, 43 + allow_concat: bool = False, 44 + callback: Optional["DecodeCallback"] = None, 45 + require_multicodec: bool = False, 46 + normalize_strings: Literal["NFC", "NFKC", "NFD", "NFKD", None] = None) -> IPLDKind: 47 + r""" 48 + Decodes and returns a single data item from the given ``stream_or_bytes``, with the DAG-CBOR codec. 49 + 50 + A simple use for the optional ``callback`` argument is to count the number of bytes read from the stream: 51 + 52 + >>> import dag_cbor 53 + >>> from io import BytesIO 54 + >>> class BytesReadCounter: 55 + ... _num_bytes_read = 0 56 + ... def __call__(self, _, num_bytes_read): 57 + ... self._num_bytes_read += num_bytes_read 58 + ... def __int__(self): 59 + ... return self._num_bytes_read 60 + ... 61 + >>> encoded_bytes = b'\xa2aa\x0cabfhello!\x82\x00\x01' 62 + >>> len(encoded_bytes) 63 + 16 64 + >>> stream = BytesIO(encoded_bytes) 65 + >>> bytes_read_cnt = BytesReadCounter() 66 + >>> dag_cbor.decode(stream, allow_concat=True, callback=bytes_read_cnt) 67 + {'a': 12, 'b': 'hello!'} 68 + >>> int(bytes_read_cnt) 69 + 13 70 + >>> bytes_remaining = stream.read() 71 + >>> bytes_remaining 72 + b'\x82\x00\x01' 73 + >>> len(bytes_remaining) 74 + 3 75 + >>> dag_cbor.decode(bytes_remaining) 76 + [0, 1] 77 + 78 + :param stream_or_bytes: the bytes object or bytes stream to decode 79 + :param allow_concat: whether to allow partial stream decoding (if this is :obj:`False`, a byte stream will always be consumed in its entirety) 80 + :param callback: optional callback to be invoked as ``callback(item, num_bytes_read)`` every time an item is decoded, 81 + where ``num_bytes_read`` is the number of bytes read decoding the item (excluding sub-items, in the case of lists or dictionaries). 82 + :param require_multicodec: if :obj:`True`, the data being decoded must be prefixed by the multicodec code for ``'dag-cbor'`` 83 + (see `multicodec.unwrap <https://multiformats.readthedocs.io/en/latest/api/multiformats.multicodec.html#unwrap>`_). 84 + :param normalize_strings: whether strings should be normalised after decoding 85 + 86 + :raises CBORDecodingError: while reading the leading byte of a data item head, if no bytes are available 87 + :raises CBORDecodingError: while reading the argument bytes of a data item head, 88 + if the expected number of argument bytes is not available 89 + :raises CBORDecodingError: while decoding the data of a bytestring or string, if the expected number of data bytes is not available 90 + :raises CBORDecodingError: while decoding the items of a list or a map (keys and values), 91 + if the expected number of items is not available 92 + :raises CBORDecodingError: if an invalid utf-8 byte sequence is encountered while attempting to decode a string 93 + :raises DAGCBORDecodingError: if attempting to decode the special :obj:`float` values ``NaN``, ``Infinity`` and ``-Infinity`` 94 + :raises DAGCBORDecodingError: if the additional info is greater than 27, or different from 27 for major type 7 95 + :raises DAGCBORDecodingError: if an integer value was not minimally encoded 96 + :raises DAGCBORDecodingError: if a key of a map is not a string 97 + :raises DAGCBORDecodingError: if a map has repeated keys 98 + :raises DAGCBORDecodingError: if map keys are not in canonical order 99 + :raises DAGCBORDecodingError: if a tag (major type 6) different than 42 (for CID data) is encountered 100 + :raises DAGCBORDecodingError: if non-bytestring data is found where CID data is expected (tag 42) 101 + :raises DAGCBORDecodingError: if a simple value (major type 7) different from 20 (False), 21 (True) or 22 (None) is encountered 102 + :raises DAGCBORDecodingError: if ``require_multicodec`` is set to :obj:`True` and 103 + the bytes are not prefixed by the ``'dag-cbor'`` multicodec code 104 + :raises DAGCBORDecodingError: if ``allow_concat`` is set to :obj:`False` and the decoding did not use all available bytes 105 + 106 + """ 107 + validate(stream_or_bytes, Union[BufferedIOBase, bytes]) 108 + validate(allow_concat, bool) 109 + validate(require_multicodec, bool) 110 + options: _DecodeOptions = {} 111 + if callback is not None: 112 + options["callback"] = callback 113 + if normalize_strings is not None: 114 + validate(normalize_strings, Literal["NFC", "NFKC", "NFD", "NFKD"]) 115 + options["normalize_strings"] = normalize_strings 116 + if isinstance(stream_or_bytes, bytes): 117 + _stream: BufferedIOBase = BytesIO(stream_or_bytes) 118 + else: 119 + _stream = stream_or_bytes 120 + if require_multicodec: 121 + code, _, _stream = multicodec.unwrap_raw(_stream) 122 + stream = Stream(_stream, varint.encode(code)) 123 + if code != _dag_cbor_code: 124 + raise DAGCBORDecodingError(_err._required_multicodec(stream)) 125 + else: 126 + stream = Stream(_stream) 127 + data, _ = _decode_item(stream, options) 128 + if not allow_concat: 129 + remaining_bytes = stream.read() 130 + if len(remaining_bytes) > 0: 131 + raise DAGCBORDecodingError(_err._multiple_top_level_items(stream)) 132 + return data 133 + 134 + def _decode_item(stream: Stream, options: _DecodeOptions) -> Tuple[IPLDKind, int]: 135 + major_type, arg, num_bytes_read = _decode_head(stream) 136 + ret: Optional[Tuple[IPLDKind, int]] = None 137 + assert 0x0 <= major_type <= 0x7, f"Major type must be one of 0x0-0x7, found 0x{major_type:x} instead." 138 + if isinstance(arg, float): 139 + # Major type 0x7 (float case): 140 + assert major_type == 0x7, f"Major type for float must be 0x7, found 0x{major_type:x} instead." 141 + if math.isnan(arg) or math.isinf(arg): 142 + raise DAGCBORDecodingError(_err._invalid_float(stream, arg)) 143 + ret = (arg, num_bytes_read) 144 + elif major_type <= 0x1: 145 + # Major types 0x0 and 0x1: 146 + ret = (arg if major_type == 0x0 else -1-arg, num_bytes_read) 147 + else: 148 + # Major types 0x2-0x6 and 0x7 (bool/null case): 149 + value, num_bytes_further_read = _decoders[major_type](stream, arg, options) 150 + ret = (value, num_bytes_read+num_bytes_further_read) 151 + if "callback" in options: 152 + options["callback"](*ret) 153 + return ret 154 + 155 + 156 + def _decode_head(stream: Stream) -> Tuple[int, Union[int, float], int]: 157 + # pylint: disable = too-many-branches 158 + # read leading byte 159 + res = stream.read(1) 160 + if len(res) < 1: 161 + raise CBORDecodingError(_err._unexpected_eof(stream, "leading byte of data item head", 1, include_prev_snapshot=False)) 162 + leading_byte = res[0] 163 + major_type = leading_byte >> 5 164 + additional_info = leading_byte & 0b11111 165 + # read argument value and return (major_type, arg, num_bytes_read) 166 + if additional_info < 24: 167 + # argument value = additional info 168 + return (major_type, additional_info, 1) 169 + if additional_info > 27 or (major_type == 0x7 and additional_info != 27): 170 + raise DAGCBORDecodingError(_err._invalid_additional_info(stream, additional_info, major_type)) 171 + argument_nbytes = 1<<(additional_info-24) 172 + res = stream.read(argument_nbytes) 173 + if len(res) < argument_nbytes: 174 + raise CBORDecodingError(_err._unexpected_eof(stream, f"{argument_nbytes} byte argument of data item head", argument_nbytes)) 175 + if additional_info == 24: 176 + # 1 byte of unsigned int argument value to follow 177 + if res[0] < 24: 178 + raise DAGCBORDecodingError(_err._excessive_int_size(stream, res[0], 1, 0)) 179 + return (major_type, res[0], 2) 180 + if additional_info == 25: 181 + # 2 bytes of unsigned int argument value to follow 182 + arg = struct.unpack(">H", res)[0] 183 + if arg <= 255: 184 + raise DAGCBORDecodingError(_err._excessive_int_size(stream, arg, 2, 1)) 185 + return (major_type, arg, 3) 186 + if additional_info == 26: 187 + # 4 bytes of unsigned int argument value to follow 188 + arg = struct.unpack(">L", res)[0] 189 + if arg <= 65535: 190 + if arg <= 255: 191 + raise DAGCBORDecodingError(_err._excessive_int_size(stream, arg, 4, 1)) 192 + raise DAGCBORDecodingError(_err._excessive_int_size(stream, arg, 4, 2)) 193 + return (major_type, arg, 5) 194 + # necessarily additional_info == 27 195 + if major_type == 0x7: 196 + # 8 bytes of float argument value to follow 197 + return (major_type, struct.unpack(">d", res)[0], 9) 198 + # 8 bytes of unsigned int argument value to follow 199 + arg = struct.unpack(">Q", res)[0] 200 + if arg <= 4294967295: 201 + if arg <= 255: 202 + raise DAGCBORDecodingError(_err._excessive_int_size(stream, arg, 8, 1)) 203 + if arg <= 65535: 204 + raise DAGCBORDecodingError(_err._excessive_int_size(stream, arg, 8, 2)) 205 + raise DAGCBORDecodingError(_err._excessive_int_size(stream, arg, 8, 4)) 206 + return (major_type, arg, 9) 207 + 208 + def _decode_bytes(stream: Stream, length: int, options: _DecodeOptions) -> Tuple[bytes, int]: 209 + res = stream.read(length) 210 + if len(res) < length: 211 + raise CBORDecodingError(_err._unexpected_eof(stream, f"{length} bytes of bytestring", length)) 212 + return (res, length) 213 + 214 + def _decode_str(stream: Stream, length: int, options: _DecodeOptions) -> Tuple[str, int]: 215 + res = stream.read(length) 216 + if len(res) < length: 217 + raise CBORDecodingError(_err._unexpected_eof(stream, f"{length} bytes of string", length)) 218 + try: 219 + s = res.decode(encoding="utf-8", errors="strict") 220 + except UnicodeDecodeError as e: 221 + raise CBORDecodingError(_err._unicode(stream, length, e.start, e.end, e.reason)) # pylint: disable = raise-missing-from 222 + if "normalize_strings" in options: 223 + s = unicodedata.normalize(options["normalize_strings"], s) 224 + return (s, length) 225 + 226 + def _decode_list(stream: Stream, length: int, options: _DecodeOptions) -> Tuple[List[Any], int]: 227 + list_head_snapshot = stream.curr_snapshot 228 + l: List[Any] = [] 229 + for idx in range(length): 230 + try: 231 + item, _ = _decode_item(stream, options) 232 + l.append(item) 233 + except CBORDecodingError as e: 234 + raise CBORDecodingError(_err._list_item(list_head_snapshot, idx, length, e)) # pylint: disable = raise-missing-from 235 + return (l, 0) 236 + 237 + def _decode_dict_key(stream: Stream, key_idx: int, dict_length: int, options: _DecodeOptions) -> Tuple[str, int, bytes]: 238 + # pylint: disable = too-many-return-statements, too-many-branches 239 + major_type, arg, num_bytes_read = _decode_head(stream) 240 + ret: Optional[Tuple[IPLDKind, int]] = None 241 + if major_type != 0x3: 242 + raise DAGCBORDecodingError(_err._dict_key_type(stream, major_type)) 243 + assert not isinstance(arg, float) 244 + str_length = arg 245 + str_bytes: bytes = stream.read(str_length) 246 + if len(str_bytes) < str_length: 247 + raise CBORDecodingError(_err._unexpected_eof(stream, f"{str_length} bytes of string", str_length)) 248 + try: 249 + s = str_bytes.decode(encoding="utf-8", errors="strict") 250 + except UnicodeDecodeError as e: 251 + raise CBORDecodingError(_err._unicode(stream, str_length, e.start, e.end, e.reason)) # pylint: disable = raise-missing-from 252 + if "normalize_strings" in options: 253 + s = unicodedata.normalize(options["normalize_strings"], s) 254 + ret = (s, num_bytes_read+str_length) 255 + if "callback" in options: 256 + options["callback"](*ret) 257 + return ret+(str_bytes,) 258 + 259 + def _decode_dict(stream: Stream, length: int, options: _DecodeOptions) -> Tuple[Dict[str, Any], int]: 260 + # pylint: disable = too-many-locals 261 + dict_head_snapshot = stream.curr_snapshot 262 + d: Dict[str, Any] = {} 263 + key_bytes_list: List[bytes] = [] 264 + for i in range(length): 265 + try: 266 + k, _, k_bytes = _decode_dict_key(stream, i, length, options) 267 + except CBORDecodingError as e: 268 + raise CBORDecodingError(_err._dict_item(dict_head_snapshot, "key", i, length, e)) # pylint: disable = raise-missing-from 269 + if k in d: 270 + raise DAGCBORDecodingError(_err._duplicate_dict_key(dict_head_snapshot, stream, k, i, length)) 271 + try: 272 + v, _ = _decode_item(stream, options) 273 + except CBORDecodingError as e: 274 + raise CBORDecodingError(_err._dict_item(dict_head_snapshot, "value", i, length, e)) # pylint: disable = raise-missing-from 275 + d[k] = v 276 + key_bytes_list.append(k_bytes) 277 + # check that keys are sorted canonically 278 + assert len(key_bytes_list) == length 279 + sorted_key_bytes_list = sorted(key_bytes_list, key=lambda e: (len(e), e)) 280 + for idx0, (kb0, kb1) in enumerate(zip(key_bytes_list, sorted_key_bytes_list)): 281 + if kb0 != kb1: 282 + idx1 = key_bytes_list.index(kb1) 283 + raise DAGCBORDecodingError(_err._dict_key_order(dict_head_snapshot, kb0, idx0, kb1, idx1, length)) 284 + return (d, 0) 285 + 286 + def _decode_cid(stream: Stream, arg: int, options: _DecodeOptions) -> Tuple[CID, int]: 287 + if arg != 42: 288 + raise DAGCBORDecodingError(_err._invalid_tag(stream, arg)) 289 + cid_head_snapshots = stream.prev_snapshot, stream.curr_snapshot 290 + try: 291 + if "callback" in options: 292 + options = cast(_DecodeOptions, {**options}) 293 + del options["callback"] 294 + cid_bytes, num_bytes_read = _decode_item(stream, options) 295 + except CBORDecodingError as e: 296 + raise CBORDecodingError(_err._cid(cid_head_snapshots, e)) # pylint: disable = raise-missing-from 297 + if not isinstance(cid_bytes, bytes): 298 + raise DAGCBORDecodingError(_err._cid_bytes(cid_head_snapshots, stream, cid_bytes)) 299 + if not cid_bytes[0] == 0: 300 + raise DAGCBORDecodingError(_err._cid_multibase(cid_head_snapshots, stream, cid_bytes)) 301 + return (CID.decode(cid_bytes[1:]), num_bytes_read) 302 + 303 + def _decode_bool_none(stream: Stream, arg: int, options: _DecodeOptions) -> Tuple[Optional[bool], int]: 304 + if arg == 20: 305 + return (False, 0) 306 + if arg == 21: 307 + return (True, 0) 308 + if arg == 22: 309 + return (None, 0) 310 + raise DAGCBORDecodingError(_err._simple_value(stream, arg)) 311 + 312 + def _decode_dummy(stream: Stream, arg: int, options: _DecodeOptions) -> Tuple[None, int]: 313 + assert False, f"Major type {arg} does not have an associated decoder." 314 + 315 + _decoders: Tuple[Callable[[Stream, int, _DecodeOptions], Tuple[IPLDKind, int]], ...] = ( 316 + _decode_dummy, 317 + _decode_dummy, 318 + _decode_bytes, 319 + _decode_str, 320 + _decode_list, 321 + _decode_dict, 322 + _decode_cid, 323 + _decode_bool_none 324 + )
+162
vendor/git/dag-cbor/dag_cbor/decoding/_err.py
··· 1 + r""" 2 + Detailed messages for all possible DAG-CBOR decoding errors. 3 + """ 4 + 5 + from __future__ import annotations # See https://peps.python.org/pep-0563/ 6 + 7 + import math 8 + from typing import Tuple 9 + from typing_extensions import Literal 10 + 11 + from multiformats import varint 12 + 13 + from ..ipld import IPLDKind 14 + from ..encoding import _dag_cbor_code 15 + from .err import CBORDecodingError 16 + from ._stream import Stream, StreamSnapshot 17 + from ._err_utils import _bytes2hex, _decode_error_msg_lines, _decode_error_msg, _extract_error_cause_lines, _cid_error_template 18 + 19 + def _required_multicodec(stream: Stream) -> str: 20 + curr_snapshot = stream.curr_snapshot 21 + msg = "Required 'dag-cbor' multicodec code." 22 + exp_bs = varint.encode(_dag_cbor_code) 23 + details = f"byte{'s' if curr_snapshot.latest_read_size > 1 else ''} should be 0x{exp_bs.hex()}." 24 + return _decode_error_msg(msg, curr_snapshot, details=details) 25 + 26 + def _multiple_top_level_items(stream: Stream) -> str: 27 + msg = "Encode and decode must operate on a single top-level CBOR object." 28 + details = "unexpected start byte of a second top-level CBOR object" 29 + return _decode_error_msg(msg, stream.curr_snapshot, details=details) 30 + 31 + def _invalid_float(stream: Stream, arg: float) -> str: 32 + if math.isnan(arg): 33 + msg = "NaN is not an allowed float value." 34 + float_str = "float('NaN')" 35 + else: 36 + assert math.isinf(arg), "Float must be NaN or infinite." 37 + s = ("" if arg > 0 else "-") 38 + msg = s+"Infinity is not an allowed float value." 39 + float_str = f"float('{s}Infinity')" 40 + details = f"struct.pack('>d', {float_str})" 41 + return _decode_error_msg(msg, stream.curr_snapshot, details=details, hl_start=1) 42 + 43 + def _unexpected_eof(stream: Stream, what: str, n: int, include_prev_snapshot: bool = True) -> str: 44 + prev_snapshot = stream.prev_snapshot if include_prev_snapshot else StreamSnapshot(bytes(), 0) 45 + curr_snapshot = stream.curr_snapshot 46 + msg = f"Unexpected EOF while attempting to read {what}." 47 + bytes_read = curr_snapshot.latest_read_size 48 + hl_start = prev_snapshot.latest_read_size 49 + details = f"{bytes_read} bytes read, out of {n} expected." 50 + snapshots = [prev_snapshot, curr_snapshot] if include_prev_snapshot else [curr_snapshot] 51 + return _decode_error_msg(msg, *snapshots, details=details, hl_start=hl_start) 52 + 53 + def _invalid_additional_info(stream: Stream, additional_info: int, major_type: int) -> str: 54 + msg = f"Invalid additional info {additional_info} in data item head for major type 0x{major_type:x}." 55 + if major_type == 0x7: 56 + details = f"lower 5 bits are {additional_info:0>5b}, expected from {0:0>5b} to {23:0>5b}, or {27:0>5b}." 57 + else: 58 + details = f"lower 5 bits are {additional_info:0>5b}, expected from {0:0>5b} to {27:0>5b}." 59 + return _decode_error_msg(msg, stream.curr_snapshot, details=details) 60 + 61 + def _excessive_int_size(stream: Stream, arg: int, bytes_used: int, bytes_sufficient: int) -> str: 62 + s = 's' if bytes_sufficient > 1 else '' 63 + msg = f"Integer {arg} was encoded using {bytes_used} bytes, while {bytes_sufficient} byte{s} would have been enough." 64 + details = f"same as byte{s} 0x{arg:0>{2*bytes_sufficient}x}" 65 + return _decode_error_msg(msg, stream.prev_snapshot, stream.curr_snapshot, details=details, hl_start=1) 66 + 67 + def _unicode(stream: Stream, length: int, start: int, end: int, reason: str) -> str: 68 + prev_snapshot = stream.prev_snapshot 69 + curr_snapshot = stream.curr_snapshot 70 + msg = "String bytes are not valid utf-8 bytes." 71 + lines = [msg] 72 + str_details = f"string of length {length}" 73 + lines.extend(_decode_error_msg_lines(prev_snapshot, curr_snapshot, details=str_details, hl_len=1)) 74 + lines.extend(_decode_error_msg_lines(curr_snapshot, details=reason, start=start, end=end, pad_start=start+prev_snapshot.latest_read_size)) 75 + return "\n".join(lines) 76 + 77 + def _list_item(list_head_snapshot: StreamSnapshot, idx: int, length: int, e: CBORDecodingError) -> str: 78 + msg = "Error while decoding list." 79 + lines = [ 80 + msg, 81 + *_decode_error_msg_lines(list_head_snapshot, details=f"list of length {length}", dots=True), 82 + f"Error occurred while decoding item at position {idx}: further details below.", 83 + *_extract_error_cause_lines(e) 84 + ] 85 + return "\n".join(lines) 86 + 87 + def _dict_key_type(stream: Stream, major_type: int) -> str: 88 + msg = "Dictionary key is not of string type." 89 + details = f"major type is {hex(major_type)}, should be 0x3 (string) instead." 90 + return _decode_error_msg(msg, stream.curr_snapshot, details=details, hl_len=1, dots=True) 91 + 92 + def _dict_item(dict_head_snapshot: StreamSnapshot, item: Literal["key", "value"], idx: int, length: int, e: CBORDecodingError) -> str: 93 + msg = "Error while decoding dict." 94 + details = f"dict of length {length}" 95 + lines = [ 96 + msg, 97 + *_decode_error_msg_lines(dict_head_snapshot, details=details, dots=True), 98 + f"Error occurred while decoding {item} at position {idx}: further details below.", 99 + *_extract_error_cause_lines(e) 100 + ] 101 + return "\n".join(lines) 102 + 103 + def _duplicate_dict_key(dict_head_snapshot: StreamSnapshot, stream: Stream, k: str, idx: int, length: int) -> str: 104 + msg = "Error while decoding dict." 105 + dict_details = f"dict of length {length}" 106 + key_details = f"decodes to key {repr(k)}" 107 + lines = [ 108 + msg, 109 + *_decode_error_msg_lines(dict_head_snapshot, details=dict_details, dots=True), 110 + f"Duplicate key is found at position {idx}.", 111 + *_decode_error_msg_lines(stream.curr_snapshot, details=key_details) 112 + ] 113 + return "\n".join(lines) 114 + 115 + def _dict_key_order(dict_head_snapshot: StreamSnapshot, kb0: bytes, idx0: int, kb1: bytes, idx1: int, length: int) -> str: 116 + # pylint: disable = too-many-arguments 117 + msg = "Error while decoding dict." 118 + pad_len = max(len(str(idx0)), len(str(idx1))) 119 + idx0_str = f"{idx0: >{pad_len}}" 120 + idx1_str = f"{idx1: >{pad_len}}" 121 + details = f"dict of length {length}" 122 + lines = [ 123 + msg, 124 + *_decode_error_msg_lines(dict_head_snapshot, details=details, dots=True), 125 + "Dictionary keys not in canonical order.", 126 + f" Key at pos #{idx0_str}: {_bytes2hex(kb0)}", 127 + f" Key at pos #{idx1_str}: {_bytes2hex(kb1)}", 128 + ] 129 + return "\n".join(lines) 130 + 131 + def _invalid_tag(stream: Stream, arg: int) -> str: 132 + prev_snapshot = stream.prev_snapshot 133 + curr_snapshot = stream.curr_snapshot 134 + msg = "Error while decoding item of major type 0x6: only tag 42 is allowed." 135 + details = f"tag {arg}" 136 + hl_start = prev_snapshot.latest_read_size 137 + return _decode_error_msg(msg, prev_snapshot, curr_snapshot, details=details, hl_start=hl_start) 138 + 139 + def _cid(cid_head_snapshots: Tuple[StreamSnapshot, StreamSnapshot], e: CBORDecodingError) -> str: 140 + return _cid_error_template(cid_head_snapshots, *_extract_error_cause_lines(e)) 141 + 142 + def _cid_bytes(cid_head_snapshots: Tuple[StreamSnapshot, StreamSnapshot], stream: Stream, cid_bytes: IPLDKind) -> str: 143 + decoded_type = type(cid_bytes).__name__ 144 + details = f"decodes to an item of type {repr(decoded_type)}" 145 + explanation = [ 146 + "CID bytes did not decode to an item of type 'bytes'.", 147 + *_decode_error_msg_lines(stream.curr_snapshot, details=details), 148 + ] 149 + return _cid_error_template(cid_head_snapshots, *explanation) 150 + 151 + def _cid_multibase(cid_head_snapshots: Tuple[StreamSnapshot, StreamSnapshot], stream: Stream, cid_bytes: bytes) -> str: 152 + details = "byte should be 0x00" 153 + explanation = [ 154 + "CID does not start with the identity Multibase prefix.", 155 + *_decode_error_msg_lines(stream.prev_snapshot, stream.curr_snapshot, details=details, hl_start=1, hl_len=1), 156 + ] 157 + return _cid_error_template(cid_head_snapshots, *explanation) 158 + 159 + def _simple_value(stream: Stream, arg: int) -> str: 160 + msg = "Error while decoding major type 0x7: allowed simple values are 0x14, 0x15 and 0x16." 161 + details = f"simple value is {arg}" 162 + return _decode_error_msg(msg, stream.curr_snapshot, details=details)
+117
vendor/git/dag-cbor/dag_cbor/decoding/_err_utils.py
··· 1 + r""" 2 + Utility functions used to produce messages for DAG-CBOR decoding errors. 3 + """ 4 + 5 + from __future__ import annotations # See https://peps.python.org/pep-0563/ 6 + 7 + from typing import List, Optional, Tuple 8 + 9 + from .err import CBORDecodingError 10 + from ._stream import StreamSnapshot 11 + 12 + _TRUNC_BYTES = 16 13 + 14 + def _bytes2hex(bs: bytes) -> str: 15 + r""" 16 + Converts bytes to a hex string, showing a truncated string if the number of bytes exceeds 16. 17 + """ 18 + if len(bs) <= _TRUNC_BYTES: 19 + return bs.hex() 20 + return bs[:1].hex()+"..."+bs[-1:].hex() # fixed length 7 < 2*_TRUNC_BYTES 21 + 22 + def _decode_error_msg_lines(*snapshots: StreamSnapshot, details: Optional[str] = None, 23 + start: int = 0, 24 + end: Optional[int] = None, 25 + pad_start: int = 0, 26 + hl_start: int = 0, 27 + hl_len: Optional[int] = None, 28 + dots: bool = False, 29 + ) -> List[str]: 30 + r""" 31 + This utility function takes one or more stream snapshots as input and collates the chunks of read bytes. Let ``bs`` be the bytes read across the 32 + snapshots (which are assumed to be sequential) and ``pos`` be the position in the stream of the first byte of ``bs`` (he ``start`` and ``end`` arguments 33 + can be used to focus on a sub-range): 34 + 35 + .. code-block:: python 36 + 37 + bs = b"".join((snapshot.latest_read for snapshot in snapshots))[start:end] 38 + pos = snapshots[0].latest_read_start+start 39 + 40 + Th utility function returns one or two lines of error message, based on that information: 41 + 42 + 1. The first line shows (a selection of) the bytes read and the position of the first byte in the stream 43 + 2. If ``details`` is specified, the second line highlights a selection of bytes from the first line, followed by the given details 44 + 45 + The following optional keyword arguments can be used to customise the selection of read bytes and the highlighting. 46 + 47 + - The ``pad_start`` arguments can be used to specify whitespace padding at the start of the bytes shown, to align them to bytes on other error lines. 48 + - The ``hl_start`` and ``hl_len`` arguments can be used to specify the start byte and lenght of the range of bytes highlighted in the second line. 49 + - The ``dots`` argument can be used to specify that three dots '...' should be added after the bytes, to indicate continuation. 50 + 51 + """ 52 + # pylint: disable = too-many-locals, too-many-arguments 53 + assert snapshots 54 + bs = b"".join((snapshot.latest_read for snapshot in snapshots))[start:end] 55 + pos = snapshots[0].latest_read_start+start 56 + bs_str = _bytes2hex(bs) 57 + truncated = len(bs_str) != 2*len(bs) 58 + if not bs_str: 59 + bs_str = "<EOF>" 60 + bs_tab = "^"*len(bs_str) 61 + else: 62 + if hl_len is None: 63 + hl_len = len(bs)-hl_start 64 + else: 65 + assert 0 <= hl_len <= len(bs)-start 66 + if truncated and not (hl_len == 1 and (hl_start in {0, len(bs)-1})): 67 + bs_tab = "^"*len(bs_str) 68 + else: 69 + bs_tab = " "*hl_start+"^^"*hl_len 70 + bs_str = " "*pad_start+bs_str 71 + bs_tab = " "*pad_start+bs_tab 72 + bytes_line = f"At byte #{pos}: {bs_str}" 73 + if truncated: 74 + last_byte_idx = pos+len(bs)-1 75 + bytes_line += f" (last byte #{last_byte_idx})" 76 + if dots: 77 + bytes_line += "..." 78 + lines = [bytes_line] 79 + if details is not None: 80 + details_line = f" {' '*len(str(pos))} {bs_tab} {details}" 81 + lines.append(details_line) 82 + return lines 83 + 84 + def _decode_error_msg(msg: str, *snapshots: StreamSnapshot, details: Optional[str] = None, 85 + start: int = 0, 86 + end: Optional[int] = None, 87 + hl_start: int = 0, 88 + hl_len: Optional[int] = None, 89 + dots: bool = False, 90 + ) -> str: 91 + r""" 92 + Creates a detailed, multi-line error message, starting from a given ``msg`` and taking into account the information from one or more stream snapshots. 93 + The resulting error message has ``msg`` on the first line, followed by the lines returned by :func:`_decode_error_msg_lines`. 94 + """ 95 + # pylint: disable = too-many-arguments 96 + lines = [msg] 97 + if snapshots: 98 + lines.extend(_decode_error_msg_lines(*snapshots, details=details, 99 + start=start, end=end, hl_start=hl_start, hl_len=hl_len, 100 + dots=dots)) 101 + return "\n".join(lines) 102 + 103 + 104 + def _extract_error_cause_lines(e: CBORDecodingError) -> List[str]: 105 + r""" Extracts lines of error description from a :class:`CBORDecodingError`. """ 106 + lines = str(e).split("\n") 107 + return [(r"\ " if idx == 0 else " ")+line for idx, line in enumerate(lines)] 108 + 109 + 110 + def _cid_error_template(cid_head_snapshots: Tuple[StreamSnapshot, StreamSnapshot], *explanation: str) -> str: 111 + r""" Template for CID errors. """ 112 + lines = [ 113 + "Error while decoding CID.", 114 + *_decode_error_msg_lines(*cid_head_snapshots, details="CID tag", dots=True), 115 + *explanation 116 + ] 117 + return "\n".join(lines)
+91
vendor/git/dag-cbor/dag_cbor/decoding/_stream.py
··· 1 + r""" 2 + Byte-streams and snapshots used in DAG-CBOR decoding, keeping track of latest and previous read byte chunks for error reporting purposes. 3 + """ 4 + 5 + from __future__ import annotations # See https://peps.python.org/pep-0563/ 6 + 7 + from io import BufferedIOBase, BytesIO 8 + from typing import Optional 9 + 10 + class StreamSnapshot: 11 + r""" A snapshot of the current state of a byte-stream being decoded. """ 12 + 13 + _bs: bytes 14 + _pos: int 15 + 16 + def __new__(cls, latest_read: bytes, next_read_start: int) -> "StreamSnapshot": 17 + instance = object.__new__(cls) 18 + instance._bs = latest_read 19 + instance._pos = next_read_start 20 + return instance 21 + 22 + @property 23 + def latest_read(self) -> bytes: 24 + r""" The latest byte chunk read from the stream. """ 25 + return self._bs 26 + 27 + @property 28 + def latest_read_size(self) -> int: 29 + r""" Size of the latest byte chunk read from the stream. """ 30 + return len(self._bs) 31 + 32 + @property 33 + def latest_read_start(self) -> int: 34 + r""" Start position in the stream for the latest byte chunk read. """ 35 + return self._pos-len(self._bs) 36 + 37 + @property 38 + def num_bytes_read(self) -> int: 39 + r""" Total number of bytes read so far from the stream. """ 40 + return self._pos 41 + 42 + 43 + class Stream: 44 + r""" 45 + Container for the byte-stream being decoded, offering additional book-keeping functionality used to produce detailed error messages. 46 + """ 47 + 48 + _buf: BufferedIOBase 49 + _bs: bytes 50 + _pos: int 51 + _prev_bs: bytes 52 + _prev_pos: int 53 + 54 + def __new__(cls, buffer: Optional[BufferedIOBase] = None, init_bytes_read: bytes = bytes()) -> "Stream": 55 + if buffer is None: 56 + buffer = BytesIO(bytes()) 57 + instance = object.__new__(cls) 58 + instance._buf = buffer 59 + instance._bs = init_bytes_read 60 + instance._pos = len(init_bytes_read) 61 + instance._prev_bs = bytes() 62 + instance._prev_pos = 0 63 + return instance 64 + 65 + @property 66 + def curr_snapshot(self) -> "StreamSnapshot": 67 + r""" A snapshot of the current state of the stream. """ 68 + return StreamSnapshot(self._bs, self._pos) 69 + 70 + @property 71 + def prev_snapshot(self) -> "StreamSnapshot": 72 + r""" A snapshot of the state of the stream immediately before the latest non-extending read. """ 73 + return StreamSnapshot(self._prev_bs, self._prev_pos) 74 + 75 + def read(self, num_bytes: Optional[int] = None, *, extend: bool = False) -> bytes: 76 + r""" 77 + Read the given number of bytes from the stream. If :obj:`None`, reads all remaining bytes. 78 + If ``extend`` is set to :obj:`True`, the current stream snapshot (see :attr:`Stream.curr_snapshot`) is extended with the bytes just read, 79 + and the previous stream snapshot (see :attr:`Stream.prev_snapshot`) is kept. 80 + Otherwise, the previous snapshot is replaced with the current snaptshot, and a new current snapshot is created with the bytes just read. 81 + """ 82 + bs = self._buf.read(num_bytes) 83 + if extend: 84 + self._bs += bs 85 + self._pos += len(bs) 86 + else: 87 + self._prev_bs = self._bs 88 + self._prev_pos = self._pos 89 + self._bs = bs 90 + self._pos += len(bs) 91 + return bs
+15
vendor/git/dag-cbor/dag_cbor/decoding/err.py
··· 1 + r""" 2 + Errors for the :mod:`dag_cbor.decoding` submodule. 3 + """ 4 + 5 + from ..encoding.err import CBORError, DAGCBORError 6 + 7 + class CBORDecodingError(CBORError): 8 + """ 9 + Class for decoding errors due to the CBOR specification. 10 + """ 11 + 12 + class DAGCBORDecodingError(CBORDecodingError, DAGCBORError): 13 + """ 14 + Class for decoding errors due to the DAG-CBOR specification. 15 + """
+272
vendor/git/dag-cbor/dag_cbor/encoding/__init__.py
··· 1 + """ 2 + Encoding functions for DAG-CBOR codec. 3 + """ 4 + 5 + from __future__ import annotations # See https://peps.python.org/pep-0563/ 6 + 7 + from io import BufferedIOBase, BytesIO 8 + import math 9 + import struct 10 + from typing import Any, Dict, List, Optional, overload, Union 11 + import unicodedata 12 + 13 + from typing_extensions import Literal, TypedDict 14 + from typing_validation import validate 15 + 16 + from multiformats import varint, multicodec, CID 17 + 18 + from ..ipld import IPLDKind, IPLDObjPath 19 + from .err import CBOREncodingError, DAGCBOREncodingError 20 + 21 + __all__ = ("CBOREncodingError", "DAGCBOREncodingError") 22 + 23 + _dag_cbor_multicodec = multicodec.get("dag-cbor") 24 + _dag_cbor_code: int = _dag_cbor_multicodec.code 25 + _dag_cbor_code_bytes: bytes = varint.encode(_dag_cbor_code) 26 + _dag_cbor_code_nbytes: int = len(_dag_cbor_code_bytes) 27 + 28 + def check_key_compliance(value: Dict[str, Any]) -> None: 29 + """ 30 + Enforces DAG-CBOR compliance for keys in a mapping. 31 + """ 32 + validate(value, Dict[str, Any]) 33 + _check_key_compliance(value) 34 + 35 + def canonical_order_dict(value: Dict[str, Any]) -> Dict[str, Any]: 36 + """ 37 + Returns a dictionary with canonically ordered keys, according to the DAG-CBOR specification. 38 + Specifically, keys are sorted increasingly first by length and then by the lexicographic ordering of the corresponding UTF-8 bytestrings. 39 + """ 40 + validate(value, Dict[str, Any]) 41 + _check_key_compliance(value) 42 + # sort keys canonically 43 + return _canonical_order_dict(value) 44 + 45 + 46 + @overload 47 + def encode(data: IPLDKind, stream: None = None, *, 48 + include_multicodec: bool = False, 49 + normalize_strings: Optional[Literal["NFC", "NFKC", "NFD", "NFKD"]] = None 50 + ) -> bytes: 51 + ... # pragma: no cover 52 + 53 + @overload 54 + def encode(data: IPLDKind, stream: BufferedIOBase, *, 55 + include_multicodec: bool = False, 56 + normalize_strings: Optional[Literal["NFC", "NFKC", "NFD", "NFKD"]] = None 57 + ) -> int: 58 + ... # pragma: no cover 59 + 60 + def encode(data: IPLDKind, stream: Optional[BufferedIOBase] = None, *, 61 + include_multicodec: bool = False, 62 + normalize_strings: Optional[Literal["NFC", "NFKC", "NFD", "NFKD"]] = None 63 + ) -> Union[bytes, int]: 64 + r""" 65 + Encodes the given data with the DAG-CBOR codec. 66 + 67 + By default, the encoded data is written to an internal stream and the bytes are returned at the end (as a `bytes` object). 68 + 69 + .. code-block:: python 70 + 71 + def encode(data: IPLDKind, stream: None = None) -> bytes: 72 + ... 73 + 74 + Example usage: 75 + 76 + >>> dag_cbor.encode({'a': 12, 'b': 'hello!'}) 77 + b'\xa2aa\x0cabfhello!' 78 + 79 + If a ``stream`` is given, the encoded data is written to the stream and the number of bytes written is returned: 80 + 81 + .. code-block:: python 82 + 83 + def encode(data: IPLDKind, stream: BufferedIOBase) -> int: 84 + ... 85 + 86 + Example usage with a stream: 87 + 88 + >>> from io import BytesIO 89 + >>> stream = BytesIO() 90 + >>> dag_cbor.encode({'a': 12, 'b': 'hello!'}, stream=stream) 91 + 13 92 + >>> stream.getvalue() 93 + b'\xa2aa\x0cabfhello!' 94 + 95 + :param data: the DAG data to be encoded 96 + :param stream: an optional stream into which the encoded data should be written 97 + :param include_multicodec: if :obj:`True`, the encoded data is prefixed by the multicodec code for ``'dag-cbor'`` 98 + (see `multicodec.wrap <https://multiformats.readthedocs.io/en/latest/api/multiformats.multicodec.html#wrap>`_). 99 + :param normalize_strings: whether strings should be normalised prior to encoding 100 + 101 + :raises CBOREncodingError: if an :obj:`int` outside of ``range(-2**64, 2**64)`` is encountered 102 + :raises DAGCBOREncodingError: if a value of type other than :obj:`None`, :obj:`bool`, :obj:`int`, :obj:`float`, :obj:`str`, 103 + :obj:`bytes`, :obj:`list`, :obj:`dict`, or :class:`~multiformats.cid.CID` is encountered 104 + :raises DAGCBOREncodingError: if attempting to encode the special :obj:`float` values ``NaN``, ``Infinity`` and ``-Infinity`` 105 + :raises DAGCBOREncodingError: if a key of a dictionary is not a string 106 + 107 + """ 108 + validate(stream, Optional[BufferedIOBase]) 109 + validate(include_multicodec, bool) 110 + options: _EncodeOptions = {} 111 + if normalize_strings is not None: 112 + validate(normalize_strings, Literal["NFC", "NFKC", "NFD", "NFKD"]) 113 + options["normalize_strings"] = normalize_strings 114 + path = IPLDObjPath() 115 + if stream is None: 116 + internal_stream = BytesIO() 117 + if include_multicodec: 118 + internal_stream.write(_dag_cbor_code_bytes) 119 + _encode(internal_stream, data, path, options) 120 + return internal_stream.getvalue() 121 + num_bytes = 0 122 + if include_multicodec: 123 + stream.write(_dag_cbor_code_bytes) 124 + num_bytes += _dag_cbor_code_nbytes 125 + num_bytes += _encode(stream, data, path, options) 126 + return num_bytes 127 + 128 + class _EncodeOptions(TypedDict, total=False): 129 + r""" Options passed around to encoding sub-routines. """ 130 + 131 + normalize_strings: Literal["NFC", "NFKC", "NFD", "NFKD"] 132 + r""" Optional Unicode normalization to be performed on UTF-8 strings prior to byte encoding. """ 133 + 134 + def _encode(stream: BufferedIOBase, value: IPLDKind, path: IPLDObjPath, options: _EncodeOptions) -> int: 135 + # pylint: disable = too-many-return-statements, too-many-branches 136 + if isinstance(value, bool): # must go before int check 137 + # major type 0x7 (additional info 20 and 21) 138 + return _encode_bool(stream, value, path, options) 139 + if isinstance(value, int): 140 + # major types 0x0 and 0x1 141 + return _encode_int(stream, value, path, options) 142 + if isinstance(value, bytes): 143 + # major type 0x2 144 + return _encode_bytes(stream, value, path, options) 145 + if isinstance(value, str): 146 + # major type 0x3 147 + return _encode_str(stream, value, path, options) 148 + if isinstance(value, list): 149 + # major type 0x4 150 + return _encode_list(stream, value, path, options) 151 + if isinstance(value, dict): 152 + # major type 0x5 153 + return _encode_dict(stream, value, path, options) 154 + if isinstance(value, CID): 155 + # major type 0x6 156 + return _encode_cid(stream, value, path, options) 157 + if value is None: 158 + # major type 0x7 (additional info 22) 159 + return _encode_none(stream, value, path, options) 160 + if isinstance(value, float): 161 + # major type 0x7 (additional info 27) 162 + return _encode_float(stream, value, path, options) 163 + err = f"Error encoding value at {path}: value is not of IPLD kind (found type {type(value)})." 164 + raise DAGCBOREncodingError(err) 165 + 166 + def _encode_head(stream: BufferedIOBase, major_type: int, arg: int) -> int: 167 + if arg < 24: 168 + # argument value as additional info in leading byte 169 + head = struct.pack(">B", (major_type<<5)|arg) 170 + elif arg <= 255: 171 + # leading byte + 1 byte argument value (additional info = 24) 172 + head = struct.pack(">BB", (major_type<<5)|24, arg) 173 + elif arg <= 65535: 174 + # leading byte + 2 bytes argument value (additional info = 25) 175 + head = struct.pack(">BH", (major_type<<5)|25, arg) 176 + elif arg <= 4294967295: 177 + # leading byte + 4 bytes argument value (additional info = 26) 178 + head = struct.pack(">BL", (major_type<<5)|26, arg) 179 + else: 180 + # leading byte + 8 bytes argument value (additional info = 27) 181 + head = struct.pack(">BQ", (major_type<<5)|27, arg) 182 + stream.write(head) 183 + return len(head) 184 + 185 + def _encode_int(stream: BufferedIOBase, value: int, path: IPLDObjPath, options: _EncodeOptions) -> int: 186 + if value >= 18446744073709551616: 187 + # unsigned int must be < 2**64 188 + err = f"Error encoding integer value at {path}: Unsigned integer out of range." 189 + raise CBOREncodingError(err) 190 + if value < -18446744073709551616: 191 + # negative int must be >= -2**64 192 + err = f"Error encoding integer value at {path}: Negative integer out of range." 193 + raise CBOREncodingError(err) 194 + if value >= 0: 195 + # unsigned int 196 + return _encode_head(stream, 0x0, value) 197 + # negative int 198 + return _encode_head(stream, 0x1, -1-value) 199 + 200 + def _encode_bytes(stream: BufferedIOBase, value: bytes, path: IPLDObjPath, options: _EncodeOptions) -> int: 201 + num_head_bytes = _encode_head(stream, 0x2, len(value)) 202 + stream.write(value) 203 + return num_head_bytes+len(value) 204 + 205 + def _encode_str(stream: BufferedIOBase, value: str, path: IPLDObjPath, options: _EncodeOptions) -> int: 206 + if "normalize_strings" in options: 207 + value = unicodedata.normalize(options["normalize_strings"], value) 208 + utf8_value: bytes = value.encode("utf-8", errors="strict") 209 + num_head_bytes = _encode_head(stream, 0x3, len(utf8_value)) 210 + stream.write(utf8_value) 211 + return num_head_bytes+len(utf8_value) 212 + 213 + def _encode_list(stream: BufferedIOBase, value: List[Any], path: IPLDObjPath, options: _EncodeOptions) -> int: 214 + num_bytes_written = _encode_head(stream, 0x4, len(value)) 215 + for idx, item in enumerate(value): 216 + num_bytes_written += _encode(stream, item, path/idx, options) 217 + return num_bytes_written 218 + 219 + def _encode_dict(stream: BufferedIOBase, value: Dict[str, Any], path: IPLDObjPath, options: _EncodeOptions) -> int: 220 + _check_key_compliance(value, path) 221 + if "normalize_strings" in options: 222 + nf = options["normalize_strings"] 223 + value = {unicodedata.normalize(nf, k): v for k, v in value.items()} 224 + utf8key_val_pairs = [(k, k.encode("utf-8", errors="strict"), v) 225 + for k, v in value.items()] 226 + # 1. sort keys canonically: 227 + sorted_utf8key_val_pairs = sorted(utf8key_val_pairs, key=lambda i: (len(i[1]), i[1])) 228 + # 2. encode key-value pairs (keys already utf-8 encoded): 229 + num_bytes_written = _encode_head(stream, 0x5, len(value)) 230 + for k, utf8k, v in sorted_utf8key_val_pairs: 231 + num_bytes_written += _encode_head(stream, 0x3, len(utf8k)) 232 + stream.write(utf8k) 233 + num_bytes_written += len(utf8k) 234 + num_bytes_written += _encode(stream, v, path/k, options) 235 + return num_bytes_written 236 + 237 + def _encode_cid(stream: BufferedIOBase, value: CID, path: IPLDObjPath, options: _EncodeOptions) -> int: 238 + num_bytes_written = _encode_head(stream, 0x6, 42) 239 + num_bytes_written += _encode_bytes(stream, b"\0" + bytes(value), path, options) 240 + return num_bytes_written 241 + 242 + def _encode_bool(stream: BufferedIOBase, value: bool, path: IPLDObjPath, options: _EncodeOptions) -> int: 243 + return _encode_head(stream, 0x7, 21 if value else 20) 244 + 245 + def _encode_none(stream: BufferedIOBase, value: None, path: IPLDObjPath, options: _EncodeOptions) -> int: 246 + return _encode_head(stream, 0x7, 22) 247 + 248 + def _encode_float(stream: BufferedIOBase, value: float, path: IPLDObjPath, options: _EncodeOptions) -> int: 249 + if math.isnan(value): 250 + err = f"Error encoding float value at {path}: NaN is not allowed." 251 + raise DAGCBOREncodingError(err) 252 + if math.isinf(value): 253 + s = "" if value > 0 else "-" 254 + err = f"Error encoding float value at {path}: {s}Infinity is not allowed." 255 + raise DAGCBOREncodingError(err) 256 + # special head, with double encoding for 4B argument value 257 + head = struct.pack(">Bd", (0x7<<5)|27, value) 258 + stream.write(head) 259 + return len(head) 260 + 261 + def _check_key_compliance(value: Dict[str, Any], path: Optional[IPLDObjPath] = None) -> None: 262 + """ Check keys for DAG-CBOR compliance. """ 263 + for idx, k in enumerate(value.keys()): 264 + if not isinstance(k, str): 265 + err = "" if path is None else f"Error encoding value of map kind at {path}: " 266 + err += f"key for key-value pair at position {idx} is not a string." 267 + raise DAGCBOREncodingError(err) 268 + 269 + def _canonical_order_dict(value: Dict[str, Any]) -> Dict[str, Any]: 270 + utf8key_key_val_pairs = [(k.encode("utf-8", errors="strict"), k, v) for k, v in value.items()] 271 + sorted_utf8key_key_val_pairs = sorted(utf8key_key_val_pairs, key=lambda i: (len(i[0]), i[0])) 272 + return {k: v for _, k, v in sorted_utf8key_key_val_pairs}
+23
vendor/git/dag-cbor/dag_cbor/encoding/err.py
··· 1 + r""" 2 + Errors for the :mod:`dag_cbor.encoding` submodule. 3 + """ 4 + 5 + class CBORError(Exception): 6 + """ 7 + Parent class for all errors due to the CBOR specification. 8 + """ 9 + 10 + class DAGCBORError(CBORError): 11 + """ 12 + Parent class for all errors due to the DAG-CBOR specification. 13 + """ 14 + 15 + class CBOREncodingError(CBORError): 16 + """ 17 + Class for encoding errors due to the CBOR specification. 18 + """ 19 + 20 + class DAGCBOREncodingError(CBOREncodingError, DAGCBORError): 21 + """ 22 + Class for encoding errors due to the DAG-CBOR specification. 23 + """
+292
vendor/git/dag-cbor/dag_cbor/ipld.py
··· 1 + r""" 2 + Types and functions relating to the IPLD data model `IPLD data model <https://ipld.io/docs/data-model/>`_. 3 + """ 4 + 5 + # Part of the dag-cbor library. 6 + # Copyright (C) 2023 Hashberg Ltd 7 + 8 + # This library is free software; you can redistribute it and/or 9 + # modify it under the terms of the GNU Lesser General Public 10 + # License as published by the Free Software Foundation; either 11 + # version 2.1 of the License, or (at your option) any later version. 12 + 13 + # This library is distributed in the hope that it will be useful, 14 + # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 + # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 + # Lesser General Public License for more details. 17 + 18 + # You should have received a copy of the GNU Lesser General Public 19 + # License along with this library; if not, write to the Free Software 20 + # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 21 + # USA 22 + 23 + from __future__ import annotations # See https://peps.python.org/pep-0563/ 24 + 25 + from typing import ClassVar, Dict, Iterator, List, MutableMapping, overload, Sequence, Tuple, Union 26 + from weakref import WeakValueDictionary 27 + 28 + from typing_validation import validate 29 + 30 + from multiformats import CID 31 + 32 + IPLDScalarKind = Union[None, bool, int, float, str, bytes, CID] 33 + r""" 34 + Python type alias for scalar `kinds <https://ipld.io/docs/data-model/kinds/>`_ in the IPLD data model: 35 + 36 + - :obj:`None` for the `Null kind <https://ipld.io/docs/data-model/kinds/#null-kind>`_ 37 + - :obj:`bool` for the `Boolean kind <https://ipld.io/docs/data-model/kinds/#boolean-kind>`_ 38 + - :obj:`int` for the `Integer kind <https://ipld.io/docs/data-model/kinds/#integer-kind>`_ 39 + - :obj:`float` for the `Float kind <https://ipld.io/docs/data-model/kinds/#float-kind>`_ 40 + - :obj:`str` for the `String kind <https://ipld.io/docs/data-model/kinds/#string-kind>`_ 41 + - :obj:`bytes` for the `Bytes kind <https://ipld.io/docs/data-model/kinds/#bytes-kind>`_ 42 + - :class:`CID` for the `Link kind <https://ipld.io/docs/data-model/kinds/#link-kind>`_ 43 + 44 + """ 45 + 46 + IPLDKind = Union[IPLDScalarKind, List["IPLDKind"], Dict[str, "IPLDKind"]] 47 + r""" 48 + Python type alias for `kinds <https://ipld.io/docs/data-model/kinds/>`_ in the IPLD data model: 49 + 50 + - :obj:`None` for the `Null kind <https://ipld.io/docs/data-model/kinds/#null-kind>`_ 51 + - :obj:`bool` for the `Boolean kind <https://ipld.io/docs/data-model/kinds/#boolean-kind>`_ 52 + - :obj:`int` for the `Integer kind <https://ipld.io/docs/data-model/kinds/#integer-kind>`_ 53 + - :obj:`float` for the `Float kind <https://ipld.io/docs/data-model/kinds/#float-kind>`_ 54 + - :obj:`str` for the `String kind <https://ipld.io/docs/data-model/kinds/#string-kind>`_ 55 + - :obj:`bytes` for the `Bytes kind <https://ipld.io/docs/data-model/kinds/#bytes-kind>`_ 56 + - :class:`CID` for the `Link kind <https://ipld.io/docs/data-model/kinds/#link-kind>`_ 57 + - :obj:`List` for the `List kind <https://ipld.io/docs/data-model/kinds/#list-kind>`_ 58 + - :obj:`Dict` for the `Map kind <https://ipld.io/docs/data-model/kinds/#map-kind>`_ 59 + 60 + """ 61 + 62 + IPLDObjPathSegment = Union[int, str] 63 + r""" 64 + An individual segment in a :class:`IPLDObjPath` within a IPLD value (see :obj:`IPLDKind` for the ). A segment can be an :obj:`int` or a :obj:`str`: 65 + 66 + - an :obj:`int` segment is a position, indexing an item in a value of List :obj:`IPLDKind` (a :obj:`List` in Python) 67 + - an :obj:`str` segment is a key, indexing a value in a value of Map :obj:`IPLDKind` (a :obj:`Dict` in Python) 68 + 69 + """ 70 + 71 + _IPLDObjPathSegments = Tuple[IPLDObjPathSegment, ...] 72 + r""" 73 + Short type alias for multiple segments. 74 + """ 75 + 76 + class IPLDObjPath(Sequence[IPLDObjPathSegment]): 77 + r""" 78 + Path within an object of :obj:`IPLDKind`, as a sequence of :obj:`IPLDObjPathSegment`. 79 + Paths are immutable and hashable, and a path is a :obj:`Sequence` of the segments that constitute it. 80 + """ 81 + 82 + _instances: ClassVar[MutableMapping[_IPLDObjPathSegments, IPLDObjPath]] = WeakValueDictionary() 83 + 84 + @staticmethod 85 + def parse(path_str: str) -> IPLDObjPath: 86 + r""" 87 + Parses a :class:`IPLDObjPath` from a string representation where segments are separated by `"/"`, such as that returned by 88 + :meth:`IPLDObjPath.__repr__`. 89 + """ 90 + if path_str.startswith("IPLDObjPath()"): 91 + path_str = path_str[6:] 92 + if not path_str.startswith("/"): 93 + raise ValueError("Path must start with '/' or 'IPLDObjPath()/'.") 94 + segs: List[IPLDObjPathSegment] = [] 95 + seg_str_list = path_str[1:].split("/") 96 + for idx, seg_str in enumerate(seg_str_list): 97 + if seg_str.startswith("'"): 98 + if not seg_str.endswith("'"): 99 + raise ValueError(f"At segment {idx}: opening single quote without closing single quote.") 100 + segs.append(seg_str[1:-1]) 101 + elif seg_str.startswith('"'): 102 + if not seg_str.endswith('"'): 103 + raise ValueError(f"At segment {idx}: opening double quote without closing double quote.") 104 + segs.append(seg_str[1:-1]) 105 + else: 106 + if not seg_str.isnumeric(): 107 + raise ValueError(f"At segment {idx}: segment is unquoted and not numeric.") 108 + segs.append(int(seg_str)) 109 + return IPLDObjPath._new_instance(tuple(segs)) 110 + 111 + @staticmethod 112 + def _new_instance(segments: Tuple[IPLDObjPathSegment, ...]) -> IPLDObjPath: 113 + r""" 114 + Returns an instance of :class:`IPLDObjPath` with given segments, without performing any validation. 115 + """ 116 + instance = IPLDObjPath._instances.get(segments) 117 + if instance is None: 118 + instance = object.__new__(IPLDObjPath) 119 + instance._segments = segments 120 + IPLDObjPath._instances[segments] = instance 121 + return instance 122 + 123 + _segments: _IPLDObjPathSegments 124 + 125 + def __new__(cls, *segments: IPLDObjPathSegment) -> IPLDObjPath: 126 + r""" Constructor for :class:`IPLDObjPath`. """ 127 + validate(segments, _IPLDObjPathSegments) 128 + return IPLDObjPath._new_instance(segments) 129 + 130 + def access(self, value: IPLDKind) -> IPLDKind: 131 + r""" 132 + Accesses the sub-value at this path in the given IPLD value. 133 + Can be written more expressively as `self >> value`, see :meth:`IPLDObjPath.__rshift__`. 134 + """ 135 + return _access(self, value) 136 + 137 + def __truediv__(self, other: Union[IPLDObjPathSegment, IPLDObjPath]) -> IPLDObjPath: 138 + r""" 139 + The `/` operator can be used to create paths by concatenating segments. Below we use `_` as a suggestive name for an empty path, acting as root: 140 + 141 + >>> _ = IPLDObjPath() 142 + >>> p = _/2/'red' 143 + >>> p 144 + /2/'red' 145 + 146 + Concatenating an existing path with one or more segments returns a new path, extended by the given segments: 147 + 148 + >>> p/3 149 + /2/'red'/3 150 + >>> p/0/'blue' 151 + /2/'red'/0/'blue' 152 + 153 + Concatenating two paths yields a new path, where the end of the first path is treated as the root for the second: 154 + 155 + >>> q = _/0/'blue' 156 + >>> p/q 157 + /2/'red'/0/'blue' 158 + """ 159 + if isinstance(other, (int, str)): 160 + return IPLDObjPath._new_instance(self._segments+(other,)) 161 + if isinstance(other, IPLDObjPath): 162 + return IPLDObjPath._new_instance(self._segments+other._segments) 163 + return NotImplemented 164 + 165 + def __rtruediv__(self, other: Union[IPLDObjPathSegment, IPLDObjPath]) -> IPLDObjPath: 166 + r""" 167 + It is possible to prepend a single segment at a time to an existing path using `/` (a new path is returned): 168 + 169 + >>> _ = IPLDObjPath() 170 + >>> p = _/2/'red' 171 + >>> 1/p 172 + /1/2/'red' 173 + 174 + Prepending multiple segments requires brackets (because the `/` operator associates to the left): 175 + 176 + >>> 0/(1/p) 177 + /0/1/2/'red' 178 + """ 179 + if isinstance(other, (int, str)): 180 + return IPLDObjPath._new_instance((other,)+self._segments) 181 + return NotImplemented 182 + 183 + def __len__(self) -> int: 184 + return len(self._segments) 185 + 186 + def __iter__(self) -> Iterator[IPLDObjPathSegment]: 187 + return iter(self._segments) 188 + 189 + @overload 190 + def __getitem__(self, idx: int) -> IPLDObjPathSegment: 191 + ... 192 + 193 + @overload 194 + def __getitem__(self, idx: slice) -> IPLDObjPath: 195 + ... 196 + 197 + def __getitem__(self, idx: Union[int, slice]) -> Union[IPLDObjPathSegment, IPLDObjPath]: 198 + if isinstance(idx, int): 199 + return self._segments[idx] 200 + return IPLDObjPath._new_instance(self._segments[idx]) 201 + 202 + def __le__(self, other: IPLDObjPath) -> bool: 203 + r""" 204 + The `<` and `<=` operators can be used to check whether a path is a (strict) sub-path of another path, starting at the same root: 205 + 206 + >>> _ = IPLDObjPath() 207 + >>> p = _/0/'red' 208 + >>> q = p/1/2 209 + >>> p == q 210 + False 211 + >>> p <= q 212 + True 213 + >>> p < q 214 + True 215 + 216 + """ 217 + if isinstance(other, IPLDObjPath): 218 + return len(self) <= len(other) and all(a == b for a, b in zip(self, other)) 219 + return NotImplemented 220 + 221 + def __lt__(self, other: IPLDObjPath) -> bool: 222 + r""" See :meth:`IPLDObjPath.__le__`. """ 223 + if isinstance(other, IPLDObjPath): 224 + return len(self) < len(other) and all(a == b for a, b in zip(self, other)) 225 + return NotImplemented 226 + 227 + def __repr__(self) -> str: 228 + r""" 229 + .. code-block:: python 230 + 231 + return "/"+"/".join(repr(seg) for seg in self) 232 + """ 233 + return "/"+"/".join(repr(seg) for seg in self) 234 + 235 + def __rshift__(self, value: IPLDKind) -> IPLDKind: 236 + r""" 237 + Accesses the sub-value at this path in the given IPLD value: 238 + 239 + >>> _ = IPLDObjPath() 240 + >>> _ >> [0, False, {"a": b"hello", "b": "bye"}] 241 + [0, False, {'a': b'hello', 'b': 'bye'}] 242 + >>> _/2 >> [0, False, {"a": b"hello", "b": "bye"}] 243 + {'a': b'hello', 'b': 'bye'} 244 + >>> _/2/'b' >> [0, False, {"a": b"hello", "b": "bye"}] 245 + 'bye' 246 + 247 + :raises ValueError: if attempting to access a sub-value in a value of :obj:`IPLDScalarKind` 248 + :raises ValueError: if attempting to access a sub-value indexed by a :obj:`str` segment in a value of list :obj:`IPLDKind` (a Python :obj:`List`) 249 + :raises ValueError: if attempting to access a sub-value keyed by a :obj:`int` segment in a value of map :obj:`IPLDKind` (a Python :obj:`Dict`) 250 + :raises IndexError: if attempting to access a sub-value in a value of list kind, where the :obj:`int` segment is not a valid index for the list 251 + :raises KeyError: if attempting to access a sub-value in a value of map kind, where the :obj:`str` segment is not a valid key for the map 252 + :raises TypeError: if any of the sub-values along the path is not of IPLD :obj:`IPLDKind` at the top level 253 + """ 254 + return _access(self, value) 255 + 256 + 257 + _scalar_kinds = (type(None), bool, int, float, str, bytes, CID) 258 + _recursive_kinds = (list, dict) 259 + 260 + def _access(path: IPLDObjPath, value: IPLDKind, idx: int = 0) -> IPLDKind: 261 + r""" 262 + Implementation for :func:`IPLDObjPath.access` and :func:`IPLDObjPath.__rshift__`. 263 + """ 264 + if isinstance(value, _scalar_kinds): 265 + if len(path) > idx: 266 + err = f"Error trying to access value at {path[:idx+1]}: value at {path[:idx]} is of scalar kind." 267 + raise ValueError(err) 268 + return value 269 + if isinstance(value, list): 270 + if idx >= len(path): 271 + return value 272 + key = path[idx] 273 + if not isinstance(key, int): 274 + err = f"Error trying to access value at {path[:idx+1]}: value at {path[:idx]} is of list kind, but segment {repr(path[idx])} is not integer." 275 + raise ValueError(err) 276 + if key not in range(len(value)): 277 + err = f"Error trying to access value at {path[:idx+1]}: segment {repr(path[idx])} is not a valid index for list at {path[:idx]}." 278 + raise IndexError(err) 279 + return _access(path, value[key], idx + 1) 280 + if isinstance(value, dict): 281 + if idx >= len(path): 282 + return value 283 + key = path[idx] 284 + if not isinstance(key, str): 285 + err = f"Error trying to access value at {path[:idx+1]}: value at {path[:idx]} is of map kind, but segment {repr(path[idx])} is not a string." 286 + raise ValueError(err) 287 + if key not in value: 288 + err = f"Error trying to access value at {path[:idx+1]}: segment {repr(path[idx])} is not a valid key for map at {path[:idx]}." 289 + raise KeyError(err) 290 + return _access(path, value[key], idx + 1) 291 + err = f"Error trying to access value at {path[:idx+1]}: value at {path[:idx]} is not of IPLD kind (found type {type(value)})." 292 + raise TypeError(err)
vendor/git/dag-cbor/dag_cbor/py.typed

This is a binary file and will not be displayed.

+601
vendor/git/dag-cbor/dag_cbor/random.py
··· 1 + """ 2 + Functions to generate random data. 3 + """ 4 + # pylint: disable = global-statement 5 + 6 + from __future__ import annotations # See https://peps.python.org/pep-0563/ 7 + 8 + from contextlib import contextmanager 9 + import math 10 + from random import Random # pylint: disable = import-self 11 + import sys 12 + from types import MappingProxyType 13 + from typing import Any, Dict, Iterator, List, Mapping, Optional, Tuple 14 + from typing_validation import validate 15 + 16 + from multiformats import multicodec, multibase, multihash, CID 17 + 18 + from .ipld import IPLDKind 19 + from .encoding import encode, _canonical_order_dict 20 + 21 + _min_int = -18446744073709551616 22 + _max_int = 18446744073709551615 23 + _min_float = -sys.float_info.max 24 + _max_float = sys.float_info.max 25 + _min_codepoint = 0x00 26 + _max_codepoint = 0x10FFFF 27 + 28 + _default_options: Dict[str, Any] = { 29 + "min_int": -100, 30 + "max_int": 100, 31 + "min_bytes": 0, 32 + "max_bytes": 8, 33 + "min_chars": 0, 34 + "max_chars": 8, 35 + "min_codepoint": 0x21, 36 + "max_codepoint": 0x7e, 37 + "min_len": 0, 38 + "max_len": 8, 39 + "max_nesting": 2, 40 + "canonical": True, 41 + "min_float": -100.0, 42 + "max_float": 100.0, 43 + "float_decimals": 3, 44 + "include_cid": True, 45 + } 46 + 47 + _options = _default_options 48 + _rand = Random(0) 49 + 50 + def reset_options() -> None: 51 + """ 52 + Resets random generation options to their default values. 53 + """ 54 + global _options 55 + global _rand 56 + _options = _default_options 57 + _rand = Random(0) 58 + 59 + def default_options() -> Mapping[str, Any]: 60 + """ 61 + Readonly view of the default random generation options. 62 + """ 63 + return MappingProxyType(_default_options) 64 + 65 + def get_options() -> Mapping[str, Any]: 66 + """ 67 + Readonly view of the current random generation options. 68 + """ 69 + return MappingProxyType(_options) 70 + 71 + @contextmanager 72 + def options(*, 73 + seed: Optional[int] = None, 74 + min_int: Optional[int] = None, 75 + max_int: Optional[int] = None, 76 + min_bytes: Optional[int] = None, 77 + max_bytes: Optional[int] = None, 78 + min_chars: Optional[int] = None, 79 + max_chars: Optional[int] = None, 80 + min_codepoint: Optional[int] = None, 81 + max_codepoint: Optional[int] = None, 82 + min_len: Optional[int] = None, 83 + max_len: Optional[int] = None, 84 + max_nesting: Optional[int] = None, 85 + canonical: Optional[bool] = None, 86 + min_float: Optional[float] = None, 87 + max_float: Optional[float] = None, 88 + float_decimals: Optional[int] = None, 89 + include_cid: Optional[bool] = None,) -> Iterator[None]: 90 + """ 91 + Returns with-statement context manager for temporary option setting: 92 + 93 + .. code-block:: python 94 + 95 + with options(**options): 96 + for value in rand_data(num_samples): 97 + ... 98 + 99 + Options available: 100 + 101 + .. code-block:: 102 + 103 + seed: int # set new random number generator, with this seed 104 + min_int: int # smallest `int` value 105 + max_int: int # largest `int` value 106 + min_bytes: int # min length of `bytes` value 107 + max_bytes: int # max length of `bytes` value 108 + min_chars: int # min length of `str` value 109 + max_chars: int # max length of `str` value 110 + min_codepoint: int # min utf-8 codepoint in `str` value 111 + max_codepoint: int # max utf-8 codepoint in `str` value 112 + min_len: int # min length of `list` and `dict` values 113 + max_len: int # max length of `list` and `dict` values 114 + max_nesting: int # max nesting of collections 115 + canonical: bool # whether `dict` values have canonically ordered keys 116 + min_float: float # smallest `float` value 117 + max_float: float # largest `float` value 118 + float_decimals: int # number of decimals to keep in floats 119 + include_cid: bool # whether to generate CID values 120 + """ 121 + # pylint: disable = too-many-locals, too-many-arguments 122 + global _options 123 + global _rand 124 + _old_options = _options 125 + _old_rand = _rand 126 + try: 127 + set_options(seed=seed, 128 + min_int=min_int, max_int=max_int, 129 + min_bytes=min_bytes, max_bytes=max_bytes, 130 + min_chars=min_chars, max_chars=max_chars, 131 + min_codepoint=min_codepoint, max_codepoint=max_codepoint, 132 + min_len=min_len, max_len=max_len, 133 + max_nesting=max_nesting, canonical=canonical, 134 + min_float=min_float, max_float=max_float, 135 + float_decimals=float_decimals, include_cid=include_cid) 136 + yield 137 + finally: 138 + _options = _old_options 139 + _rand = _old_rand 140 + 141 + def set_options(*, 142 + seed: Optional[int] = None, 143 + min_int: Optional[int] = None, 144 + max_int: Optional[int] = None, 145 + min_bytes: Optional[int] = None, 146 + max_bytes: Optional[int] = None, 147 + min_chars: Optional[int] = None, 148 + max_chars: Optional[int] = None, 149 + min_codepoint: Optional[int] = None, 150 + max_codepoint: Optional[int] = None, 151 + min_len: Optional[int] = None, 152 + max_len: Optional[int] = None, 153 + max_nesting: Optional[int] = None, 154 + canonical: Optional[bool] = None, 155 + min_float: Optional[float] = None, 156 + max_float: Optional[float] = None, 157 + float_decimals: Optional[int] = None, 158 + include_cid: Optional[bool] = None,) -> None: 159 + """ 160 + Permanently sets random generation options. See :func:`options` for the available options. 161 + 162 + """ 163 + # pylint: disable = too-many-branches, too-many-locals, too-many-statements, too-many-arguments 164 + for iarg in (seed, min_int, max_int, min_bytes, max_bytes, min_chars, max_chars, 165 + min_codepoint, max_codepoint, min_len, max_len, max_nesting, float_decimals): 166 + validate(iarg, Optional[int]) 167 + for barg in (canonical, include_cid): 168 + validate(barg, Optional[bool]) 169 + for farg in (min_float, max_float): 170 + validate(farg, Optional[float]) 171 + global _options 172 + global _rand 173 + # set newly passed options 174 + _new_options: Dict[str, Any] = {} 175 + if seed is not None: 176 + _rand = Random(seed) 177 + if min_int is not None: 178 + if min_int < _min_int: 179 + raise ValueError("Value for min_int is not a valid CBOR integer.") 180 + _new_options["min_int"] = min_int 181 + if max_int is not None: 182 + if max_int > _max_int: 183 + raise ValueError("Value for max_int is not a valid CBOR integer.") 184 + _new_options["max_int"] = max_int 185 + if min_bytes is not None: 186 + if min_bytes < 0: 187 + raise ValueError("Value for min_bytes is negative.") 188 + _new_options["min_bytes"] = min_bytes 189 + if max_bytes is not None: 190 + if max_bytes < 0: 191 + raise ValueError("Value for max_bytes is negative.") 192 + _new_options["max_bytes"] = max_bytes 193 + if min_chars is not None: 194 + if min_chars < 0: 195 + raise ValueError("Value for min_chars is negative.") 196 + _new_options["min_chars"] = min_chars 197 + if max_chars is not None: 198 + if max_chars < 0: 199 + raise ValueError("Value for max_chars is negative.") 200 + _new_options["max_chars"] = max_chars 201 + if min_codepoint is not None: 202 + if min_codepoint < _min_codepoint or min_codepoint > _max_codepoint: 203 + raise ValueError("Value for min_codepoint not a valid utf-8 codepoint.") 204 + _new_options["min_codepoint"] = min_codepoint 205 + if max_codepoint is not None: 206 + if max_codepoint < _min_codepoint or max_codepoint > _max_codepoint: 207 + raise ValueError("Value for max_codepoint not a valid utf-8 codepoint.") 208 + _new_options["max_codepoint"] = max_codepoint 209 + if min_len is not None: 210 + if min_len < 0: 211 + raise ValueError("Value for min_len is negative.") 212 + _new_options["min_len"] = min_len 213 + if max_len is not None: 214 + if max_len < 0: 215 + raise ValueError("Value for max_len is negative.") 216 + _new_options["max_len"] = max_len 217 + if max_nesting is not None: 218 + if max_nesting < 0: 219 + raise ValueError("Value for max_nesting is negative.") 220 + _new_options["max_nesting"] = max_nesting 221 + if canonical is not None: 222 + _new_options["canonical"] = canonical 223 + if min_float is not None: 224 + if math.isnan(min_float) or math.isinf(min_float): 225 + raise ValueError("Value for min_float is not a valid CBOR float.") 226 + _new_options["min_float"] = min_float 227 + if max_float is not None: 228 + if math.isnan(max_float) or math.isinf(max_float): 229 + raise ValueError("Value for max_float is not a valid CBOR float.") 230 + _new_options["max_float"] = max_float 231 + if float_decimals is not None: 232 + if float_decimals < 0: 233 + raise ValueError("Value for float_decimals is negative.") 234 + _new_options["float_decimals"] = float_decimals 235 + if include_cid is not None: 236 + _new_options["include_cid"] = include_cid 237 + # pass-through other options with former values 238 + for k, v in _options.items(): 239 + if k not in _new_options: 240 + _new_options[k] = v 241 + # check compatibility conditions 242 + if _new_options["min_bytes"] > _new_options["max_bytes"]: 243 + raise ValueError("Value for min_bytes is larger than value for max_bytes.") 244 + if _new_options["min_chars"] > _new_options["max_chars"]: 245 + raise ValueError("Value for min_chars is larger than value for max_chars.") 246 + if _new_options["min_codepoint"] > _new_options["max_codepoint"]: 247 + raise ValueError("Value for min_codepoint is larger than value for max_codepoint.") 248 + if _new_options["min_len"] > _new_options["max_len"]: 249 + raise ValueError("Value for min_len is larger than value for max_len.") 250 + # update options 251 + _options = _new_options 252 + 253 + 254 + def rand_data(n: Optional[int] = None, *, max_nesting: Optional[int] = None) -> Iterator[IPLDKind]: 255 + r""" 256 + Generates a stream of random data. 257 + 258 + :param n: the number of samples to be yielded; if :obj:`None`, an infinite stream is yielded 259 + :param max_nesting: the maximum nesting level for containers; if :obj:`None`, value from :func:`get_options` is used 260 + 261 + Maximum nesting level for containers: 262 + 263 + - the integer value -1 means no containers will be generated 264 + - integer values >= 0 mean that containers will be generated, with items generated by ``random_data(max_nesting=max_nesting-1)`` 265 + - no other values are valid 266 + 267 + """ 268 + validate(n, Optional[int]) 269 + validate(max_nesting, Optional[int]) 270 + return _rand_data(n, max_nesting=max_nesting) 271 + 272 + def _rand_data(n: Optional[int] = None, *, max_nesting: Optional[int] = None) -> Iterator[IPLDKind]: 273 + if n is not None and n < 0: 274 + raise ValueError() 275 + if max_nesting is None: 276 + max_nesting = _options["max_nesting"] 277 + elif max_nesting < -1: 278 + raise ValueError("Value for max_nesting must be >= -1 (with -1 indicating no containers).") 279 + include_cid = _options["include_cid"] 280 + data_generators: List[Iterator[Any]] = [ 281 + _rand_list(max_nesting=max_nesting) if max_nesting >= 0 else iter([]), 282 + _rand_dict(max_nesting=max_nesting) if max_nesting >= 0 else iter([]), 283 + _rand_int(), 284 + _rand_bytes(), 285 + _rand_str(), 286 + _rand_bool_none(), 287 + _rand_float(), 288 + _rand_cid() 289 + ] 290 + num_data_generators = len(data_generators) if include_cid else len(data_generators)-1 291 + i = 0 292 + while n is None or i < n: 293 + if max_nesting == -1: 294 + # exclude containers 295 + datatype = _rand.randrange(0x2, num_data_generators) 296 + else: 297 + # include containers 298 + datatype = _rand.randrange(0x0, num_data_generators) 299 + try: 300 + yield next(data_generators[datatype]) 301 + except StopIteration as e: 302 + raise RuntimeError("All random streams are infinite, this should not happen.") from e 303 + i += 1 304 + 305 + def rand_list(n: Optional[int] = None, *, length: Optional[int] = None, max_nesting: Optional[int] = None) -> Iterator[List[Any]]: 306 + """ 307 + Generates a stream of random :obj:`list` data. 308 + 309 + :param n: the number of samples to be yielded; if :obj:`None`, an infinite stream is yielded 310 + :param length: size for the lists; if :obj:`None`, a random value is sampled according to the :func:`options` 311 + :param max_nesting: the maximum nesting level for containers; if :obj:`None`, value from :func:`get_options` is used 312 + """ 313 + validate(n, Optional[int]) 314 + validate(length, Optional[int]) 315 + validate(max_nesting, Optional[int]) 316 + return _rand_list(n, length=length, max_nesting=max_nesting) 317 + 318 + def _rand_list(n: Optional[int] = None, *, length: Optional[int] = None, max_nesting: Optional[int] = None) -> Iterator[List[Any]]: 319 + if n is not None and n < 0: 320 + raise ValueError() 321 + if length is not None and length < 0: 322 + raise ValueError() 323 + if max_nesting is None: 324 + max_nesting = _options["max_nesting"] 325 + elif max_nesting < 0: 326 + raise ValueError("Value for max_nesting is negative.") 327 + min_len = _options["min_len"] 328 + max_len = _options["max_len"] 329 + i = 0 330 + while n is None or i < n: 331 + _length = length if length is not None else _rand.randint(min_len, max_len) 332 + yield list(_rand_data(_length, max_nesting=max_nesting-1)) 333 + i += 1 334 + 335 + def rand_dict(n: Optional[int] = None, *, length: Optional[int] = None, max_nesting: Optional[int] = None) -> Iterator[Dict[str, Any]]: 336 + """ 337 + Generates a stream of random :obj:`dict` data. 338 + 339 + :param n: the number of samples to be yielded; if :obj:`None`, an infinite stream is yielded 340 + :param length: size for the dicts; if :obj:`None`, a random value is sampled according to the :func:`options` 341 + :param max_nesting: the maximum nesting level for containers; if :obj:`None`, value from :func:`get_options` is used 342 + """ 343 + validate(n, Optional[int]) 344 + validate(length, Optional[int]) 345 + validate(max_nesting, Optional[int]) 346 + return _rand_dict(n, length=length, max_nesting=max_nesting) 347 + 348 + def _rand_dict(n: Optional[int] = None, *, length: Optional[int] = None, max_nesting: Optional[int] = None) -> Iterator[Dict[str, Any]]: 349 + # pylint: disable = too-many-locals, too-many-branches 350 + if n is not None and n < 0: 351 + raise ValueError() 352 + if length is not None and length < 0: 353 + raise ValueError() 354 + if max_nesting is None: 355 + max_nesting = _options["max_nesting"] 356 + elif max_nesting < 0: 357 + raise ValueError("Value for max_nesting is negative.") 358 + min_len = _options["min_len"] 359 + max_len = _options["max_len"] 360 + canonical = _options["canonical"] 361 + min_chars = _options["min_chars"] 362 + max_chars = _options["max_chars"] 363 + max_codepoint = _options["max_codepoint"] 364 + num_codepoints = max_codepoint-_options["min_codepoint"] 365 + i = 0 366 + while n is None or i < n: 367 + _length = length if length is not None else _rand.randint(min_len, max_len) 368 + # check whether we have enough distinct strings to generate a random dictionary of desired length 369 + if num_codepoints == 1: 370 + num_strings = max_chars-min_chars+1 371 + else: 372 + num_strings = (num_codepoints**min_chars)*(num_codepoints**(max_chars-min_chars+1)-1)//(num_codepoints-1) 373 + if num_strings < _length: 374 + raise ValueError(f"Not enough distinct strings available to make a dictionary of length {_length}") 375 + # generate distinct dictionary keys 376 + if num_codepoints == 1: 377 + key_lengths = _rand.sample(range(min_chars, max_chars+1), _length) 378 + keys = [chr(max_codepoint)*l for l in key_lengths] 379 + else: 380 + keys = [] 381 + keys_set = set() 382 + str_generator = _rand_str() 383 + while len(keys) < _length: 384 + try: 385 + s = next(str_generator) 386 + except StopIteration as e: 387 + raise RuntimeError("Random string stream is infinite, this should not happen.") from e 388 + if s not in keys_set: 389 + keys.append(s) 390 + keys_set.add(s) 391 + # generate dictionary 392 + raw_dict = dict(zip(keys, _rand_data(_length, max_nesting=max_nesting-1))) 393 + if canonical: 394 + yield _canonical_order_dict(raw_dict) 395 + else: 396 + yield raw_dict 397 + i += 1 398 + 399 + def rand_int(n: Optional[int] = None) -> Iterator[int]: 400 + """ 401 + Generates a stream of random :obj:`int` data. 402 + 403 + :param n: the number of samples to be yielded; if :obj:`None`, an infinite stream is yielded 404 + """ 405 + validate(n, Optional[int]) 406 + return _rand_int(n) 407 + 408 + def _rand_int(n: Optional[int] = None) -> Iterator[int]: 409 + if n is not None and n < 0: 410 + raise ValueError() 411 + min_int = _options["min_int"] 412 + max_int = _options["max_int"] 413 + i = 0 414 + while n is None or i < n: 415 + yield _rand.randint(min_int, max_int) 416 + i += 1 417 + 418 + def rand_bytes(n: Optional[int] = None, *, length: Optional[int] = None) -> Iterator[bytes]: 419 + """ 420 + Generates a stream of random :obj:`bytes` data. 421 + 422 + :param n: the number of samples to be yielded; if :obj:`None`, an infinite stream is yielded 423 + :param length: length of the bytestrings; if :obj:`None`, a random value is sampled according to the :func:`options` 424 + """ 425 + validate(n, Optional[int]) 426 + validate(length, Optional[int]) 427 + return _rand_bytes(n, length=length) 428 + 429 + def _rand_bytes(n: Optional[int] = None, *, length: Optional[int] = None) -> Iterator[bytes]: 430 + if n is not None and n < 0: 431 + raise ValueError() 432 + if length is not None and length < 0: 433 + raise ValueError() 434 + min_bytes = _options["min_bytes"] 435 + max_bytes = _options["max_bytes"] 436 + i = 0 437 + while n is None or i < n: 438 + _length = length if length is not None else _rand.randint(min_bytes, max_bytes) 439 + yield bytes([_rand.randint(0, 255) for _ in range(_length)]) 440 + i += 1 441 + 442 + def rand_str(n: Optional[int] = None, *, length: Optional[int] = None) -> Iterator[str]: 443 + """ 444 + Generates a stream of random :obj:`str` data. 445 + 446 + :param n: the number of samples to be yielded; if :obj:`None`, an infinite stream is yielded 447 + :param length: length of the strings; if :obj:`None`, a random value is sampled according to the :func:`options` 448 + """ 449 + validate(n, Optional[int]) 450 + validate(length, Optional[int]) 451 + return _rand_str(n, length=length) 452 + 453 + def _rand_str(n: Optional[int] = None, *, length: Optional[int] = None) -> Iterator[str]: 454 + if n is not None and n < 0: 455 + raise ValueError() 456 + if length is not None and length < 0: 457 + raise ValueError() 458 + min_chars = _options["min_chars"] 459 + max_chars = _options["max_chars"] 460 + min_codepoint = _options["min_codepoint"] 461 + max_codepoint = _options["max_codepoint"] 462 + i = 0 463 + while n is None or i < n: 464 + _length = length if length is not None else _rand.randint(min_chars, max_chars) 465 + codepoints = [_rand.randint(min_codepoint, max_codepoint) for _ in range(_length)] 466 + try: 467 + string = "".join(chr(c) for c in codepoints) 468 + string.encode("utf-8", errors="strict") 469 + yield string 470 + i += 1 471 + except UnicodeError: 472 + continue 473 + 474 + def rand_bool(n: Optional[int] = None) -> Iterator[bool]: 475 + """ 476 + Generates a stream of random :obj:`bool` data. 477 + 478 + :param n: the number of samples to be yielded; if :obj:`None`, an infinite stream is yielded 479 + """ 480 + validate(n, Optional[int]) 481 + return _rand_bool(n) 482 + 483 + def _rand_bool(n: Optional[int] = None) -> Iterator[bool]: 484 + if n is not None and n < 0: 485 + raise ValueError() 486 + i = 0 487 + while n is None or i < n: 488 + x = _rand.randint(0, 1) 489 + yield x == 1 490 + i += 1 491 + 492 + def rand_bool_none(n: Optional[int] = None) -> Iterator[Optional[bool]]: 493 + """ 494 + Generates a stream of random :obj:`bool` or :obj:`None` data. 495 + 496 + :param n: the number of samples to be yielded; if :obj:`None`, an infinite stream is yielded 497 + """ 498 + validate(n, Optional[int]) 499 + return _rand_bool_none(n) 500 + 501 + def _rand_bool_none(n: Optional[int] = None) -> Iterator[Optional[bool]]: 502 + if n is not None and n < 0: 503 + raise ValueError() 504 + i = 0 505 + while n is None or i < n: 506 + x = _rand.randint(0, 2) 507 + yield None if x == 2 else x == 1 508 + i += 1 509 + 510 + def rand_float(n: Optional[int] = None) -> Iterator[float]: 511 + """ 512 + Generates a stream of random :obj:`float` data. 513 + 514 + :param n: the number of samples to be yielded; if :obj:`None`, an infinite stream is yielded 515 + """ 516 + validate(n, Optional[int]) 517 + return _rand_float(n) 518 + 519 + def _rand_float(n: Optional[int] = None) -> Iterator[float]: 520 + if n is not None and n < 0: 521 + raise ValueError() 522 + min_float = _options["min_float"] 523 + max_float = _options["max_float"] 524 + float_decimals = _options["float_decimals"] 525 + eps = 10.0**-float_decimals 526 + if min_float >= 0 or max_float <= 0: 527 + # no overflow in `min_float + (max_float-min_float) * random()`, can use `Random.uniform` 528 + i = 0 529 + while n is None or i < n: 530 + x = _rand.uniform(min_float, max_float) 531 + yield x-x%eps 532 + i += 1 533 + else: 534 + # overflow in `min_float + (max_float-min_float) * random()`, cannot use `Random.uniform` 535 + i = 0 536 + while n is None or i < n: 537 + x = 1/(1+max_float/(-min_float)) 538 + # x is (-min_float)/(max_float-min_float), the probability of sampling a number in (-min_float, 0) 539 + if _rand.random() < x: 540 + x = _rand.random()*min_float 541 + else: 542 + x = _rand.random()*max_float 543 + yield x-x%eps 544 + i += 1 545 + 546 + _cid_multibase = multibase.get("base58btc") # the default base for binary CIDs 547 + _cid_version = 1 548 + _cid_multicodec = multicodec.get("dag-cbor") 549 + _cid_multihash = multihash.get("sha3-512") 550 + 551 + def rand_data_cid(n: Optional[int] = None, *, max_nesting: Optional[int] = None) -> Iterator[Tuple[IPLDKind, CID]]: 552 + r""" 553 + Generates a stream of random DAG-CBOR data and associated CIDs: 554 + 555 + - multibase 'base32' 556 + - CIDv1 557 + - multicodec 'dag-cbor' 558 + - multihash 'sha3-512', with full 512-bit digest 559 + 560 + :param n: the number of samples to be yielded; if :obj:`None`, an infinite stream is yielded 561 + :param max_nesting: the maximum nesting level for containers; if :obj:`None`, value from :func:`get_options` is used 562 + 563 + """ 564 + validate(n, Optional[int]) 565 + return _rand_data_cid(n, max_nesting=max_nesting) 566 + 567 + def rand_cid(n: Optional[int] = None, *, max_nesting: Optional[int] = None) -> Iterator[CID]: 568 + """ 569 + Generates a stream of random CIDs: 570 + 571 + - multibase 'base32' 572 + - CIDv1 573 + - multicodec 'dag-cbor' 574 + - multihash 'sha3-512', with full 512-bit digest 575 + 576 + :param n: the number of samples to be yielded; if :obj:`None`, an infinite stream is yielded 577 + :param max_nesting: the maximum nesting level for containers; if :obj:`None`, value from :func:`get_options` is used 578 + """ 579 + validate(n, Optional[int]) 580 + return _rand_cid(n, max_nesting=max_nesting) 581 + 582 + def _rand_cid(n: Optional[int] = None, *, max_nesting: Optional[int] = None) -> Iterator[CID]: 583 + return (cid for _, cid in _rand_data_cid(n, max_nesting=max_nesting)) 584 + 585 + def _rand_data_cid(n: Optional[int] = None, *, max_nesting: Optional[int] = None) -> Iterator[Tuple[IPLDKind, CID]]: 586 + if n is not None and n < 0: 587 + raise ValueError() 588 + if max_nesting is None: 589 + max_nesting = _options["max_nesting"] 590 + elif max_nesting < 0: 591 + raise ValueError("Value for max_nesting is negative.") 592 + i = 0 593 + rand_data_generator = _rand_data(max_nesting=max_nesting-1) 594 + while n is None or i < n: 595 + try: 596 + dag_cbor_data = next(rand_data_generator) 597 + binary_data = encode(dag_cbor_data) 598 + except StopIteration as e: 599 + raise RuntimeError("Random digest stream is infinite, this should not happen.") from e 600 + yield (dag_cbor_data, CID(_cid_multibase, _cid_version, _cid_multicodec, _cid_multihash.digest(binary_data))) 601 + i += 1
+23
vendor/git/dag-cbor/docs/Makefile
··· 1 + # Minimal makefile for Sphinx documentation 2 + # 3 + 4 + # You can set these variables from the command line, and also 5 + # from the environment for the first two. 6 + SPHINXOPTS ?= 7 + SPHINXBUILD ?= sphinx-build 8 + SOURCEDIR = . 9 + BUILDDIR = _build 10 + 11 + # Put it first so that "make" without argument is like "make help". 12 + help: 13 + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 + 15 + .PHONY: help Makefile 16 + 17 + api: 18 + python make-api.py 19 + 20 + # Catch-all target: route all unknown targets to Sphinx using the new 21 + # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 22 + %: Makefile 23 + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+11
vendor/git/dag-cbor/docs/api-toc.rst
··· 1 + .. toctree:: 2 + :maxdepth: 2 3 + :caption: API Documentation 4 + 5 + api/dag_cbor 6 + api/dag_cbor.decoding 7 + api/dag_cbor.decoding.err 8 + api/dag_cbor.encoding 9 + api/dag_cbor.encoding.err 10 + api/dag_cbor.ipld 11 + api/dag_cbor.random
+18
vendor/git/dag-cbor/docs/api/dag_cbor.decoding.err.rst
··· 1 + dag_cbor.decoding.err 2 + ===================== 3 + 4 + .. automodule:: dag_cbor.decoding.err 5 + 6 + CBORDecodingError 7 + ----------------- 8 + 9 + .. autoclass:: dag_cbor.decoding.err.CBORDecodingError 10 + :show-inheritance: 11 + :members: 12 + 13 + DAGCBORDecodingError 14 + -------------------- 15 + 16 + .. autoclass:: dag_cbor.decoding.err.DAGCBORDecodingError 17 + :show-inheritance: 18 + :members:
+24
vendor/git/dag-cbor/docs/api/dag_cbor.decoding.rst
··· 1 + dag_cbor.decoding 2 + ================= 3 + 4 + .. automodule:: dag_cbor.decoding 5 + 6 + DecodeCallback 7 + -------------- 8 + 9 + .. autoclass:: dag_cbor.decoding.DecodeCallback 10 + :show-inheritance: 11 + :members: 12 + 13 + decode 14 + ------ 15 + 16 + .. autofunction:: dag_cbor.decoding.decode 17 + 18 + dag_cbor.decoding.__all__ 19 + ------------------------- 20 + 21 + The following members were explicitly reexported using ``__all__``: 22 + 23 + - :py:class:`dag_cbor.decoding.err.CBORDecodingError` 24 + - :py:class:`dag_cbor.decoding.err.DAGCBORDecodingError`
+32
vendor/git/dag-cbor/docs/api/dag_cbor.encoding.err.rst
··· 1 + dag_cbor.encoding.err 2 + ===================== 3 + 4 + .. automodule:: dag_cbor.encoding.err 5 + 6 + CBOREncodingError 7 + ----------------- 8 + 9 + .. autoclass:: dag_cbor.encoding.err.CBOREncodingError 10 + :show-inheritance: 11 + :members: 12 + 13 + CBORError 14 + --------- 15 + 16 + .. autoclass:: dag_cbor.encoding.err.CBORError 17 + :show-inheritance: 18 + :members: 19 + 20 + DAGCBOREncodingError 21 + -------------------- 22 + 23 + .. autoclass:: dag_cbor.encoding.err.DAGCBOREncodingError 24 + :show-inheritance: 25 + :members: 26 + 27 + DAGCBORError 28 + ------------ 29 + 30 + .. autoclass:: dag_cbor.encoding.err.DAGCBORError 31 + :show-inheritance: 32 + :members:
+27
vendor/git/dag-cbor/docs/api/dag_cbor.encoding.rst
··· 1 + dag_cbor.encoding 2 + ================= 3 + 4 + .. automodule:: dag_cbor.encoding 5 + 6 + canonical_order_dict 7 + -------------------- 8 + 9 + .. autofunction:: dag_cbor.encoding.canonical_order_dict 10 + 11 + check_key_compliance 12 + -------------------- 13 + 14 + .. autofunction:: dag_cbor.encoding.check_key_compliance 15 + 16 + encode 17 + ------ 18 + 19 + .. autofunction:: dag_cbor.encoding.encode 20 + 21 + dag_cbor.encoding.__all__ 22 + ------------------------- 23 + 24 + The following members were explicitly reexported using ``__all__``: 25 + 26 + - :py:class:`dag_cbor.encoding.err.CBOREncodingError` 27 + - :py:class:`dag_cbor.encoding.err.DAGCBOREncodingError`
+27
vendor/git/dag-cbor/docs/api/dag_cbor.ipld.rst
··· 1 + dag_cbor.ipld 2 + ============= 3 + 4 + .. automodule:: dag_cbor.ipld 5 + 6 + IPLDKind 7 + -------- 8 + 9 + .. autodata:: dag_cbor.ipld.IPLDKind 10 + 11 + IPLDObjPath 12 + ----------- 13 + 14 + .. autoclass:: dag_cbor.ipld.IPLDObjPath 15 + :show-inheritance: 16 + :members: 17 + :special-members: __new__, __truediv__, __rtruediv__, __le__, __lt__, __repr__, __rshift__ 18 + 19 + IPLDObjPathSegment 20 + ------------------ 21 + 22 + .. autodata:: dag_cbor.ipld.IPLDObjPathSegment 23 + 24 + IPLDScalarKind 25 + -------------- 26 + 27 + .. autodata:: dag_cbor.ipld.IPLDScalarKind
+84
vendor/git/dag-cbor/docs/api/dag_cbor.random.rst
··· 1 + dag_cbor.random 2 + =============== 3 + 4 + .. automodule:: dag_cbor.random 5 + 6 + default_options 7 + --------------- 8 + 9 + .. autofunction:: dag_cbor.random.default_options 10 + 11 + get_options 12 + ----------- 13 + 14 + .. autofunction:: dag_cbor.random.get_options 15 + 16 + options 17 + ------- 18 + 19 + .. autofunction:: dag_cbor.random.options 20 + 21 + rand_bool 22 + --------- 23 + 24 + .. autofunction:: dag_cbor.random.rand_bool 25 + 26 + rand_bool_none 27 + -------------- 28 + 29 + .. autofunction:: dag_cbor.random.rand_bool_none 30 + 31 + rand_bytes 32 + ---------- 33 + 34 + .. autofunction:: dag_cbor.random.rand_bytes 35 + 36 + rand_cid 37 + -------- 38 + 39 + .. autofunction:: dag_cbor.random.rand_cid 40 + 41 + rand_data 42 + --------- 43 + 44 + .. autofunction:: dag_cbor.random.rand_data 45 + 46 + rand_data_cid 47 + ------------- 48 + 49 + .. autofunction:: dag_cbor.random.rand_data_cid 50 + 51 + rand_dict 52 + --------- 53 + 54 + .. autofunction:: dag_cbor.random.rand_dict 55 + 56 + rand_float 57 + ---------- 58 + 59 + .. autofunction:: dag_cbor.random.rand_float 60 + 61 + rand_int 62 + -------- 63 + 64 + .. autofunction:: dag_cbor.random.rand_int 65 + 66 + rand_list 67 + --------- 68 + 69 + .. autofunction:: dag_cbor.random.rand_list 70 + 71 + rand_str 72 + -------- 73 + 74 + .. autofunction:: dag_cbor.random.rand_str 75 + 76 + reset_options 77 + ------------- 78 + 79 + .. autofunction:: dag_cbor.random.reset_options 80 + 81 + set_options 82 + ----------- 83 + 84 + .. autofunction:: dag_cbor.random.set_options
+15
vendor/git/dag-cbor/docs/api/dag_cbor.rst
··· 1 + dag_cbor 2 + ======== 3 + 4 + .. automodule:: dag_cbor 5 + 6 + dag_cbor.__all__ 7 + ---------------- 8 + 9 + The following members were explicitly reexported using ``__all__``: 10 + 11 + - :py:obj:`dag_cbor.ipld.IPLDKind` 12 + - :py:class:`dag_cbor.ipld.IPLDObjPath` 13 + - :py:obj:`dag_cbor.ipld.IPLDScalarKind` 14 + - :py:func:`dag_cbor.decoding.decode` 15 + - :py:func:`dag_cbor.encoding.encode`
+7
vendor/git/dag-cbor/docs/autodoc-type-aliases.json
··· 1 + { 2 + "DecodeCallback": "dag_cbor.decoding.DecodeCallback", 3 + "IPLDScalarKind": "dag_cbor.ipld.IPLDScalarKind", 4 + "IPLDKind": "dag_cbor.ipld.IPLDKind", 5 + "IPLDObjPath": "dag_cbor.ipld.IPLDObjPath", 6 + "IPLDObjPathSegment": "dag_cbor.ipld.IPLDObjPathSegment" 7 + }
+95
vendor/git/dag-cbor/docs/conf.py
··· 1 + # pylint: disable = all 2 + # Configuration file for the Sphinx documentation builder. 3 + # 4 + # This file only contains a selection of the most common options. For a full 5 + # list see the documentation: 6 + # https://www.sphinx-doc.org/en/master/usage/configuration.html 7 + 8 + # -- Path setup -------------------------------------------------------------- 9 + 10 + # If extensions (or modules to document with autodoc) are in another directory, 11 + # add these directories to sys.path here. If the directory is relative to the 12 + # documentation root, use os.path.abspath to make it absolute, like shown here. 13 + # 14 + 15 + from __future__ import annotations 16 + 17 + import inspect 18 + import json 19 + import os 20 + import sys 21 + sys.path.insert(0, os.path.abspath('..')) 22 + import sphinx_rtd_theme # type: ignore 23 + 24 + 25 + # -- Project information ----------------------------------------------------- 26 + 27 + project = 'dag-cbor' 28 + copyright = '2023, Hashberg' 29 + author = 'Hashberg' 30 + 31 + 32 + # The version info for the project you"re documenting, acts as replacement for 33 + # |version| and |release|, also used in various other places throughout the 34 + # built documents. 35 + # 36 + # The full version, including alpha/beta/rc tags. 37 + release = "0.3.0" 38 + # The short X.Y version. 39 + version = "0.3.0" 40 + 41 + 42 + # -- General configuration --------------------------------------------------- 43 + 44 + # Add any Sphinx extension module names here, as strings. They can be 45 + # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 46 + # ones. 47 + extensions = [ 48 + 'sphinx.ext.autodoc', 49 + 'sphinx.ext.mathjax', 50 + 'sphinx.ext.intersphinx', 51 + 'sphinx_rtd_theme', 52 + 'sphinx.ext.viewcode', 53 + 'autodoc_typehints', 54 + ] 55 + 56 + nitpicky = True # warn about every broken reference 57 + add_function_parentheses = False # no parentheses after function names 58 + add_module_names = False # no module names at start of classes 59 + set_type_checking_flag = False # setting to True creates issues when dealing with circular dependencies introduced for static typechecking 60 + autodoc_typehints = "none" # don't document type hints, extension 'autodoc_typehints' will take care of it 61 + 62 + with open("autodoc-type-aliases.json", "r") as f: 63 + autodoc_type_aliases = json.load(f) # load type aliases generated by make-api.py 64 + 65 + intersphinx_cache_limit = -1 66 + intersphinx_timeout = 20 67 + intersphinx_mapping = { 68 + 'python': ('https://docs.python.org/3', None), 69 + 'multiformats': ('http://multiformats.readthedocs.io/en/latest', None) 70 + } 71 + 72 + # Add any paths that contain templates here, relative to this directory. 73 + templates_path = ['_templates'] 74 + 75 + # List of patterns, relative to source directory, that match files and 76 + # directories to ignore when looking for source files. 77 + # This pattern also affects html_static_path and html_extra_path. 78 + exclude_patterns = [ 79 + "test" 80 + ] 81 + 82 + # The name of the Pygments (syntax highlighting) style to use. 83 + pygments_style = 'sphinx' 84 + 85 + # -- Options for HTML output ------------------------------------------------- 86 + 87 + # The theme to use for HTML and HTML Help pages. See the documentation for 88 + # a list of builtin themes. 89 + # 90 + html_theme = 'sphinx_rtd_theme' 91 + 92 + # Add any paths that contain custom static files (such as style sheets) here, 93 + # relative to this directory. They are copied after the builtin static files, 94 + # so a file named "default.css" will overwrite the builtin "default.css". 95 + # html_static_path = ['_static']
+17
vendor/git/dag-cbor/docs/decoding.rst
··· 1 + Decoding 2 + ======== 3 + 4 + The core decoding functionality is performed by the :func:`~dag_cbor.decoding.decode` function, which decodes :obj:`bytes` into a value: 5 + 6 + >>> import dag_cbor 7 + >>> dag_cbor.decode(b'\xa2aa\x0cabfhello!') 8 + {'a': 12, 'b': 'hello!'} 9 + 10 + A buffered binary stream (i.e. an instance of :obj:`~io.BufferedIOBase`) can be passed to the :func:`~dag_cbor.decoding.decode` function instead of a :obj:`bytes` object, in which case the contents of the stream are read in their entirety and decoded: 11 + 12 + >>> stream = BytesIO(b'\xa2aa\x0cabfhello!') 13 + >>> dag_cbor.decode(stream) 14 + {'a': 12, 'b': 'hello!'} 15 + 16 + The decision to read the entirety of the stream stems from the `DAG-CBOR codec <https://ipld.io/specs/codecs/dag-cbor/spec/>`_ specification, stating that encoding and decoding is only allowed on a single top-level item. 17 + However, the optional keyword argument ``allow_concat`` (default :obj:`False`) can be set to :obj:`True` to disable this behaviour and allow only part of the stream to be decoded.
+18
vendor/git/dag-cbor/docs/encoding.rst
··· 1 + 2 + Encoding 3 + ======== 4 + 5 + The core encoding functionality is performed by the :func:`~dag_cbor.encoding.encode` function, which encods a value into a :obj:`bytes` object: 6 + 7 + >>> import dag_cbor 8 + >>> dag_cbor.encode({'a': 12, 'b': 'hello!'}) 9 + b'\xa2aa\x0cabfhello!' 10 + 11 + A buffered binary stream (i.e. an instance of :obj:`~io.BufferedIOBase`) can be passed to the :func:`~dag_cbor.encoding.encode` function using the optional keyword argument ``stream``, in which case the encoded bytes are written to the stream and the number of bytes written is returned: 12 + 13 + >>> from io import BytesIO 14 + >>> stream = BytesIO() 15 + >>> dag_cbor.encode({'a': 12, 'b': 'hello!'}, stream=stream) 16 + 13 17 + >>> stream.getvalue() 18 + b'\xa2aa\x0cabfhello!'
+67
vendor/git/dag-cbor/docs/getting-started.rst
··· 1 + Getting Started 2 + =============== 3 + 4 + This is a fully compliant Python implementation of the `DAG-CBOR codec <https://ipld.io/specs/codecs/dag-cbor/spec/>`_, a subset of the `Concise Binary Object Representation (CBOR) <https://cbor.io/>`_ supporting the `IPLD Data Model <https://ipld.io/docs/data-model/>`_ and enforcing a unique (strict) encoded representation of items. 5 + 6 + 7 + Installation 8 + ------------ 9 + 10 + You can install the latest release from `PyPI <https://pypi.org/project/dag-cbor/>`_ as follows: 11 + 12 + .. code-block:: console 13 + 14 + $ pip install --upgrade dag-cbor 15 + 16 + GitHub repo: https://github.com/hashberg-io/dag-cbor 17 + 18 + 19 + Basic Usage 20 + ----------- 21 + 22 + The core functionality of the library is performed by the :func:`~dag_cbor.encoding.encode` and :func:`~dag_cbor.decoding.decode` functions: 23 + 24 + >>> import dag_cbor 25 + >>> dag_cbor.encode({'a': 12, 'b': 'hello!'}) 26 + b'\xa2aa\x0cabfhello!' 27 + >>> dag_cbor.decode(b'\xa2aa\x0cabfhello!') 28 + {'a': 12, 'b': 'hello!'} 29 + 30 + The :mod:`~dag_cbor.ipld` module contains utility types and functions pertaining to the `IPLD Data Model <https://ipld.io/docs/data-model/>`_. 31 + The :mod:`~dag_cbor.random` module contains functions to generate random data compatible with DAG-CBOR encoding. 32 + 33 + 34 + The DAG-CBOR codec 35 + ------------------ 36 + 37 + The `DAG-CBOR codec <https://ipld.io/specs/codecs/dag-cbor/spec/>`_ is a restriction of the `CBOR codec <https://cbor.io/>`_, enforcing additional conventions: 38 + 39 + - The only tag (major type 6) allowed is the CID tag 42, to be encoded as a two bytes head ``0xd82a`` 40 + (``0xd8`` is ``0b110_11000``, which means "major type 6 (``0b110``, i.e. 6) with 1 byte of argument (``0b11000``, i.e. 24)", 41 + while `0x2a` is the number 42). 42 + - Integers (major types 0 and 1) must be encoded using the minimum possible number of bytes. 43 + - Lengths for major types 2, 3, 4 and 5 must be encoded in the data item head using the minimum possible number of bytes. 44 + - Map keys must be strings (major type 3) and must be unique. 45 + - Map keys must be sorted ascendingly, first by increasing length and then by lexicographic ordering of their utf-8 encoded bytes (cf. `strictness <https://ipld.io/specs/codecs/dag-cbor/spec/#strictness>`_). 46 + - Indefinite-length items (bytes, strings, lists or maps) are not allowed. 47 + - The "break" token is not allowed. 48 + - The only major type 7 items allowed are 64-bit floats (minor 27) and the simple values `true` (minor 20), 49 + `false` (minor 21) and `null` (minor 22). 50 + - The special float values ``NaN``, ``Infinity`` and ``-Infinity`` are not allowed. 51 + - Encoding and decoding is only allowed on a single top-level item: back-to-back concatenated items at the top level 52 + are not allowed. 53 + 54 + Because the CBOR codec can encode/decode all data handled by the DAG-CBOR codec, we use an established CBOR implementation as the reference when testing, namely the `cbor2 <https://github.com/agronholm/cbor2>`_ package (with the exception of CID data, which is not natively handled by cbor2). 55 + 56 + 57 + Multiformats Config 58 + ------------------- 59 + 60 + Please note that :mod:`dag_cbor` internally imports `multiformats <https://github.com/hashberg-io/multiformats>`_: if you'd like to initialise multiformats 61 + with a custom selection of multicodecs/multihashes, you should call ``multiformats_config.enable()`` **before** you import :mod:`dag_cbor` (see the `multiformats docs <https://multiformats.readthedocs.io/en/latest/getting-started.html>`_ for further details): 62 + 63 + .. code-block:: python 64 + 65 + import multiformats_config 66 + multiformats_config.enable(codecs=["sha1", 0x29], bases=["base64url", "9"]) 67 + import dag_cbor # internally imports multiformats
+27
vendor/git/dag-cbor/docs/index.rst
··· 1 + 2 + dag-cbor: A Python implementation of the DAG-CBOR codec 3 + ======================================================= 4 + 5 + This is a fully compliant Python implementation of the `DAG-CBOR codec <https://ipld.io/specs/codecs/dag-cbor/spec/>`_, a subset of the `Concise Binary Object Representation (CBOR) <https://cbor.io/>`_ supporting the `IPLD Data Model <https://ipld.io/docs/data-model/>`_ and enforcing a unique (strict) encoded representation of items. 6 + 7 + GitHub repo: https://github.com/hashberg-io/dag-cbor 8 + 9 + 10 + .. toctree:: 11 + :maxdepth: 3 12 + :caption: Contents 13 + 14 + getting-started 15 + encoding 16 + decoding 17 + random 18 + 19 + .. include:: api-toc.rst 20 + 21 + 22 + Indices and tables 23 + ================== 24 + 25 + * :ref:`genindex` 26 + * :ref:`modindex` 27 + * :ref:`search`
+4
vendor/git/dag-cbor/docs/make-api-clean-html.bat
··· 1 + call make api 2 + call make clean 3 + call make html 4 + @pause
+23
vendor/git/dag-cbor/docs/make-api.json
··· 1 + { 2 + "pkg_name": "dag_cbor", 3 + "pkg_path": "..", 4 + "apidocs_folder": "api", 5 + "toc_filename": "api-toc.rst", 6 + "type_alias_dict_filename": "autodoc-type-aliases.json", 7 + "include_members": {}, 8 + "type_aliases": { 9 + "dag_cbor.decoding": ["DecodeCallback"], 10 + "dag_cbor.ipld": ["IPLDScalarKind", "IPLDKind", "IPLDObjPath", "IPLDObjPathSegment"] 11 + }, 12 + "exclude_members": {}, 13 + "include_modules": [], 14 + "exclude_modules": [ 15 + "dag_cbor.decoding._err", 16 + "dag_cbor.decoding._err_utils", 17 + "dag_cbor.decoding._stream" 18 + ], 19 + "member_fullnames": {}, 20 + "special_class_members": { 21 + "dag_cbor.ipld.IPLDObjPath": ["__new__", "__truediv__", "__rtruediv__", "__le__", "__lt__", "__repr__", "__rshift__"] 22 + } 23 + }
+241
vendor/git/dag-cbor/docs/make-api.py
··· 1 + """ 2 + A script to generate .rst files for API documentation. 3 + """ 4 + 5 + import glob 6 + import importlib 7 + import inspect 8 + import json 9 + import os 10 + import pkgutil 11 + from typing import Dict, List, Optional, Tuple 12 + import sys 13 + 14 + from typing_validation import validate 15 + 16 + def _list_package_contents(pkg_name: str) -> List[str]: 17 + modules = [pkg_name] 18 + for submod in pkgutil.iter_modules([pkg_name.replace(".", "/")]): 19 + submod_fullname = pkg_name+"."+submod.name 20 + if submod.ispkg: 21 + for subsubmod_name in _list_package_contents(submod_fullname): 22 + modules.append(subsubmod_name) 23 + else: 24 + modules.append(submod_fullname) 25 + return modules 26 + 27 + def make_apidocs() -> None: 28 + """ 29 + A script to generate .rst files for API documentation. 30 + """ 31 + err_msg = """Expected a 'make-api.json' file, with the following structure: 32 + { 33 + "pkg_name": str, 34 + "apidocs_folder": str, 35 + "pkg_path": str, 36 + "toc_filename": str, 37 + "type_alias_dict_filename": Optional[str], 38 + "include_members": Dict[str, List[str]], 39 + "type_aliases": Dict[str, List[str]], 40 + "exclude_members": Dict[str, List[str]], 41 + "exclude_modules": List[str], 42 + "member_fullnames": Dict[str, Dict[str, str]], 43 + "special_class_members": Dict[str, List[str]], 44 + } 45 + 46 + Set "toc_filename" to null to avoid generating a table of contents file. 47 + 48 + """ 49 + try: 50 + with open("make-api.json", "r") as f: 51 + config = json.load(f) 52 + pkg_name = config.get("pkg_name", None) 53 + validate(pkg_name, str) 54 + pkg_path = config.get("pkg_path", None) 55 + validate(pkg_path, str) 56 + apidocs_folder = config.get("apidocs_folder", None) 57 + validate(apidocs_folder, str) 58 + toc_filename = config.get("toc_filename", None) 59 + validate(toc_filename, str) 60 + type_alias_dict_filename = config.get("type_alias_dict_filename", None) 61 + validate(type_alias_dict_filename, Optional[str]) 62 + include_members = config.get("include_members", {}) 63 + validate(include_members, Dict[str, List[str]]) 64 + type_aliases = config.get("type_aliases", {}) 65 + validate(type_aliases, Dict[str, List[str]]) 66 + exclude_members = config.get("exclude_members", {}) 67 + validate(exclude_members, Dict[str, List[str]]) 68 + include_modules = config.get("include_modules", []) 69 + validate(include_modules, List[str]) 70 + exclude_modules = config.get("exclude_modules", []) 71 + validate(exclude_modules, List[str]) 72 + member_fullnames = config.get("member_fullnames", {}) 73 + validate(member_fullnames, Dict[str, Dict[str, str]]) 74 + special_class_members = config.get("special_class_members", {}) 75 + validate(special_class_members, Dict[str, List[str]]) 76 + except FileNotFoundError: 77 + print(err_msg) 78 + sys.exit(1) 79 + except TypeError: 80 + print(err_msg) 81 + sys.exit(1) 82 + for mod_name, type_alias_members in type_aliases.items(): 83 + if mod_name not in include_members: 84 + include_members[mod_name] = [] 85 + include_members[mod_name].extend(type_alias_members) 86 + 87 + cwd = os.getcwd() 88 + os.chdir(pkg_path) 89 + sys.path = [os.getcwd()]+sys.path 90 + modules = _list_package_contents(pkg_name) 91 + modules_dict = { 92 + mod_name: importlib.import_module(mod_name) 93 + for mod_name in modules 94 + } 95 + for mod_name in include_modules: 96 + if mod_name not in modules_dict: 97 + modules_dict[mod_name] = importlib.import_module(mod_name) 98 + os.chdir(cwd) 99 + 100 + print(f"Removing all docfiles from {apidocs_folder}/") 101 + for apidoc_file in glob.glob(f"{apidocs_folder}/*.rst"): 102 + print(f" {apidoc_file}") 103 + os.remove(apidoc_file) 104 + print() 105 + 106 + type_alias_fullnames: dict[str, str] = {} 107 + 108 + print("Pre-processing type aliases:") 109 + for mod_name, mod_type_aliases in type_aliases.items(): 110 + if mod_name in exclude_modules: 111 + continue 112 + for member_name in mod_type_aliases: 113 + member_fullname = f"{mod_name}.{member_name}" 114 + if member_name in type_alias_fullnames: 115 + print(f" WARNING! Skipping type alias {member_name} -> {member_fullname}") 116 + print(f" Existing type alias {member_name} -> {type_alias_fullnames[member_name]}") 117 + else: 118 + type_alias_fullnames[member_name] = member_fullname 119 + print(f" {member_name} -> {member_fullname}") 120 + print() 121 + 122 + for mod_name, mod in modules_dict.items(): 123 + if mod_name in exclude_modules: 124 + continue 125 + filename = f"{apidocs_folder}/{mod_name}.rst" 126 + print(f"Writing API docfile {filename}") 127 + lines: List[str] = [ 128 + mod_name, 129 + "="*len(mod_name), 130 + "", 131 + f".. automodule:: {mod_name}", 132 + "" 133 + ] 134 + mod__all__ = getattr(mod, "__all__", []) 135 + reexported_members: List[Tuple[str, str]] = [] 136 + for member_name in sorted(name for name in dir(mod)): 137 + to_include = mod_name in include_members and member_name in include_members[mod_name] 138 + to_exclude = mod_name in exclude_members and member_name in exclude_members[mod_name] 139 + if to_exclude: 140 + continue 141 + if member_name.startswith("_") and not to_include: 142 + continue 143 + member = getattr(mod, member_name) 144 + member_module = inspect.getmodule(member) 145 + member_module_name = member_module.__name__ if member_module is not None else None 146 + imported_member = member_module is not None and member_module != mod 147 + if mod_name in include_members and member_name in include_members[mod_name]: 148 + imported_member = False 149 + if member_name in type_alias_fullnames: 150 + member_fullname = type_alias_fullnames[member_name] 151 + elif mod_name in member_fullnames and member_name in member_fullnames[mod_name]: 152 + member_fullname = member_fullnames[mod_name][member_name] 153 + elif imported_member: 154 + if inspect.ismodule(member): 155 + member_fullname = member_module_name or "" 156 + else: 157 + member_fullname = f"{member_module_name}.{member_name}" 158 + else: 159 + member_fullname = f"{mod_name}.{member_name}" 160 + member_kind = "data" 161 + if inspect.isclass(member): 162 + member_kind = "class" 163 + elif inspect.isfunction(member): 164 + member_kind = "function" 165 + elif inspect.ismodule(member): 166 + member_kind = "module" 167 + if not imported_member: 168 + member_lines: List[str] = [] 169 + member_lines = [ 170 + member_name, 171 + "-"*len(member_name), 172 + "", 173 + f".. auto{member_kind}:: {member_fullname}", 174 + ] 175 + if member_kind == "class": 176 + member_lines.append(" :show-inheritance:") 177 + member_lines.append(" :members:") 178 + if member_fullname in special_class_members and special_class_members[member_fullname]: 179 + member_lines.append(f" :special-members: {', '.join(special_class_members[member_fullname])}") 180 + member_lines.append("") 181 + if member_name in type_alias_fullnames: 182 + print(f" {member_kind} {member_name} -> {type_alias_fullnames[member_name]} (type alias)") 183 + else: 184 + print(f" {member_kind} {member_name}") 185 + lines.extend(member_lines) 186 + elif member_name in mod__all__: 187 + reexported_members.append((member_fullname, member_kind)) 188 + if reexported_members: 189 + reexported_members_header = f"{mod_name}.__all__" 190 + print(f" {reexported_members_header}:") 191 + lines.extend([ 192 + reexported_members_header, 193 + "-"*len(reexported_members_header), 194 + "", 195 + "The following members were explicitly reexported using ``__all__``:", 196 + "", 197 + ]) 198 + refkinds = { 199 + "data": "obj", 200 + "function": "func", 201 + "class": "class", 202 + "module": "mod" 203 + } 204 + for member_fullname, member_kind in reexported_members: 205 + refkind = f":py:{refkinds[member_kind]}:" 206 + lines.append(f" - {refkind}`{member_fullname}`") 207 + print(f" {member_kind} {member_fullname}") 208 + lines.append("") 209 + with open(filename, "w") as f: 210 + f.write("\n".join(lines)) 211 + print("") 212 + 213 + toctable_lines = [ 214 + ".. toctree::", 215 + " :maxdepth: 2", 216 + " :caption: API Documentation", 217 + "" 218 + ] 219 + print(f"Writing TOC for API docfiles at {toc_filename}") 220 + for mod_name in modules_dict: 221 + if mod_name in exclude_modules: 222 + continue 223 + line = f" {apidocs_folder}/{mod_name}" 224 + toctable_lines.append(line) 225 + print(line) 226 + toctable_lines.append("") 227 + print() 228 + 229 + with open(toc_filename, "w") as f: 230 + f.write("\n".join(toctable_lines)) 231 + 232 + if type_alias_dict_filename is not None: 233 + print(f"Writing type alias dictionary: {type_alias_dict_filename}") 234 + for name, fullname in type_alias_fullnames.items(): 235 + print(f" {name} -> {fullname}") 236 + print() 237 + with open(type_alias_dict_filename, "w") as f: 238 + json.dump(type_alias_fullnames, f, indent=4) 239 + 240 + if __name__ == "__main__": 241 + make_apidocs()
+39
vendor/git/dag-cbor/docs/make.bat
··· 1 + @ECHO OFF 2 + 3 + pushd %~dp0 4 + 5 + REM Command file for Sphinx documentation 6 + 7 + if "%SPHINXBUILD%" == "" ( 8 + set SPHINXBUILD=sphinx-build 9 + ) 10 + set SOURCEDIR=. 11 + set BUILDDIR=_build 12 + 13 + if "%1" == "" goto help 14 + if "%1" == "api" goto api 15 + 16 + %SPHINXBUILD% >NUL 2>NUL 17 + if errorlevel 9009 ( 18 + echo. 19 + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 20 + echo.installed, then set the SPHINXBUILD environment variable to point 21 + echo.to the full path of the 'sphinx-build' executable. Alternatively you 22 + echo.may add the Sphinx directory to PATH. 23 + echo. 24 + echo.If you don't have Sphinx installed, grab it from 25 + echo.https://www.sphinx-doc.org/ 26 + exit /b 1 27 + ) 28 + 29 + %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 30 + goto end 31 + 32 + :help 33 + %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 34 + 35 + :api 36 + python make-api.py 37 + 38 + :end 39 + popd
+84
vendor/git/dag-cbor/docs/random.rst
··· 1 + Random DAG-CBOR Data 2 + ==================== 3 + 4 + The module :mod:`~dag_cbor.random` contains a set of functions to generate random DAG-CBOR data. 5 + The functions are named ``rand_X``, where ``X`` is one of: 6 + 7 + - :obj:`int` for uniformly distributed integers 8 + - :obj:`float` for uniformly distributed floats, with fixed decimals 9 + - :obj:`bytes` for byte-strings of uniformly distributed length, with uniformly distributed bytes 10 + - :obj:`str` for strings of uniformly distributed length, with uniformly distributed codepoints (all valid UTF-8 strings, by rejection sampling) 11 + - :obj:`bool` for :obj:`False` or :obj:`True` (50% each) 12 + - ``bool_none`` for :obj:`False`, :obj:`True` or :obj:`None` (33.3% each) 13 + - :obj:`list` for lists of uniformly distributed length, with random elements of any type 14 + - :obj:`dict` for dictionaries of uniformly distributed length, with distinct random string keys and random values of any type 15 + - `cid` for CID data (instance of :obj:`~multiformats.cid.CID` from the `multiformats <https://github.com/hashberg-io/multiformats>`_ library) 16 + 17 + 18 + Generating random values 19 + ------------------------ 20 + 21 + The function call ``rand_X(n)`` returns an iterator yielding a stream of ``n`` random values of type ``X``, e.g.: 22 + 23 + >>> import pprint 24 + >>> import dag_cbor 25 + >>> kwargs = dict(min_codepoint=0x41, max_codepoint=0x5a, include_cid=False) 26 + >>> with dag_cbor.random.options(**kwargs): 27 + ... for d in dag_cbor.random.rand_dict(3): 28 + ... pprint.pp(d) 29 + ... 30 + {'BIQPMZ': b'\x85\x1f\x07/\xcc\x00\xfc\xaa', 31 + 'EJEYDTZI': {}, 32 + 'PLSG': {'G': 'JFG', 33 + 'HZE': -61.278, 34 + 'JWDRKRGZ': b'-', 35 + 'OCCKQPDJ': True, 36 + 'SJOCTZMK': False}, 37 + 'PRDLN': 39.129, 38 + 'TUGRP': None, 39 + 'WZTEJDXC': -69.933} 40 + {'GHAXI': 39.12, 41 + 'PVUWZLC': 4.523, 42 + 'TDPSU': 'TVCADUGT', 43 + 'ZHGVSNSI': [-57, 9, -78.312]} 44 + {'': 11, 'B': True, 'FWD': {}, 'GXZBVAR': 'BTDWMGI', 'TDICHC': 87} 45 + 46 + The function call ``rand_X()``, without the positional argument ``n``, instead yields an infinite stream of random values. 47 + 48 + 49 + Random generation options 50 + ------------------------- 51 + 52 + The :func:`dag_cbor.random.options` context manager is used to set options temporarily, within the scope of a ``with`` directive. 53 + In the snippet below, we set string characters to be uppercase alphabetic (codepoints `0x41`-`0x5a`) and we excluded CID values from being generated: 54 + 55 + .. code-block:: python 56 + 57 + kwargs = dict(min_codepoint=0x41, max_codepoint=0x5a, include_cid=False) 58 + with dag_cbor.random.options(**kwargs): 59 + ... 60 + 61 + Options can be permanently set with :func:`~dag_cbor.random.set_options` and reset with :func:`~dag_cbor.random.reset_options`. 62 + A read-only view on options can be obtained from :func:`~dag_cbor.random.get_options`, and a read-only view on default options can be obtained from :func:`~dag_cbor.random.default_options`: 63 + 64 + >>> import pprint 65 + >>> import dag_cbor 66 + >>> pprint.pp(dag_cbor.random.default_options()) 67 + mappingproxy({'min_int': -100, 68 + 'max_int': 100, 69 + 'min_bytes': 0, 70 + 'max_bytes': 8, 71 + 'min_chars': 0, 72 + 'max_chars': 8, 73 + 'min_codepoint': 33, 74 + 'max_codepoint': 126, 75 + 'min_len': 0, 76 + 'max_len': 8, 77 + 'max_nesting': 2, 78 + 'canonical': True, 79 + 'min_float': -100.0, 80 + 'max_float': 100.0, 81 + 'float_decimals': 3, 82 + 'include_cid': True}) 83 + 84 + See :func:`~dag_cbor.random.set_options` for a description of the individual options.
+4
vendor/git/dag-cbor/docs/requirements.txt
··· 1 + sphinx_rtd_theme 2 + 3 + typing_extensions 4 + multiformats
+598
vendor/git/dag-cbor/model.pylintrc
··· 1 + 2 + [MASTER] 3 + 4 + # A comma-separated list of package or module names from where C extensions may 5 + # be loaded. Extensions are loading into the active Python interpreter and may 6 + # run arbitrary code. 7 + extension-pkg-whitelist= 8 + 9 + # Specify a score threshold to be exceeded before program exits with error. 10 + fail-under=10 11 + 12 + # Add files or directories to the blacklist. They should be base names, not 13 + # paths. 14 + ignore=CVS 15 + 16 + # Add files or directories matching the regex patterns to the blacklist. The 17 + # regex matches against base names, not paths. 18 + ignore-patterns= 19 + 20 + # Python code to execute, usually for sys.path manipulation such as 21 + # pygtk.require(). 22 + #init-hook= 23 + 24 + # Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the 25 + # number of processors available to use. 26 + jobs=0 27 + 28 + # Control the amount of potential inferred values when inferring a single 29 + # object. This can help the performance when dealing with large functions or 30 + # complex, nested conditions. 31 + limit-inference-results=100 32 + 33 + # List of plugins (as comma separated values of python module names) to load, 34 + # usually to register additional checkers. 35 + load-plugins= 36 + 37 + # Pickle collected data for later comparisons. 38 + persistent=yes 39 + 40 + # When enabled, pylint would attempt to guess common misconfiguration and emit 41 + # user-friendly hints instead of false-positive error messages. 42 + suggestion-mode=yes 43 + 44 + # Allow loading of arbitrary C extensions. Extensions are imported into the 45 + # active Python interpreter and may run arbitrary code. 46 + unsafe-load-any-extension=no 47 + 48 + 49 + [MESSAGES CONTROL] 50 + 51 + # Only show warnings with the listed confidence levels. Leave empty to show 52 + # all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED. 53 + confidence= 54 + 55 + # Disable the message, report, category or checker with the given id(s). You 56 + # can either give multiple identifiers separated by comma (,) or put this 57 + # option multiple times (only on the command line, not in the configuration 58 + # file where it should appear only once). You can also use "--disable=all" to 59 + # disable everything first and then reenable specific checks. For example, if 60 + # you want to run only the similarities checker, you can use "--disable=all 61 + # --enable=similarities". If you want to run only the classes checker, but have 62 + # no Warning level messages displayed, use "--disable=all --enable=classes 63 + # --disable=W". 64 + disable=print-statement, 65 + parameter-unpacking, 66 + unpacking-in-except, 67 + old-raise-syntax, 68 + backtick, 69 + long-suffix, 70 + old-ne-operator, 71 + old-octal-literal, 72 + import-star-module-level, 73 + non-ascii-bytes-literal, 74 + raw-checker-failed, 75 + bad-inline-option, 76 + locally-disabled, 77 + file-ignored, 78 + suppressed-message, 79 + useless-suppression, 80 + deprecated-pragma, 81 + use-symbolic-message-instead, 82 + apply-builtin, 83 + basestring-builtin, 84 + buffer-builtin, 85 + cmp-builtin, 86 + coerce-builtin, 87 + execfile-builtin, 88 + file-builtin, 89 + long-builtin, 90 + raw_input-builtin, 91 + reduce-builtin, 92 + standarderror-builtin, 93 + unicode-builtin, 94 + xrange-builtin, 95 + coerce-method, 96 + delslice-method, 97 + getslice-method, 98 + setslice-method, 99 + no-absolute-import, 100 + old-division, 101 + dict-iter-method, 102 + dict-view-method, 103 + next-method-called, 104 + metaclass-assignment, 105 + indexing-exception, 106 + raising-string, 107 + reload-builtin, 108 + oct-method, 109 + hex-method, 110 + nonzero-method, 111 + cmp-method, 112 + input-builtin, 113 + round-builtin, 114 + intern-builtin, 115 + unichr-builtin, 116 + map-builtin-not-iterating, 117 + zip-builtin-not-iterating, 118 + range-builtin-not-iterating, 119 + filter-builtin-not-iterating, 120 + using-cmp-argument, 121 + eq-without-hash, 122 + div-method, 123 + idiv-method, 124 + rdiv-method, 125 + exception-message-attribute, 126 + invalid-str-codec, 127 + sys-max-int, 128 + bad-python3-import, 129 + deprecated-string-function, 130 + deprecated-str-translate-call, 131 + deprecated-itertools-function, 132 + deprecated-types-field, 133 + next-method-defined, 134 + dict-items-not-iterating, 135 + dict-keys-not-iterating, 136 + dict-values-not-iterating, 137 + deprecated-operator-function, 138 + deprecated-urllib-function, 139 + xreadlines-attribute, 140 + deprecated-sys-function, 141 + exception-escape, 142 + comprehension-escape 143 + 144 + # Enable the message, report, category or checker with the given id(s). You can 145 + # either give multiple identifier separated by comma (,) or put this option 146 + # multiple time (only on the command line, not in the configuration file where 147 + # it should appear only once). See also the "--disable" option for examples. 148 + enable=c-extension-no-member 149 + 150 + 151 + [REPORTS] 152 + 153 + # Python expression which should return a score less than or equal to 10. You 154 + # have access to the variables 'error', 'warning', 'refactor', and 'convention' 155 + # which contain the number of messages in each category, as well as 'statement' 156 + # which is the total number of statements analyzed. This score is used by the 157 + # global evaluation report (RP0004). 158 + evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) 159 + 160 + # Template used to display messages. This is a python new-style format string 161 + # used to format the message information. See doc for all details. 162 + #msg-template= 163 + 164 + # Set the output format. Available formats are text, parseable, colorized, json 165 + # and msvs (visual studio). You can also give a reporter class, e.g. 166 + # mypackage.mymodule.MyReporterClass. 167 + output-format=text 168 + 169 + # Tells whether to display a full report or only the messages. 170 + reports=no 171 + 172 + # Activate the evaluation score. 173 + score=yes 174 + 175 + 176 + [REFACTORING] 177 + 178 + # Maximum number of nested blocks for function / method body 179 + max-nested-blocks=5 180 + 181 + # Complete name of functions that never returns. When checking for 182 + # inconsistent-return-statements if a never returning function is called then 183 + # it will be considered as an explicit return statement and no message will be 184 + # printed. 185 + never-returning-functions=sys.exit 186 + 187 + 188 + [BASIC] 189 + 190 + # Naming style matching correct argument names. 191 + argument-naming-style=snake_case 192 + 193 + # Regular expression matching correct argument names. Overrides argument- 194 + # naming-style. 195 + #argument-rgx= 196 + 197 + # Naming style matching correct attribute names. 198 + attr-naming-style=snake_case 199 + 200 + # Regular expression matching correct attribute names. Overrides attr-naming- 201 + # style. 202 + #attr-rgx= 203 + 204 + # Bad variable names which should always be refused, separated by a comma. 205 + bad-names=foo, 206 + bar, 207 + baz, 208 + toto, 209 + tutu, 210 + tata 211 + 212 + # Bad variable names regexes, separated by a comma. If names match any regex, 213 + # they will always be refused 214 + bad-names-rgxs= 215 + 216 + # Naming style matching correct class attribute names. 217 + class-attribute-naming-style=any 218 + 219 + # Regular expression matching correct class attribute names. Overrides class- 220 + # attribute-naming-style. 221 + #class-attribute-rgx= 222 + 223 + # Naming style matching correct class names. 224 + class-naming-style=PascalCase 225 + 226 + # Regular expression matching correct class names. Overrides class-naming- 227 + # style. 228 + #class-rgx= 229 + 230 + # Naming style matching correct constant names. 231 + const-naming-style=UPPER_CASE 232 + 233 + # Regular expression matching correct constant names. Overrides const-naming- 234 + # style. 235 + #const-rgx= 236 + 237 + # Minimum line length for functions/classes that require docstrings, shorter 238 + # ones are exempt. 239 + docstring-min-length=-1 240 + 241 + # Naming style matching correct function names. 242 + function-naming-style=snake_case 243 + 244 + # Regular expression matching correct function names. Overrides function- 245 + # naming-style. 246 + #function-rgx= 247 + 248 + # Good variable names which should always be accepted, separated by a comma. 249 + good-names=i, 250 + j, 251 + k, 252 + ex, 253 + Run, 254 + _ 255 + 256 + # Good variable names regexes, separated by a comma. If names match any regex, 257 + # they will always be accepted 258 + good-names-rgxs= 259 + 260 + # Include a hint for the correct naming format with invalid-name. 261 + include-naming-hint=no 262 + 263 + # Naming style matching correct inline iteration names. 264 + inlinevar-naming-style=any 265 + 266 + # Regular expression matching correct inline iteration names. Overrides 267 + # inlinevar-naming-style. 268 + #inlinevar-rgx= 269 + 270 + # Naming style matching correct method names. 271 + method-naming-style=snake_case 272 + 273 + # Regular expression matching correct method names. Overrides method-naming- 274 + # style. 275 + #method-rgx= 276 + 277 + # Naming style matching correct module names. 278 + module-naming-style=snake_case 279 + 280 + # Regular expression matching correct module names. Overrides module-naming- 281 + # style. 282 + #module-rgx= 283 + 284 + # Colon-delimited sets of names that determine each other's naming style when 285 + # the name regexes allow several styles. 286 + name-group= 287 + 288 + # Regular expression which should only match function or class names that do 289 + # not require a docstring. 290 + no-docstring-rgx=^_ 291 + 292 + # List of decorators that produce properties, such as abc.abstractproperty. Add 293 + # to this list to register other decorators that produce valid properties. 294 + # These decorators are taken in consideration only for invalid-name. 295 + property-classes=abc.abstractproperty 296 + 297 + # Naming style matching correct variable names. 298 + variable-naming-style=snake_case 299 + 300 + # Regular expression matching correct variable names. Overrides variable- 301 + # naming-style. 302 + #variable-rgx= 303 + 304 + 305 + [FORMAT] 306 + 307 + # Expected format of line ending, e.g. empty (any line ending), LF or CRLF. 308 + expected-line-ending-format= 309 + 310 + # Regexp for a line that is allowed to be longer than the limit. 311 + ignore-long-lines=^\s*(# )?<?https?://\S+>?$ 312 + 313 + # Number of spaces of indent required inside a hanging or continued line. 314 + indent-after-paren=4 315 + 316 + # String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 317 + # tab). 318 + indent-string=' ' 319 + 320 + # Maximum number of characters on a single line. 321 + max-line-length=100 322 + 323 + # Maximum number of lines in a module. 324 + max-module-lines=1000 325 + 326 + # List of optional constructs for which whitespace checking is disabled. `dict- 327 + # separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}. 328 + # `trailing-comma` allows a space between comma and closing bracket: (a, ). 329 + # `empty-line` allows space-only lines. 330 + no-space-check=trailing-comma, 331 + dict-separator 332 + 333 + # Allow the body of a class to be on the same line as the declaration if body 334 + # contains single statement. 335 + single-line-class-stmt=no 336 + 337 + # Allow the body of an if to be on the same line as the test if there is no 338 + # else. 339 + single-line-if-stmt=no 340 + 341 + 342 + [LOGGING] 343 + 344 + # The type of string formatting that logging methods do. `old` means using % 345 + # formatting, `new` is for `{}` formatting. 346 + logging-format-style=old 347 + 348 + # Logging modules to check that the string format arguments are in logging 349 + # function parameter format. 350 + logging-modules=logging 351 + 352 + 353 + [MISCELLANEOUS] 354 + 355 + # List of note tags to take in consideration, separated by a comma. 356 + notes=FIXME, 357 + XXX, 358 + TODO 359 + 360 + # Regular expression of note tags to take in consideration. 361 + #notes-rgx= 362 + 363 + 364 + [SIMILARITIES] 365 + 366 + # Ignore comments when computing similarities. 367 + ignore-comments=yes 368 + 369 + # Ignore docstrings when computing similarities. 370 + ignore-docstrings=yes 371 + 372 + # Ignore imports when computing similarities. 373 + ignore-imports=no 374 + 375 + # Minimum lines number of a similarity. 376 + min-similarity-lines=4 377 + 378 + 379 + [SPELLING] 380 + 381 + # Limits count of emitted suggestions for spelling mistakes. 382 + max-spelling-suggestions=4 383 + 384 + # Spelling dictionary name. Available dictionaries: none. To make it work, 385 + # install the python-enchant package. 386 + spelling-dict= 387 + 388 + # List of comma separated words that should not be checked. 389 + spelling-ignore-words= 390 + 391 + # A path to a file that contains the private dictionary; one word per line. 392 + spelling-private-dict-file= 393 + 394 + # Tells whether to store unknown words to the private dictionary (see the 395 + # --spelling-private-dict-file option) instead of raising a message. 396 + spelling-store-unknown-words=no 397 + 398 + 399 + [STRING] 400 + 401 + # This flag controls whether inconsistent-quotes generates a warning when the 402 + # character used as a quote delimiter is used inconsistently within a module. 403 + check-quote-consistency=no 404 + 405 + # This flag controls whether the implicit-str-concat should generate a warning 406 + # on implicit string concatenation in sequences defined over several lines. 407 + check-str-concat-over-line-jumps=no 408 + 409 + 410 + [TYPECHECK] 411 + 412 + # List of decorators that produce context managers, such as 413 + # contextlib.contextmanager. Add to this list to register other decorators that 414 + # produce valid context managers. 415 + contextmanager-decorators=contextlib.contextmanager 416 + 417 + # List of members which are set dynamically and missed by pylint inference 418 + # system, and so shouldn't trigger E1101 when accessed. Python regular 419 + # expressions are accepted. 420 + generated-members= 421 + 422 + # Tells whether missing members accessed in mixin class should be ignored. A 423 + # mixin class is detected if its name ends with "mixin" (case insensitive). 424 + ignore-mixin-members=yes 425 + 426 + # Tells whether to warn about missing members when the owner of the attribute 427 + # is inferred to be None. 428 + ignore-none=yes 429 + 430 + # This flag controls whether pylint should warn about no-member and similar 431 + # checks whenever an opaque object is returned when inferring. The inference 432 + # can return multiple potential results while evaluating a Python object, but 433 + # some branches might not be evaluated, which results in partial inference. In 434 + # that case, it might be useful to still emit no-member and other checks for 435 + # the rest of the inferred objects. 436 + ignore-on-opaque-inference=yes 437 + 438 + # List of class names for which member attributes should not be checked (useful 439 + # for classes with dynamically set attributes). This supports the use of 440 + # qualified names. 441 + ignored-classes=optparse.Values,thread._local,_thread._local 442 + 443 + # List of module names for which member attributes should not be checked 444 + # (useful for modules/projects where namespaces are manipulated during runtime 445 + # and thus existing member attributes cannot be deduced by static analysis). It 446 + # supports qualified module names, as well as Unix pattern matching. 447 + ignored-modules= 448 + 449 + # Show a hint with possible names when a member name was not found. The aspect 450 + # of finding the hint is based on edit distance. 451 + missing-member-hint=yes 452 + 453 + # The minimum edit distance a name should have in order to be considered a 454 + # similar match for a missing member name. 455 + missing-member-hint-distance=1 456 + 457 + # The total number of similar names that should be taken in consideration when 458 + # showing a hint for a missing member. 459 + missing-member-max-choices=1 460 + 461 + # List of decorators that change the signature of a decorated function. 462 + signature-mutators= 463 + 464 + 465 + [VARIABLES] 466 + 467 + # List of additional names supposed to be defined in builtins. Remember that 468 + # you should avoid defining new builtins when possible. 469 + additional-builtins= 470 + 471 + # Tells whether unused global variables should be treated as a violation. 472 + allow-global-unused-variables=yes 473 + 474 + # List of strings which can identify a callback function by name. A callback 475 + # name must start or end with one of those strings. 476 + callbacks=cb_, 477 + _cb 478 + 479 + # A regular expression matching the name of dummy variables (i.e. expected to 480 + # not be used). 481 + dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ 482 + 483 + # Argument names that match this expression will be ignored. Default to name 484 + # with leading underscore. 485 + ignored-argument-names=_.*|^ignored_|^unused_ 486 + 487 + # Tells whether we should check for unused import in __init__ files. 488 + init-import=no 489 + 490 + # List of qualified module names which can have objects that can redefine 491 + # builtins. 492 + redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io 493 + 494 + 495 + [CLASSES] 496 + 497 + # List of method names used to declare (i.e. assign) instance attributes. 498 + defining-attr-methods=__init__, 499 + __new__, 500 + setUp, 501 + __post_init__ 502 + 503 + # List of member names, which should be excluded from the protected access 504 + # warning. 505 + exclude-protected=_asdict, 506 + _fields, 507 + _replace, 508 + _source, 509 + _make 510 + 511 + # List of valid names for the first argument in a class method. 512 + valid-classmethod-first-arg=cls 513 + 514 + # List of valid names for the first argument in a metaclass class method. 515 + valid-metaclass-classmethod-first-arg=cls 516 + 517 + 518 + [DESIGN] 519 + 520 + # Maximum number of arguments for function / method. 521 + max-args=5 522 + 523 + # Maximum number of attributes for a class (see R0902). 524 + max-attributes=7 525 + 526 + # Maximum number of boolean expressions in an if statement (see R0916). 527 + max-bool-expr=5 528 + 529 + # Maximum number of branch for function / method body. 530 + max-branches=12 531 + 532 + # Maximum number of locals for function / method body. 533 + max-locals=15 534 + 535 + # Maximum number of parents for a class (see R0901). 536 + max-parents=7 537 + 538 + # Maximum number of public methods for a class (see R0904). 539 + max-public-methods=20 540 + 541 + # Maximum number of return / yield for function / method body. 542 + max-returns=6 543 + 544 + # Maximum number of statements in function / method body. 545 + max-statements=50 546 + 547 + # Minimum number of public methods for a class (see R0903). 548 + min-public-methods=2 549 + 550 + 551 + [IMPORTS] 552 + 553 + # List of modules that can be imported at any level, not just the top level 554 + # one. 555 + allow-any-import-level= 556 + 557 + # Allow wildcard imports from modules that define __all__. 558 + allow-wildcard-with-all=no 559 + 560 + # Analyse import fallback blocks. This can be used to support both Python 2 and 561 + # 3 compatible code, which means that the block might have code that exists 562 + # only in one or another interpreter, leading to false positives when analysed. 563 + analyse-fallback-blocks=no 564 + 565 + # Deprecated modules which should not be used, separated by a comma. 566 + deprecated-modules=optparse,tkinter.tix 567 + 568 + # Create a graph of external dependencies in the given file (report RP0402 must 569 + # not be disabled). 570 + ext-import-graph= 571 + 572 + # Create a graph of every (i.e. internal and external) dependencies in the 573 + # given file (report RP0402 must not be disabled). 574 + import-graph= 575 + 576 + # Create a graph of internal dependencies in the given file (report RP0402 must 577 + # not be disabled). 578 + int-import-graph= 579 + 580 + # Force import order to recognize a module as part of the standard 581 + # compatibility libraries. 582 + known-standard-library= 583 + 584 + # Force import order to recognize a module as part of a third party library. 585 + known-third-party=enchant 586 + 587 + # Couples of modules and preferred modules, separated by a comma. 588 + preferred-modules= 589 + 590 + 591 + [EXCEPTIONS] 592 + 593 + # Exceptions that will emit a warning when being caught. Defaults to 594 + # "BaseException, Exception". 595 + overgeneral-exceptions=BaseException, 596 + Exception 597 + 598 + C:\Users\Stefa\Documents\git\hashberg-io\quetz>
+3
vendor/git/dag-cbor/pypi-distrib-upload.bat
··· 1 + python -m build 2 + python -m twine upload --skip-existing dist/* 3 + pause
+11
vendor/git/dag-cbor/pyproject.toml
··· 1 + # pyproject.toml 2 + [build-system] 3 + requires = [ 4 + "setuptools>=45", 5 + "wheel", 6 + "setuptools_scm>=6.2" 7 + ] 8 + build-backend = "setuptools.build_meta" 9 + [tool.setuptools_scm] 10 + version_scheme = "post-release" 11 + local_scheme = "no-local-version"
+1
vendor/git/dag-cbor/requirements.txt
··· 1 + .
+7
vendor/git/dag-cbor/run-tests.bat
··· 1 + @echo off 2 + mypy --strict dag_cbor 3 + pylint --rcfile=.pylintrc --disable=fixme dag_cbor 4 + python -m readme_renderer README.rst -o README-PROOF.html 5 + pytest test/ --cov=./dag_cbor 6 + coverage html 7 + @pause
+37
vendor/git/dag-cbor/setup.cfg
··· 1 + # https://packaging.python.org/en/latest/tutorials/packaging-projects/ 2 + # https://setuptools.pypa.io/en/latest/userguide/declarative_config.html 3 + # https://packaging.python.org/en/latest/guides/distributing-packages-using-setuptools/#setup-cfg 4 + 5 + [metadata] 6 + name = dag-cbor 7 + author = hashberg 8 + author_email = sg495@users.noreply.github.com 9 + description = Python implementation of the DAG-CBOR codec. 10 + long_description = file: README.rst 11 + long_description_content_type = text/x-rst 12 + url = https://github.com/hashberg-io/dag-cbor 13 + project_urls = 14 + Bug Tracker = https://github.com/hashberg-io/dag-cbor/issues 15 + classifiers = 16 + Development Status :: 4 - Beta 17 + Programming Language :: Python :: 3.10 18 + Programming Language :: Python :: 3.9 19 + Programming Language :: Python :: 3.8 20 + Programming Language :: Python :: 3.7 21 + Operating System :: OS Independent 22 + Natural Language :: English 23 + Typing :: Typed 24 + 25 + [options] 26 + packages = find: 27 + python_requires = >=3.7 28 + install_requires = 29 + typing-extensions>=4.6.0 30 + typing-validation>=1.1.0 31 + multiformats>=0.3.1 32 + 33 + [options.package_data] 34 + * = py.typed 35 + 36 + [options.packages.find] 37 + exclude = test
vendor/git/dag-cbor/test/__init__.py

This is a binary file and will not be displayed.

+119
vendor/git/dag-cbor/test/test_00_encode_eq_cbor2_encode.py
··· 1 + """ 2 + Tests on encoding data using `dag_cbor` vs encoding data using `cbor2`. 3 + """ 4 + # pylint: disable = global-statement 5 + 6 + import cbor2 # type: ignore 7 + 8 + from dag_cbor import encode 9 + from dag_cbor.random import rand_list, rand_dict, rand_int, rand_bytes, rand_str, rand_bool_none, rand_float, rand_cid, options 10 + 11 + nsamples = 1000 12 + 13 + def test_int() -> None: 14 + """ 15 + Encodes random `int` samples with `dag_cbor.encoding.encode`, 16 + encodes them with `cbor2.encoder.dumps` and checks that the two encodings match. 17 + """ 18 + test_data = rand_int(nsamples) 19 + for i, x in enumerate(test_data): 20 + error_msg = f"failed at #{i} = {repr(x)}" 21 + assert cbor2.dumps(x) == encode(x), error_msg 22 + 23 + def test_special_int() -> None: 24 + """ 25 + Encodes specially crafted `int` samples with `dag_cbor.encoding.encode`, 26 + encodes them with `cbor2.encoder.dumps` and checks that the two encodings match. 27 + """ 28 + exponents = [8, 16, 32, 26] 29 + special_data = [2**e for e in exponents] 30 + special_data += [x-1 for x in special_data] 31 + special_data += [-x for x in special_data] 32 + for i, x in enumerate(special_data): 33 + error_msg = f"failed at #{i} = {repr(x)}" 34 + assert cbor2.dumps(x) == encode(x), error_msg 35 + 36 + def test_bytes() -> None: 37 + """ 38 + Encodes random `bytes` samples with `dag_cbor.encoding.encode`, 39 + encodes them with `cbor2.encoder.dumps` and checks that the two encodings match. 40 + """ 41 + test_data = rand_bytes(nsamples) 42 + for i, x in enumerate(test_data): 43 + error_msg = f"failed at #{i} = {repr(x)}" 44 + assert cbor2.dumps(x) == encode(x), error_msg 45 + 46 + def test_str() -> None: 47 + """ 48 + Encodes random `str` samples with `dag_cbor.encoding.encode`, 49 + encodes them with `cbor2.encoder.dumps` and checks that the two encodings match. 50 + """ 51 + test_data = rand_str(nsamples) 52 + for i, x in enumerate(test_data): 53 + error_msg = f"failed at #{i} = {repr(x)}" 54 + assert cbor2.dumps(x) == encode(x), error_msg 55 + 56 + def test_bool_none() -> None: 57 + """ 58 + Encodes random `Optional[bool]` or samples with `dag_cbor.encoding.encode`, 59 + encodes them with `cbor2.encoder.dumps` and checks that the two encodings match. 60 + """ 61 + test_data = rand_bool_none(nsamples) 62 + for i, x in enumerate(test_data): 63 + error_msg = f"failed at #{i} = {repr(x)}" 64 + assert cbor2.dumps(x) == encode(x), error_msg 65 + 66 + def test_float() -> None: 67 + """ 68 + Encodes random `float` samples with `dag_cbor.encoding.encode`, 69 + encodes them with `cbor2.encoder.dumps` and checks that the two encodings match. 70 + """ 71 + test_data = rand_float(nsamples) 72 + for i, x in enumerate(test_data): 73 + error_msg = f"failed at #{i} = {repr(x)}" 74 + assert cbor2.dumps(x) == encode(x), error_msg 75 + 76 + def test_list() -> None: 77 + """ 78 + Encodes random `list` samples with `dag_cbor.encoding.encode`, 79 + encodes them with `cbor2.encoder.dumps` and checks that the two encodings match. 80 + """ 81 + with options(include_cid=False): 82 + test_data = rand_list(nsamples) 83 + for i, x in enumerate(test_data): 84 + error_msg = f"failed at #{i} = {repr(x)}" 85 + assert cbor2.dumps(x) == encode(x), error_msg 86 + 87 + def test_dict() -> None: 88 + """ 89 + Encodes random `dict` samples with `dag_cbor.encoding.encode`, 90 + encodes them with `cbor2.encoder.dumps` and checks that the two encodings match. 91 + """ 92 + with options(include_cid=False): 93 + test_data = rand_dict(nsamples) 94 + for i, x in enumerate(test_data): 95 + error_msg = f"failed at #{i} = {repr(x)}" 96 + assert cbor2.dumps(x) == encode(x), error_msg 97 + 98 + 99 + def test_dict_noncanonical() -> None: 100 + """ 101 + Encodes a dict given in noncanonical order and tests if it is encoded in canonical order. 102 + from the specs (https://ipld.io/specs/codecs/dag-cbor/spec/#strictness): 103 + 104 + If two keys have different lengths, the shorter one sorts earlier; 105 + If two keys have the same length, the one with the lower value in (byte-wise) lexical order sorts earlier. 106 + """ 107 + test_data = {"bar123": 5, "zap": 7, "abc432": 9} 108 + assert list(cbor2.loads(encode(test_data))) == ["zap", "abc432", "bar123"] 109 + 110 + 111 + def test_cid() -> None: 112 + """ 113 + Encodes random CID samples with `dag_cbor.encoding.encode`, 114 + encodes them with `cbor2.encoder.dumps` and checks that the two encodings match. 115 + """ 116 + test_data = rand_cid(nsamples) 117 + for i, x in enumerate(test_data): 118 + error_msg = f"failed at #{i} = {repr(x)}" 119 + assert cbor2.dumps(cbor2.CBORTag(42, b"\0" + bytes(x))) == encode(x), error_msg
+116
vendor/git/dag-cbor/test/test_01_encode_decode_eq_original.py
··· 1 + """ 2 + Tests on encoding data using `dag_cbor` and decoding back using `cbor2`. 3 + """ 4 + # pylint: disable = global-statement 5 + 6 + from dag_cbor import encode, decode 7 + from dag_cbor.random import rand_list, rand_dict, rand_int, rand_bytes, rand_str, rand_bool_none, rand_float, rand_cid, options 8 + 9 + import pytest 10 + 11 + nsamples = 1000 12 + 13 + def test_int() -> None: 14 + """ 15 + Encodes random `int` samples with `dag_cbor.encoding.encode`, 16 + encodes them with `cbor2.encoder.dumps` and checks that the two encodings match. 17 + """ 18 + test_data = rand_int(nsamples) 19 + for i, x in enumerate(test_data): 20 + error_msg = f"failed at #{i} = {repr(x)}" 21 + assert x == decode(encode(x)), error_msg 22 + assert x == decode(encode(x, include_multicodec=True), require_multicodec=True), error_msg 23 + 24 + def test_special_int() -> None: 25 + """ 26 + Encodes specially crafted `int` samples with `dag_cbor.encoding.encode`, 27 + encodes them with `cbor2.encoder.dumps` and checks that the two encodings match. 28 + """ 29 + exponents = [8, 16, 32, 26] 30 + special_data = [2**e for e in exponents] 31 + special_data += [x-1 for x in special_data] 32 + special_data += [-x for x in special_data] 33 + for i, x in enumerate(special_data): 34 + error_msg = f"failed at #{i} = {repr(x)}" 35 + assert x == decode(encode(x)), error_msg 36 + assert x == decode(encode(x, include_multicodec=True), require_multicodec=True), error_msg 37 + 38 + def test_bytes() -> None: 39 + """ 40 + Encodes random `bytes` samples with `dag_cbor.encoding.encode`, 41 + encodes them with `cbor2.encoder.dumps` and checks that the two encodings match. 42 + """ 43 + test_data = rand_bytes(nsamples) 44 + for i, x in enumerate(test_data): 45 + error_msg = f"failed at #{i} = {repr(x)}" 46 + assert x == decode(encode(x)), error_msg 47 + assert x == decode(encode(x, include_multicodec=True), require_multicodec=True), error_msg 48 + 49 + def test_str() -> None: 50 + """ 51 + Encodes random `str` samples with `dag_cbor.encoding.encode`, 52 + encodes them with `cbor2.encoder.dumps` and checks that the two encodings match. 53 + """ 54 + test_data = rand_str(nsamples) 55 + for i, x in enumerate(test_data): 56 + error_msg = f"failed at #{i} = {repr(x)}" 57 + assert x == decode(encode(x)), error_msg 58 + assert x == decode(encode(x, include_multicodec=True), require_multicodec=True), error_msg 59 + 60 + def test_bool_none() -> None: 61 + """ 62 + Encodes random `Optional[bool]` or samples with `dag_cbor.encoding.encode`, 63 + encodes them with `cbor2.encoder.dumps` and checks that the two encodings match. 64 + """ 65 + test_data = rand_bool_none(nsamples) 66 + for i, x in enumerate(test_data): 67 + error_msg = f"failed at #{i} = {repr(x)}" 68 + assert x == decode(encode(x)), error_msg 69 + assert x == decode(encode(x, include_multicodec=True), require_multicodec=True), error_msg 70 + 71 + def test_float() -> None: 72 + """ 73 + Encodes random `float` samples with `dag_cbor.encoding.encode`, 74 + encodes them with `cbor2.encoder.dumps` and checks that the two encodings match. 75 + """ 76 + test_data = rand_float(nsamples) 77 + for i, x in enumerate(test_data): 78 + error_msg = f"failed at #{i} = {repr(x)}" 79 + assert x == decode(encode(x)), error_msg 80 + assert x == decode(encode(x, include_multicodec=True), require_multicodec=True), error_msg 81 + 82 + def test_list() -> None: 83 + """ 84 + Encodes random `list` samples with `dag_cbor.encoding.encode`, 85 + encodes them with `cbor2.encoder.dumps` and checks that the two encodings match. 86 + """ 87 + with options(include_cid=False): 88 + test_data = rand_list(nsamples) 89 + for i, x in enumerate(test_data): 90 + error_msg = f"failed at #{i} = {repr(x)}" 91 + assert x == decode(encode(x)), error_msg 92 + assert x == decode(encode(x, include_multicodec=True), require_multicodec=True), error_msg 93 + 94 + @pytest.mark.parametrize("canonical", [True, False]) 95 + def test_dict(canonical: bool) -> None: 96 + """ 97 + Encodes random `dict` samples with `dag_cbor.encoding.encode`, 98 + encodes them with `cbor2.encoder.dumps` and checks that the two encodings match. 99 + """ 100 + with options(include_cid=False, canonical=canonical): 101 + test_data = rand_dict(nsamples) 102 + for i, x in enumerate(test_data): 103 + error_msg = f"failed at #{i} = {repr(x)}" 104 + assert x == decode(encode(x)), error_msg 105 + assert x == decode(encode(x, include_multicodec=True), require_multicodec=True), error_msg 106 + 107 + def test_cid() -> None: 108 + """ 109 + Encodes random CID samples with `dag_cbor.encoding.encode`, 110 + encodes them with `cbor2.encoder.dumps` and checks that the two encodings match. 111 + """ 112 + test_data = rand_cid(nsamples) 113 + for i, x in enumerate(test_data): 114 + error_msg = f"failed at #{i} = {repr(x)}" 115 + assert x == decode(encode(x)), error_msg 116 + assert x == decode(encode(x, include_multicodec=True), require_multicodec=True), error_msg
+119
vendor/git/dag-cbor/test/test_02_decode_eq_cbor2_decode.py
··· 1 + """ 2 + Tests on encoding data using `dag_cbor` and decoding back using `dag_cbor`. 3 + """ 4 + # pylint: disable = global-statement 5 + 6 + import cbor2.decoder as cbor2 # type: ignore 7 + 8 + from multiformats import CID 9 + 10 + from dag_cbor import encode, decode 11 + from dag_cbor.random import rand_list, rand_dict, rand_int, rand_bytes, rand_str, rand_bool_none, rand_float, rand_cid, options 12 + 13 + nsamples = 1000 14 + 15 + def test_int() -> None: 16 + """ 17 + Encodes random `int` samples with `dag_cbor.encoding.encode`, 18 + encodes them with `cbor2.encoder.dumps` and checks that the two encodings match. 19 + """ 20 + test_data = rand_int(nsamples) 21 + for i, x in enumerate(test_data): 22 + error_msg = f"failed at #{i} = {repr(x)}" 23 + encoded_data = encode(x) 24 + assert decode(encoded_data) == cbor2.loads(encoded_data), error_msg 25 + 26 + def test_special_int() -> None: 27 + """ 28 + Encodes specially crafted `int` samples with `dag_cbor.encoding.encode`, 29 + encodes them with `cbor2.encoder.dumps` and checks that the two encodings match. 30 + """ 31 + exponents = [8, 16, 32, 26] 32 + special_data = [2**e for e in exponents] 33 + special_data += [x-1 for x in special_data] 34 + special_data += [-x for x in special_data] 35 + for i, x in enumerate(special_data): 36 + error_msg = f"failed at #{i} = {repr(x)}" 37 + encoded_data = encode(x) 38 + assert decode(encoded_data) == cbor2.loads(encoded_data), error_msg 39 + 40 + def test_bytes() -> None: 41 + """ 42 + Encodes random `bytes` samples with `dag_cbor.encoding.encode`, 43 + encodes them with `cbor2.encoder.dumps` and checks that the two encodings match. 44 + """ 45 + test_data = rand_bytes(nsamples) 46 + for i, x in enumerate(test_data): 47 + error_msg = f"failed at #{i} = {repr(x)}" 48 + encoded_data = encode(x) 49 + assert decode(encoded_data) == cbor2.loads(encoded_data), error_msg 50 + 51 + def test_str() -> None: 52 + """ 53 + Encodes random `str` samples with `dag_cbor.encoding.encode`, 54 + encodes them with `cbor2.encoder.dumps` and checks that the two encodings match. 55 + """ 56 + test_data = rand_str(nsamples) 57 + for i, x in enumerate(test_data): 58 + error_msg = f"failed at #{i} = {repr(x)}" 59 + encoded_data = encode(x) 60 + assert decode(encoded_data) == cbor2.loads(encoded_data), error_msg 61 + 62 + def test_bool_none() -> None: 63 + """ 64 + Encodes random `Optional[bool]` or samples with `dag_cbor.encoding.encode`, 65 + encodes them with `cbor2.encoder.dumps` and checks that the two encodings match. 66 + """ 67 + test_data = rand_bool_none(nsamples) 68 + for i, x in enumerate(test_data): 69 + error_msg = f"failed at #{i} = {repr(x)}" 70 + encoded_data = encode(x) 71 + assert decode(encoded_data) == cbor2.loads(encoded_data), error_msg 72 + 73 + def test_float() -> None: 74 + """ 75 + Encodes random `float` samples with `dag_cbor.encoding.encode`, 76 + encodes them with `cbor2.encoder.dumps` and checks that the two encodings match. 77 + """ 78 + test_data = rand_float(nsamples) 79 + for i, x in enumerate(test_data): 80 + error_msg = f"failed at #{i} = {repr(x)}" 81 + encoded_data = encode(x) 82 + assert decode(encoded_data) == cbor2.loads(encoded_data), error_msg 83 + 84 + def test_list() -> None: 85 + """ 86 + Encodes random `list` samples with `dag_cbor.encoding.encode`, 87 + encodes them with `cbor2.encoder.dumps` and checks that the two encodings match. 88 + """ 89 + with options(include_cid=False): 90 + test_data = rand_list(nsamples) 91 + for i, x in enumerate(test_data): 92 + error_msg = f"failed at #{i} = {repr(x)}" 93 + encoded_data = encode(x) 94 + assert decode(encoded_data) == cbor2.loads(encoded_data), error_msg 95 + 96 + def test_dict() -> None: 97 + """ 98 + Encodes random `dict` samples with `dag_cbor.encoding.encode`, 99 + encodes them with `cbor2.encoder.dumps` and checks that the two encodings match. 100 + """ 101 + with options(include_cid=False): 102 + test_data = rand_dict(nsamples) 103 + for i, x in enumerate(test_data): 104 + error_msg = f"failed at #{i} = {repr(x)}" 105 + encoded_data = encode(x) 106 + assert decode(encoded_data) == cbor2.loads(encoded_data), error_msg 107 + 108 + def test_cid() -> None: 109 + """ 110 + Encodes random CID samples with `dag_cbor.encoding.encode`, 111 + encodes them with `cbor2.encoder.dumps` and checks that the two encodings match. 112 + """ 113 + test_data = rand_cid(nsamples) 114 + for i, x in enumerate(test_data): 115 + error_msg = f"failed at #{i} = {repr(x)}" 116 + encoded_data = encode(x) 117 + decoded_data = decode(encoded_data) 118 + assert isinstance(decoded_data, CID) 119 + assert cbor2.CBORTag(42, b"\0" + bytes(decoded_data)) == cbor2.loads(encoded_data), error_msg
+53
vendor/git/dag-cbor/test/test_03_concat_and_length.py
··· 1 + """ 2 + Tests on number of bytes written to streams in encoding and read from streams in decoding, 3 + as well as tests on decoding concatenated data. 4 + """ 5 + # pylint: disable = global-statement 6 + 7 + from io import BytesIO 8 + from dag_cbor import encode, decode 9 + from dag_cbor.ipld import IPLDKind 10 + from dag_cbor.random import rand_data 11 + 12 + nsamples = 1000 13 + nconcat = 3 14 + 15 + class BytesReadCounter: 16 + """ Counter for bytes read while decoding. """ 17 + _num_bytes_read: int = 0 18 + def __call__(self, value: IPLDKind, num_bytes_read: int) -> None: 19 + self._num_bytes_read += num_bytes_read 20 + def __int__(self) -> int: 21 + return self._num_bytes_read 22 + 23 + def test_decode_concat_length() -> None: 24 + """ 25 + Encodes random item samples with `dag_cbor.encoding.encode`, then concatenates the bytes of three items. 26 + Decodes with `dag_cbor.decoding.decode` allowing concatenation and checks that the correct number of bytes 27 + are read for each encoded item. 28 + """ 29 + test_data = rand_data(nconcat*nsamples) 30 + for i in range(nsamples): 31 + items = [next(test_data) for j in range(nconcat)] 32 + encoded_items = [encode(x) for x in items] 33 + stream = BytesIO(b''.join(encoded_items)) 34 + for j in range(nconcat): 35 + x = items[j] 36 + bytes_read_cnt = BytesReadCounter() 37 + decode(stream, allow_concat=True, callback=bytes_read_cnt) 38 + error_msg = f"failed at #{i}:{j} = {repr(x)}" 39 + assert len(encoded_items[j]) == int(bytes_read_cnt), error_msg 40 + error_msg = f"failed at #{i}" 41 + assert len(stream.read()) == 0, error_msg 42 + 43 + def test_encode_length() -> None: 44 + """ 45 + Encodes random item samples with `dag_cbor.encoding.encode` to a stream, 46 + then checks that the number of bytes written is the one returned by `encode`. 47 + """ 48 + test_data = rand_data(nsamples) 49 + for i, x in enumerate(test_data): 50 + error_msg = f"failed at #{i} = {repr(x)}" 51 + stream = BytesIO() 52 + num_bytes_written: int = encode(x, stream=stream) 53 + assert len(stream.getvalue()) == num_bytes_written, error_msg
+20
vendor/git/dag-cbor/tox.ini
··· 1 + # content of: tox.ini, put in same dir as setup.py 2 + [tox] 3 + envlist = py37, py38, py39, py310, py311, py312 4 + isolated_build = True 5 + 6 + [testenv] 7 + deps = 8 + -rrequirements.txt 9 + mypy 10 + pylint 11 + pytest 12 + pytest-cov 13 + cbor2==5.4.1 14 + setenv = 15 + PYTHONPATH = {toxinidir} 16 + commands = 17 + pytest 18 + mypy --strict dag_cbor 19 + pylint --errors-only --rcfile=.pylintrc dag_cbor 20 + pylint --exit-zero --rcfile=.pylintrc --disable=fixme dag_cbor