diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml deleted file mode 100644 index 3e3e1df..0000000 --- a/.github/dependabot.yaml +++ /dev/null @@ -1,7 +0,0 @@ -version: 2 -updates: - - - package-ecosystem: github-actions - directory: / - schedule: - interval: monthly diff --git a/.github/release.yaml b/.github/release.yaml deleted file mode 100644 index ee081a1..0000000 --- a/.github/release.yaml +++ /dev/null @@ -1,18 +0,0 @@ -changelog: - exclude: - labels: - - ignore-for-release - authors: - - dependabot - categories: - - title: Breaking Changes πŸ›  - labels: - - semver-major - - breaking-change - - title: New Features πŸŽ‰ - labels: - - semver-minor - - enhancement - - title: Other Changes - labels: - - '*' \ No newline at end of file diff --git a/.github/workflows/bench.yaml b/.github/workflows/bench.yaml deleted file mode 100644 index a12206b..0000000 --- a/.github/workflows/bench.yaml +++ /dev/null @@ -1,38 +0,0 @@ -name: Benchmark - -on: - pull_request: - push: - branches: [main] - -permissions: - contents: write - deployments: write - -jobs: - benchmark: - name: Run benchmarks - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - uses: actions/setup-python@v5 - with: - python-version: "3.10" - - - name: Run benchmarks - run: | - pip install .[dev] - pytest tests/bench.py --benchmark-json benchmark.json - - - name: Report results - uses: benchmark-action/github-action-benchmark@v1 - with: - name: Python Benchmarks - tool: pytest - output-file-path: benchmark.json - github-token: ${{ secrets.GITHUB_TOKEN }} - # Only update results if they are from main - auto-push: ${{ github.ref == 'refs/heads/main' }} - alert-threshold: "110%" - comment-on-alert: true diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml deleted file mode 100644 index a64cd45..0000000 --- a/.github/workflows/ci.yaml +++ /dev/null @@ -1,88 +0,0 @@ -name: CI - -on: - pull_request: - push: - branches: [main] - tags: [v*.*.*] - -jobs: - build: - name: Test & Build - strategy: - matrix: - python-version: ['3.7', '3.8', '3.10'] - image-variant: [''] - include: - - python-version: '2.7' - image-variant: '-buster' - runs-on: [ubuntu-latest] - container: - image: "python:${{ matrix.python-version }}${{ matrix.image-variant }}" - env: - PYTHON: ${{ matrix.python-version }} - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 # fetch all history for setuptools_scm to be able to read tags - - - name: Install python dependencies - run: | - apt-get update - apt-get -y install sudo - pip install --upgrade pip - sudo chown root . - sudo -H pip install wheel build tox - sudo -H pip install .[dev] - - - name: Determine pyenv - id: pyenv - run: echo "value=py$(echo $PYTHON | tr -d '.')" >> $GITHUB_OUTPUT - - - name: Run tests - env: - TOXENV: ${{ steps.pyenv.outputs.value }} - run: tox - - - name: Build python package - run: python -m build - - - name: Upload coverage - uses: codecov/codecov-action@v5 - if: matrix.python-version == '3.10' - with: - token: ${{ secrets.CODECOV_TOKEN }} - env_vars: PYTHON - fail_ci_if_error: true - files: .coverage.${{ steps.pyenv.outputs.value }}.xml - - - uses: actions/upload-artifact@v4 - if: matrix.python-version == '2.7' || matrix.python-version == '3.8' - with: - name: dist-${{ matrix.python-version }} - path: dist - - publish: - name: Publish to PyPI - needs: build - runs-on: [ubuntu-latest] - permissions: - id-token: write - if: github.event_name != 'pull_request' - steps: - - uses: actions/download-artifact@v4 - - - name: Organize files for upload - run: | - mkdir dist - mv dist-3.8/* dist/ - mv dist-2.7/*.whl dist/ - - - name: Test Publish package - uses: pypa/gh-action-pypi-publish@release/v1 - with: - repository-url: https://test.pypi.org/legacy/ - - - name: Publish package - uses: pypa/gh-action-pypi-publish@release/v1 - if: startsWith(github.event.ref, 'refs/tags/v') diff --git a/.github/workflows/cla.yaml b/.github/workflows/cla.yaml deleted file mode 100644 index fa180c6..0000000 --- a/.github/workflows/cla.yaml +++ /dev/null @@ -1,25 +0,0 @@ -name: Check CLA - -on: - issue_comment: - types: [created] - pull_request_target: - types: [opened, closed, synchronize] - -jobs: - cla: - name: Check CLA - runs-on: ubuntu-latest - steps: - - name: CLA Assistant - if: (github.event.comment.body == 'recheck' || github.event.comment.body == 'I have read the CLA Document and I hereby sign the CLA') || github.event_name == 'pull_request_target' - uses: secondlife-3p/contributor-assistant@v2 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - PERSONAL_ACCESS_TOKEN: ${{ secrets.SHARED_CLA_TOKEN }} - with: - branch: main - path-to-document: https://github.com/secondlife/cla/blob/master/CLA.md - path-to-signatures: signatures.json - remote-organization-name: secondlife - remote-repository-name: cla-signatures diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 86fcbe9..0000000 --- a/.gitignore +++ /dev/null @@ -1,287 +0,0 @@ -# Created by https://www.toptal.com/developers/gitignore/api/python,linux,windows,macos,vim,visualstudiocode -# Edit at https://www.toptal.com/developers/gitignore?templates=python,linux,windows,macos,vim,visualstudiocode - -### Linux ### -*~ - -# temporary files which can be created if a process still has a handle open of a deleted file -.fuse_hidden* - -# KDE directory preferences -.directory - -# Linux trash folder which might appear on any partition or disk -.Trash-* - -# .nfs files are created when an open file is removed but is still being accessed -.nfs* - -### macOS ### -# General -.DS_Store -.AppleDouble -.LSOverride - -# Icon must end with two \r -Icon - - -# Thumbnails -._* - -# Files that might appear in the root of a volume -.DocumentRevisions-V100 -.fseventsd -.Spotlight-V100 -.TemporaryItems -.Trashes -.VolumeIcon.icns -.com.apple.timemachine.donotpresent - -# Directories potentially created on remote AFP share -.AppleDB -.AppleDesktop -Network Trash Folder -Temporary Items -.apdisk - -### macOS Patch ### -# iCloud generated files -*.icloud - -### Python ### -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -share/python-wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.nox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -*.py,cover -.hypothesis/ -.pytest_cache/ -cover/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 -db.sqlite3-journal - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -.pybuilder/ -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# IPython -profile_default/ -ipython_config.py - -# pyenv -# For a library or package, you might want to ignore these files since the code is -# intended to run in multiple environments; otherwise, check them in: -# .python-version - -# pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don't work, or not -# install all needed dependencies. -#Pipfile.lock - -# poetry -# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. -# This is especially recommended for binary packages to ensure reproducibility, and is more -# commonly ignored for libraries. -# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control -#poetry.lock - -# pdm -# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. -#pdm.lock -# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it -# in version control. -# https://pdm.fming.dev/#use-with-ide -.pdm.toml - -# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm -__pypackages__/ - -# Celery stuff -celerybeat-schedule -celerybeat.pid - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ -.dmypy.json -dmypy.json - -# Pyre type checker -.pyre/ - -# pytype static type analyzer -.pytype/ - -# Cython debug symbols -cython_debug/ - -# PyCharm -# JetBrains specific template is maintained in a separate JetBrains.gitignore that can -# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore -# and can be added to the global gitignore or merged into this file. For a more nuclear -# option (not recommended) you can uncomment the following to ignore the entire idea folder. -#.idea/ - -### Vim ### -# Swap -[._]*.s[a-v][a-z] -!*.svg # comment out if you don't need vector files -[._]*.sw[a-p] -[._]s[a-rt-v][a-z] -[._]ss[a-gi-z] -[._]sw[a-p] - -# Session -Session.vim -Sessionx.vim - -# Temporary -.netrwhist -# Auto-generated tag files -tags -# Persistent undo -[._]*.un~ - -### VisualStudioCode ### -.vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json -!.vscode/*.code-snippets - -# Local History for Visual Studio Code -.history/ - -# Built Visual Studio Code Extensions -*.vsix - -### VisualStudioCode Patch ### -# Ignore all local history of files -.history -.ionide - -# Support for Project snippet scope -.vscode/*.code-snippets - -# Ignore code-workspaces -*.code-workspace - -### Windows ### -# Windows thumbnail cache files -Thumbs.db -Thumbs.db:encryptable -ehthumbs.db -ehthumbs_vista.db - -# Dump file -*.stackdump - -# Folder config file -[Dd]esktop.ini - -# Recycle Bin used on file shares -$RECYCLE.BIN/ - -# Windows Installer files -*.cab -*.msi -*.msix -*.msm -*.msp - -# Windows shortcuts -*.lnk - -# End of https://www.toptal.com/developers/gitignore/api/python,linux,windows,macos,vim,visualstudiocode - -.benchmarks/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml deleted file mode 100644 index fae0520..0000000 --- a/.pre-commit-config.yaml +++ /dev/null @@ -1,21 +0,0 @@ -repos: -- repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.6.0 - hooks: - - id: check-ast - - id: check-yaml - - id: mixed-line-ending - - id: trailing-whitespace -- repo: https://github.com/pycqa/isort - rev: 5.13.2 - hooks: - - id: isort - args: [--line-length=120] -- repo: local - hooks: - - id: test - name: run pytest - language: system - entry: pytest - pass_filenames: false - types: [python] \ No newline at end of file diff --git a/CREDITS.md b/CREDITS.md deleted file mode 100644 index df3f841..0000000 --- a/CREDITS.md +++ /dev/null @@ -1,4 +0,0 @@ -# Credits - -Thanks to [Tao Takashi](https://github.com/mrtopf) for -the llsd PyPI package name. diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 9d14f49..0000000 --- a/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2006 Linden Research, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/README.md b/README.md deleted file mode 100644 index 829b739..0000000 --- a/README.md +++ /dev/null @@ -1,62 +0,0 @@ -# llsd - -[![codecov](https://codecov.io/gh/secondlife/python-llsd/branch/main/graph/badge.svg?token=Y0CD45CTNI)](https://codecov.io/gh/secondlife/python-llsd) - -Official python serialization library for [Linden Lab Structured Data (LLSD)][llsd]. - -## Use - -Install **llsd** with pip: -``` -pip install llsd -``` - -Use **llsd** to parse/format your data: -```py -import llsd - -data = {"foo": "bar"} - -# Format - -data_xml = llsd.format_xml(data) -# >>> 'foobar' -data_notation = llsd.format_notation(data) -# >>> "{'foo':'bar'}" -data_binary = llsd.format_binary(data) -# >>> '\n{\x00\x00\x00\x01k\x00\x00\x00\x03foos\x00\x00\x00\x03bar}' - -# Parse - -data = llsd.parse(data_xml) -# >>> {'foo: 'bar'} -data = llsd.parse(data_notation) -# >>> {'foo: 'bar'} -data = llsd.parse(data_binary) -# >>> {'foo: 'bar'} -``` - -## Develop - -Requirements: - -- [pre-commit](https://pre-commit.com/) - -Set up a venv and install development dependencies: -``` -python3 -m venv .venv -. .venv/bin/activate -pip install .[dev] -``` - -Run tests: -``` -pytest -``` - -### Benchmarks - -Benchmarks from commits to `main` are published [here](https://secondlife.github.io/python-llsd/dev/bench/). - -[llsd]: https://wiki.secondlife.com/wiki/LLSD -[llbase]: https://pypi.org/project/llbase/ diff --git a/dev/bench/data.js b/dev/bench/data.js new file mode 100644 index 0000000..543db0a --- /dev/null +++ b/dev/bench/data.js @@ -0,0 +1,2117 @@ +window.BENCHMARK_DATA = { + "lastUpdate": 1742056357911, + "repoUrl": "https://github.com/secondlife/python-llsd", + "entries": { + "Python Benchmarks": [ + { + "commit": { + "author": { + "email": "signal@lindenlab.com", + "name": "Signal Linden", + "username": "bennettgoble" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": true, + "id": "8f5fabab741e3427f85be880d28af5cb9dcfc353", + "message": "Merge pull request #10 from secondlife/signal/bench-summary\n\nPublish benchmarks in job summary", + "timestamp": "2023-03-16T12:51:09-07:00", + "tree_id": "f8f2a6eef383b51ca5c29485a3ac2bc7d9609009", + "url": "https://github.com/secondlife/python-llsd/commit/8f5fabab741e3427f85be880d28af5cb9dcfc353" + }, + "date": 1678996298253, + "tool": "pytest", + "benches": [ + { + "name": "tests/bench.py::test_parse_xml_stream", + "value": 12872.181812208772, + "unit": "iter/sec", + "range": "stddev: 0.0000022186791421610883", + "extra": "mean: 77.68690767337813 usec\nrounds: 4874" + }, + { + "name": "tests/bench.py::test_parse_notation_stream", + "value": 2205.4076588476173, + "unit": "iter/sec", + "range": "stddev: 0.00019966932409915566", + "extra": "mean: 453.43090924175266 usec\nrounds: 1807" + }, + { + "name": "tests/bench.py::test_parse_binary_stream", + "value": 12166.264529452834, + "unit": "iter/sec", + "range": "stddev: 0.00003357310024603729", + "extra": "mean: 82.19449754517001 usec\nrounds: 7740" + }, + { + "name": "tests/bench.py::test_parse_notation_bytes", + "value": 4002.991825681371, + "unit": "iter/sec", + "range": "stddev: 0.00003568834188397126", + "extra": "mean: 249.81315065008522 usec\nrounds: 3153" + }, + { + "name": "tests/bench.py::test_parse_xml_bytes", + "value": 13982.993986334646, + "unit": "iter/sec", + "range": "stddev: 0.0000017384725864036928", + "extra": "mean: 71.5154423278222 usec\nrounds: 6667" + }, + { + "name": "tests/bench.py::test_parse_binary_bytes", + "value": 14578.919949218085, + "unit": "iter/sec", + "range": "stddev: 0.00003596055356104874", + "extra": "mean: 68.59218676577159 usec\nrounds: 8765" + }, + { + "name": "tests/bench.py::test_format_xml", + "value": 14470.950234925818, + "unit": "iter/sec", + "range": "stddev: 0.0000017050134935794276", + "extra": "mean: 69.10396233596931 usec\nrounds: 7381" + }, + { + "name": "tests/bench.py::test_format_notation", + "value": 24882.69811579331, + "unit": "iter/sec", + "range": "stddev: 0.000014272846192442663", + "extra": "mean: 40.18856778900876 usec\nrounds: 2198" + }, + { + "name": "tests/bench.py::test_format_binary", + "value": 26489.816812509813, + "unit": "iter/sec", + "range": "stddev: 9.922669037536676e-7", + "extra": "mean: 37.75035543196925 usec\nrounds: 11313" + } + ] + }, + { + "commit": { + "author": { + "email": "signal@lindenlab.com", + "name": "Signal Linden", + "username": "bennettgoble" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": true, + "id": "438e41d47687af3040be60a73f01f80cb7c5009a", + "message": "Merge pull request #9 from secondlife/sl-18330-fix\n\nSL-18830: Fix sporadic notation parse failure with very large input.", + "timestamp": "2023-03-16T13:02:10-07:00", + "tree_id": "d6af6f478047eb93b3f084e047405efb7302c204", + "url": "https://github.com/secondlife/python-llsd/commit/438e41d47687af3040be60a73f01f80cb7c5009a" + }, + "date": 1678996960629, + "tool": "pytest", + "benches": [ + { + "name": "tests/bench.py::test_parse_xml_stream", + "value": 12495.159989307285, + "unit": "iter/sec", + "range": "stddev: 0.000001796551337468087", + "extra": "mean: 80.03098806703944 usec\nrounds: 4609" + }, + { + "name": "tests/bench.py::test_parse_notation_stream", + "value": 3009.6538886291164, + "unit": "iter/sec", + "range": "stddev: 0.00021585645151115033", + "extra": "mean: 332.26411973088886 usec\nrounds: 2230" + }, + { + "name": "tests/bench.py::test_parse_binary_stream", + "value": 13524.361867793872, + "unit": "iter/sec", + "range": "stddev: 0.000031128561715433327", + "extra": "mean: 73.9406420632194 usec\nrounds: 8278" + }, + { + "name": "tests/bench.py::test_parse_notation_bytes", + "value": 3703.0402892099933, + "unit": "iter/sec", + "range": "stddev: 0.00003487006193489698", + "extra": "mean: 270.0483715810016 usec\nrounds: 2998" + }, + { + "name": "tests/bench.py::test_parse_xml_bytes", + "value": 13713.750342074072, + "unit": "iter/sec", + "range": "stddev: 0.0000020156423101261827", + "extra": "mean: 72.91951326632942 usec\nrounds: 6671" + }, + { + "name": "tests/bench.py::test_parse_binary_bytes", + "value": 14466.237302542444, + "unit": "iter/sec", + "range": "stddev: 0.00003324372179853647", + "extra": "mean: 69.12647560566766 usec\nrounds: 8547" + }, + { + "name": "tests/bench.py::test_format_xml", + "value": 15020.580807426615, + "unit": "iter/sec", + "range": "stddev: 0.0000012655967514258396", + "extra": "mean: 66.57532174159142 usec\nrounds: 7189" + }, + { + "name": "tests/bench.py::test_format_notation", + "value": 25999.305531143098, + "unit": "iter/sec", + "range": "stddev: 0.000013443974619298474", + "extra": "mean: 38.462565809773515 usec\nrounds: 12331" + }, + { + "name": "tests/bench.py::test_format_binary", + "value": 26090.624414662354, + "unit": "iter/sec", + "range": "stddev: 0.0000010597874351514089", + "extra": "mean: 38.3279443261627 usec\nrounds: 11262" + } + ] + }, + { + "commit": { + "author": { + "email": "nat@lindenlab.com", + "name": "nat-goodspeed", + "username": "nat-goodspeed" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": true, + "id": "c4035fb6ed54bafbb968d778a0e9fa60cb65e74f", + "message": "Merge pull request #11 from secondlife/signal/bench-readme\n\nAdd basic development instructions", + "timestamp": "2023-03-16T17:20:51-04:00", + "tree_id": "a9a8fad2902645e32f4cf61fbcceb95ec10df74a", + "url": "https://github.com/secondlife/python-llsd/commit/c4035fb6ed54bafbb968d778a0e9fa60cb65e74f" + }, + "date": 1679001686035, + "tool": "pytest", + "benches": [ + { + "name": "tests/bench.py::test_parse_xml_stream", + "value": 8300.473669688761, + "unit": "iter/sec", + "range": "stddev: 0.00005706394950755007", + "extra": "mean: 120.47505236378835 usec\nrounds: 3342" + }, + { + "name": "tests/bench.py::test_parse_notation_stream", + "value": 2170.2928269190556, + "unit": "iter/sec", + "range": "stddev: 0.00029043219624840633", + "extra": "mean: 460.76731563436005 usec\nrounds: 1695" + }, + { + "name": "tests/bench.py::test_parse_binary_stream", + "value": 9755.97508852484, + "unit": "iter/sec", + "range": "stddev: 0.00006527358784005084", + "extra": "mean: 102.50128674233892 usec\nrounds: 5280" + }, + { + "name": "tests/bench.py::test_parse_notation_bytes", + "value": 2657.2559583178227, + "unit": "iter/sec", + "range": "stddev: 0.0001100800966197872", + "extra": "mean: 376.32806763299175 usec\nrounds: 1863" + }, + { + "name": "tests/bench.py::test_parse_xml_bytes", + "value": 9057.213634529715, + "unit": "iter/sec", + "range": "stddev: 0.000060535948325585013", + "extra": "mean: 110.40923184008828 usec\nrounds: 5603" + }, + { + "name": "tests/bench.py::test_parse_binary_bytes", + "value": 10356.718680816699, + "unit": "iter/sec", + "range": "stddev: 0.00006844433281053462", + "extra": "mean: 96.55567857146266 usec\nrounds: 6216" + }, + { + "name": "tests/bench.py::test_format_xml", + "value": 10817.987785820233, + "unit": "iter/sec", + "range": "stddev: 0.00005813904804121425", + "extra": "mean: 92.43863274746514 usec\nrounds: 2162" + }, + { + "name": "tests/bench.py::test_format_notation", + "value": 18440.266312430256, + "unit": "iter/sec", + "range": "stddev: 0.000051923028874614916", + "extra": "mean: 54.229151740933254 usec\nrounds: 10742" + }, + { + "name": "tests/bench.py::test_format_binary", + "value": 18598.440988295966, + "unit": "iter/sec", + "range": "stddev: 0.00003668074092684782", + "extra": "mean: 53.76794757309508 usec\nrounds: 9785" + } + ] + }, + { + "commit": { + "author": { + "email": "nat@lindenlab.com", + "name": "nat-goodspeed", + "username": "nat-goodspeed" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": true, + "id": "0adae956ed43a09d71d7200b11eec525a6d7b0d6", + "message": "Merge pull request #5 from secondlife/sl-19314\n\nSL-19314: Recast llsd serialization to write to a stream.", + "timestamp": "2023-03-20T14:41:09-04:00", + "tree_id": "b553a8ff4e2aa4d7c664e0fd49b42f534965f057", + "url": "https://github.com/secondlife/python-llsd/commit/0adae956ed43a09d71d7200b11eec525a6d7b0d6" + }, + "date": 1679337702394, + "tool": "pytest", + "benches": [ + { + "name": "tests/bench.py::test_parse_xml_stream", + "value": 10141.084204071363, + "unit": "iter/sec", + "range": "stddev: 0.00006546413149893282", + "extra": "mean: 98.60878579417847 usec\nrounds: 3590" + }, + { + "name": "tests/bench.py::test_parse_notation_stream", + "value": 2451.860006533864, + "unit": "iter/sec", + "range": "stddev: 0.0003483534560991509", + "extra": "mean: 407.8536284025759 usec\nrounds: 1690" + }, + { + "name": "tests/bench.py::test_parse_binary_stream", + "value": 10133.009280781782, + "unit": "iter/sec", + "range": "stddev: 0.0001688658022580344", + "extra": "mean: 98.68736643679932 usec\nrounds: 6817" + }, + { + "name": "tests/bench.py::test_parse_notation_bytes", + "value": 2854.595266990118, + "unit": "iter/sec", + "range": "stddev: 0.0002153905641903825", + "extra": "mean: 350.3123583100447 usec\nrounds: 2509" + }, + { + "name": "tests/bench.py::test_parse_xml_bytes", + "value": 10584.71309520326, + "unit": "iter/sec", + "range": "stddev: 0.00015353939907668615", + "extra": "mean: 94.4758720435395 usec\nrounds: 930" + }, + { + "name": "tests/bench.py::test_parse_binary_bytes", + "value": 11095.315584964033, + "unit": "iter/sec", + "range": "stddev: 0.00016813694472493784", + "extra": "mean: 90.12812590523909 usec\nrounds: 7593" + }, + { + "name": "tests/bench.py::test_format_xml", + "value": 10565.237942231777, + "unit": "iter/sec", + "range": "stddev: 0.00008288246169716064", + "extra": "mean: 94.65002165287365 usec\nrounds: 1755" + }, + { + "name": "tests/bench.py::test_format_notation", + "value": 20531.269478147016, + "unit": "iter/sec", + "range": "stddev: 0.00004856970267587009", + "extra": "mean: 48.706194279139716 usec\nrounds: 10593" + }, + { + "name": "tests/bench.py::test_format_binary", + "value": 19827.7841545781, + "unit": "iter/sec", + "range": "stddev: 0.00007648882122992257", + "extra": "mean: 50.43427910067837 usec\nrounds: 9785" + } + ] + }, + { + "commit": { + "author": { + "email": "nat@lindenlab.com", + "name": "nat-goodspeed", + "username": "nat-goodspeed" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": true, + "id": "09310464c7c5ec5df260a151ed191d2a72898d9c", + "message": "Merge pull request #6 from secondlife/sl-18330-perf\n\nSL-18330: Refactor notation parsing to manage a lookahead char.", + "timestamp": "2023-03-22T16:36:50-04:00", + "tree_id": "48e9a5ffd5491d9afb542d5dcc3b3b6daa3e7638", + "url": "https://github.com/secondlife/python-llsd/commit/09310464c7c5ec5df260a151ed191d2a72898d9c" + }, + "date": 1679517446149, + "tool": "pytest", + "benches": [ + { + "name": "tests/bench.py::test_parse_xml_stream", + "value": 12909.923296042954, + "unit": "iter/sec", + "range": "stddev: 0.0000015962367173426108", + "extra": "mean: 77.45979407224766 usec\nrounds: 4521" + }, + { + "name": "tests/bench.py::test_parse_notation_stream", + "value": 4227.471582622453, + "unit": "iter/sec", + "range": "stddev: 0.00017779182856725737", + "extra": "mean: 236.54801231795955 usec\nrounds: 2192" + }, + { + "name": "tests/bench.py::test_parse_binary_stream", + "value": 15563.761914953298, + "unit": "iter/sec", + "range": "stddev: 0.000042570073606750205", + "extra": "mean: 64.2518181314007 usec\nrounds: 9001" + }, + { + "name": "tests/bench.py::test_parse_notation_bytes", + "value": 4641.493022455551, + "unit": "iter/sec", + "range": "stddev: 0.000029440320878755105", + "extra": "mean: 215.44791625496327 usec\nrounds: 3642" + }, + { + "name": "tests/bench.py::test_parse_xml_bytes", + "value": 15632.688204563447, + "unit": "iter/sec", + "range": "stddev: 0.0000013954437107169295", + "extra": "mean: 63.96852460142351 usec\nrounds: 7337" + }, + { + "name": "tests/bench.py::test_parse_binary_bytes", + "value": 17080.303553675392, + "unit": "iter/sec", + "range": "stddev: 0.00003450061722708274", + "extra": "mean: 58.54696884381875 usec\nrounds: 10977" + }, + { + "name": "tests/bench.py::test_format_xml", + "value": 13383.90740274769, + "unit": "iter/sec", + "range": "stddev: 0.000013177191563706168", + "extra": "mean: 74.71659582721723 usec\nrounds: 6950" + }, + { + "name": "tests/bench.py::test_format_notation", + "value": 25066.830254749835, + "unit": "iter/sec", + "range": "stddev: 0.000013077455730759876", + "extra": "mean: 39.893356672430215 usec\nrounds: 13211" + }, + { + "name": "tests/bench.py::test_format_binary", + "value": 26214.91177645831, + "unit": "iter/sec", + "range": "stddev: 8.712771456782391e-7", + "extra": "mean: 38.146227938025206 usec\nrounds: 11683" + } + ] + }, + { + "commit": { + "author": { + "email": "signal@lindenlab.com", + "name": "Signal Linden", + "username": "bennettgoble" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": true, + "id": "ed393fde08e3669bbc9d2d9214aa10e1022b2563", + "message": "Add PyPI trusted publication (#12)\n\nAdd PyPI trusted publication\r\n\r\nPublish llsd with PyPI's new [trusted publisher](https://blog.pypi.org/posts/2023-04-20-introducing-trusted-publishers/) functionality rather than an access token.", + "timestamp": "2023-04-21T10:39:22-07:00", + "tree_id": "b9dc9d85eba28f2b9b1ed09b6201cb3b42838560", + "url": "https://github.com/secondlife/python-llsd/commit/ed393fde08e3669bbc9d2d9214aa10e1022b2563" + }, + "date": 1682098796079, + "tool": "pytest", + "benches": [ + { + "name": "tests/bench.py::test_parse_xml_stream", + "value": 8401.410233480117, + "unit": "iter/sec", + "range": "stddev: 0.000028143593606264825", + "extra": "mean: 119.02763610029906 usec\nrounds: 3108" + }, + { + "name": "tests/bench.py::test_parse_notation_stream", + "value": 3103.4882017496516, + "unit": "iter/sec", + "range": "stddev: 0.0002465618766434401", + "extra": "mean: 322.2180768840141 usec\nrounds: 1964" + }, + { + "name": "tests/bench.py::test_parse_binary_stream", + "value": 11845.999955317911, + "unit": "iter/sec", + "range": "stddev: 0.00007200932853574851", + "extra": "mean: 84.41668105452588 usec\nrounds: 5653" + }, + { + "name": "tests/bench.py::test_parse_notation_bytes", + "value": 3598.045609310651, + "unit": "iter/sec", + "range": "stddev: 0.00008768229864581673", + "extra": "mean: 277.9286614411733 usec\nrounds: 3317" + }, + { + "name": "tests/bench.py::test_parse_xml_bytes", + "value": 9989.97967818425, + "unit": "iter/sec", + "range": "stddev: 0.00005278396556448257", + "extra": "mean: 100.10030372571859 usec\nrounds: 2657" + }, + { + "name": "tests/bench.py::test_parse_binary_bytes", + "value": 14149.262982134755, + "unit": "iter/sec", + "range": "stddev: 0.000047877897681145966", + "extra": "mean: 70.67505927783147 usec\nrounds: 8418" + }, + { + "name": "tests/bench.py::test_format_xml", + "value": 10445.428648020821, + "unit": "iter/sec", + "range": "stddev: 0.000033306463552091626", + "extra": "mean: 95.73565946376723 usec\nrounds: 4998" + }, + { + "name": "tests/bench.py::test_format_notation", + "value": 20189.290617260536, + "unit": "iter/sec", + "range": "stddev: 0.00002340255990154317", + "extra": "mean: 49.53121033113787 usec\nrounds: 8905" + }, + { + "name": "tests/bench.py::test_format_binary", + "value": 21068.91781205809, + "unit": "iter/sec", + "range": "stddev: 0.000019710617091594125", + "extra": "mean: 47.46328259098734 usec\nrounds: 10977" + } + ] + }, + { + "commit": { + "author": { + "email": "signal@lindenlab.com", + "name": "Signal Linden", + "username": "bennettgoble" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": true, + "id": "46fdce0be06eb90cc2bf0bb4d1736cd81844b890", + "message": "Switch PyPI action param case to kebab\n\nFixes deprecation warning about repository_url.", + "timestamp": "2023-05-04T12:36:02-07:00", + "tree_id": "e77533ea846462ef801ec06f8ad47aa995ba4a5e", + "url": "https://github.com/secondlife/python-llsd/commit/46fdce0be06eb90cc2bf0bb4d1736cd81844b890" + }, + "date": 1683228996474, + "tool": "pytest", + "benches": [ + { + "name": "tests/bench.py::test_parse_xml_stream", + "value": 8596.225166748598, + "unit": "iter/sec", + "range": "stddev: 0.00005808476456724832", + "extra": "mean: 116.33013102869153 usec\nrounds: 3587" + }, + { + "name": "tests/bench.py::test_parse_notation_stream", + "value": 3215.5453601785107, + "unit": "iter/sec", + "range": "stddev: 0.0002668789446984807", + "extra": "mean: 310.98923759062916 usec\nrounds: 1793" + }, + { + "name": "tests/bench.py::test_parse_binary_stream", + "value": 12292.815169082878, + "unit": "iter/sec", + "range": "stddev: 0.0000753499327366744", + "extra": "mean: 81.34833121993539 usec\nrounds: 7886" + }, + { + "name": "tests/bench.py::test_parse_notation_bytes", + "value": 3697.3929771043913, + "unit": "iter/sec", + "range": "stddev: 0.00014229848015188685", + "extra": "mean: 270.4608371878146 usec\nrounds: 3243" + }, + { + "name": "tests/bench.py::test_parse_xml_bytes", + "value": 11152.30995060082, + "unit": "iter/sec", + "range": "stddev: 0.00007596157896915804", + "extra": "mean: 89.66752219311533 usec\nrounds: 6511" + }, + { + "name": "tests/bench.py::test_parse_binary_bytes", + "value": 14294.424712368927, + "unit": "iter/sec", + "range": "stddev: 0.000058709002444133834", + "extra": "mean: 69.95734491747 usec\nrounds: 8834" + }, + { + "name": "tests/bench.py::test_format_xml", + "value": 11150.297285691477, + "unit": "iter/sec", + "range": "stddev: 0.00005536912357994805", + "extra": "mean: 89.68370747237758 usec\nrounds: 5179" + }, + { + "name": "tests/bench.py::test_format_notation", + "value": 21237.79814122248, + "unit": "iter/sec", + "range": "stddev: 0.00003803726295527947", + "extra": "mean: 47.085860471524306 usec\nrounds: 10858" + }, + { + "name": "tests/bench.py::test_format_binary", + "value": 21154.097004896656, + "unit": "iter/sec", + "range": "stddev: 0.000040814894197669976", + "extra": "mean: 47.27216669983712 usec\nrounds: 10030" + } + ] + }, + { + "commit": { + "author": { + "email": "nat@lindenlab.com", + "name": "nat-goodspeed", + "username": "nat-goodspeed" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": true, + "id": "a63abbef1ed29c4e750529854b7f2c43fbe360b8", + "message": "Merge pull request #13 from secondlife/log/deep_map\n\nSL-18330: In XML formatter, avoid adding call stack depth.", + "timestamp": "2023-05-08T10:13:57-04:00", + "tree_id": "c9e8fa449ffcd8dd71fb48e9d3daddac89f3c115", + "url": "https://github.com/secondlife/python-llsd/commit/a63abbef1ed29c4e750529854b7f2c43fbe360b8" + }, + "date": 1683555267712, + "tool": "pytest", + "benches": [ + { + "name": "tests/bench.py::test_parse_xml_stream", + "value": 12385.13965525791, + "unit": "iter/sec", + "range": "stddev: 0.000003357671187857512", + "extra": "mean: 80.74192361452026 usec\nrounds: 4294" + }, + { + "name": "tests/bench.py::test_parse_notation_stream", + "value": 4316.033655275787, + "unit": "iter/sec", + "range": "stddev: 0.00017824729524758487", + "extra": "mean: 231.69420812500633 usec\nrounds: 2806" + }, + { + "name": "tests/bench.py::test_parse_binary_stream", + "value": 15655.984973123714, + "unit": "iter/sec", + "range": "stddev: 0.000030265184103295064", + "extra": "mean: 63.87333672820191 usec\nrounds: 9524" + }, + { + "name": "tests/bench.py::test_parse_notation_bytes", + "value": 4713.246069957984, + "unit": "iter/sec", + "range": "stddev: 0.00002772524407139972", + "extra": "mean: 212.16800166109607 usec\nrounds: 3612" + }, + { + "name": "tests/bench.py::test_parse_xml_bytes", + "value": 14963.893650457858, + "unit": "iter/sec", + "range": "stddev: 0.0000016281721266114382", + "extra": "mean: 66.8275265354751 usec\nrounds: 3109" + }, + { + "name": "tests/bench.py::test_parse_binary_bytes", + "value": 17550.267383165712, + "unit": "iter/sec", + "range": "stddev: 0.00003116645055969604", + "extra": "mean: 56.979188873168056 usec\nrounds: 10515" + }, + { + "name": "tests/bench.py::test_format_xml", + "value": 17118.960322648418, + "unit": "iter/sec", + "range": "stddev: 0.000012240580240138473", + "extra": "mean: 58.41476241270318 usec\nrounds: 8439" + }, + { + "name": "tests/bench.py::test_format_notation", + "value": 26326.55225540916, + "unit": "iter/sec", + "range": "stddev: 0.000011888050812629382", + "extra": "mean: 37.984464896824306 usec\nrounds: 9643" + }, + { + "name": "tests/bench.py::test_format_binary", + "value": 25760.089405910738, + "unit": "iter/sec", + "range": "stddev: 0.000001149915234740709", + "extra": "mean: 38.81974104369944 usec\nrounds: 11249" + } + ] + }, + { + "commit": { + "author": { + "email": "roxanne@roxiware.com", + "name": "Roxanne Skelly", + "username": "roxanneskelly" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": true, + "id": "b703873ef2f5e09155b5d6e58e841145437a3c27", + "message": "Merge pull request #15 from secondlife/SRV-439\n\nSRV-439 - performance optimizations for string handling in xml formatting", + "timestamp": "2023-09-07T13:13:19-07:00", + "tree_id": "90faa284611ea2757c433e5d983d07c90b32965f", + "url": "https://github.com/secondlife/python-llsd/commit/b703873ef2f5e09155b5d6e58e841145437a3c27" + }, + "date": 1694117688208, + "tool": "pytest", + "benches": [ + { + "name": "tests/bench.py::test_parse_xml_stream", + "value": 12400.876930837765, + "unit": "iter/sec", + "range": "stddev: 0.0000020154217057357803", + "extra": "mean: 80.63945844936653 usec\nrounds: 4308" + }, + { + "name": "tests/bench.py::test_parse_notation_stream", + "value": 4285.042083926906, + "unit": "iter/sec", + "range": "stddev: 0.00002688969382671492", + "extra": "mean: 233.36993672733738 usec\nrounds: 2750" + }, + { + "name": "tests/bench.py::test_parse_binary_stream", + "value": 15502.002579149737, + "unit": "iter/sec", + "range": "stddev: 0.00003278538982642423", + "extra": "mean: 64.50779471195575 usec\nrounds: 8510" + }, + { + "name": "tests/bench.py::test_parse_notation_bytes", + "value": 4608.93699067061, + "unit": "iter/sec", + "range": "stddev: 0.00002849527827174635", + "extra": "mean: 216.96977025813015 usec\nrounds: 3517" + }, + { + "name": "tests/bench.py::test_parse_xml_bytes", + "value": 15178.721126042934, + "unit": "iter/sec", + "range": "stddev: 0.0000015114742756373176", + "extra": "mean: 65.88170318804048 usec\nrounds: 7183" + }, + { + "name": "tests/bench.py::test_parse_binary_bytes", + "value": 16951.5580969395, + "unit": "iter/sec", + "range": "stddev: 0.00003407958627938856", + "extra": "mean: 58.99162745285012 usec\nrounds: 10039" + }, + { + "name": "tests/bench.py::test_format_xml", + "value": 19329.48894091656, + "unit": "iter/sec", + "range": "stddev: 0.00001291174012510934", + "extra": "mean: 51.73442521199851 usec\nrounds: 7782" + }, + { + "name": "tests/bench.py::test_format_notation", + "value": 23281.178240620364, + "unit": "iter/sec", + "range": "stddev: 0.000012682945522827392", + "extra": "mean: 42.95315252796043 usec\nrounds: 10503" + }, + { + "name": "tests/bench.py::test_format_binary", + "value": 25350.441553605502, + "unit": "iter/sec", + "range": "stddev: 9.170085610769461e-7", + "extra": "mean: 39.44704465543219 usec\nrounds: 11376" + }, + { + "name": "tests/bench.py::test_format_xml_deep", + "value": 220.80638639515766, + "unit": "iter/sec", + "range": "stddev: 0.00006939136434361314", + "extra": "mean: 4.528854515151516 msec\nrounds: 198" + }, + { + "name": "tests/bench.py::test_format_xml_wide", + "value": 0.5439629781791014, + "unit": "iter/sec", + "range": "stddev: 0.004288264931640944", + "extra": "mean: 1.838360403400003 sec\nrounds: 5" + }, + { + "name": "tests/bench.py::test_format_notation_deep", + "value": 282.30209368668704, + "unit": "iter/sec", + "range": "stddev: 0.000037876346844994716", + "extra": "mean: 3.54230458209017 msec\nrounds: 268" + }, + { + "name": "tests/bench.py::test_format_notation_wide", + "value": 0.6950752910135733, + "unit": "iter/sec", + "range": "stddev: 0.004344220335527082", + "extra": "mean: 1.4386930638000082 sec\nrounds: 5" + }, + { + "name": "tests/bench.py::test_format_notation_wide_array", + "value": 1.1267186085687908, + "unit": "iter/sec", + "range": "stddev: 0.0021471007159548573", + "extra": "mean: 887.5330471999973 msec\nrounds: 5" + }, + { + "name": "tests/bench.py::test_format_binary_deep", + "value": 250.0105358877701, + "unit": "iter/sec", + "range": "stddev: 0.000007938472069168623", + "extra": "mean: 3.999831432899695 msec\nrounds: 231" + }, + { + "name": "tests/bench.py::test_format_binary_wide", + "value": 0.6229985032994216, + "unit": "iter/sec", + "range": "stddev: 0.0011434795723045251", + "extra": "mean: 1.6051402928000074 sec\nrounds: 5" + }, + { + "name": "tests/bench.py::test_format_binary_wide_array", + "value": 0.8591906594760393, + "unit": "iter/sec", + "range": "stddev: 0.006885452751917601", + "extra": "mean: 1.1638860234000106 sec\nrounds: 5" + }, + { + "name": "tests/bench.py::test_parse_xml_deep", + "value": 257.7893561696026, + "unit": "iter/sec", + "range": "stddev: 0.005908778845637322", + "extra": "mean: 3.8791361088705627 msec\nrounds: 248" + }, + { + "name": "tests/bench.py::test_parse_binary_deep", + "value": 202.06927819673965, + "unit": "iter/sec", + "range": "stddev: 0.000034898422119658154", + "extra": "mean: 4.948797803030579 msec\nrounds: 198" + } + ] + }, + { + "commit": { + "author": { + "email": "nat@lindenlab.com", + "name": "nat-goodspeed", + "username": "nat-goodspeed" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": true, + "id": "9655ab4629c37881b223060972a1155d4c4f58e0", + "message": "Merge pull request #16 from secondlife/dependabot/github_actions/actions/checkout-4\n\nBump actions/checkout from 3 to 4", + "timestamp": "2023-10-05T17:21:08-04:00", + "tree_id": "5784caaeff5a07eb18d6fe83495cfacf54e70c3c", + "url": "https://github.com/secondlife/python-llsd/commit/9655ab4629c37881b223060972a1155d4c4f58e0" + }, + "date": 1696540958005, + "tool": "pytest", + "benches": [ + { + "name": "tests/bench.py::test_parse_xml_stream", + "value": 11892.318545601172, + "unit": "iter/sec", + "range": "stddev: 0.000001939030663054047", + "extra": "mean: 84.08789221088331 usec\nrounds: 3980" + }, + { + "name": "tests/bench.py::test_parse_notation_stream", + "value": 4015.1881579431906, + "unit": "iter/sec", + "range": "stddev: 0.00005715691360910909", + "extra": "mean: 249.0543308715717 usec\nrounds: 2708" + }, + { + "name": "tests/bench.py::test_parse_binary_stream", + "value": 14850.95786138028, + "unit": "iter/sec", + "range": "stddev: 0.00003053199286817588", + "extra": "mean: 67.33572402090553 usec\nrounds: 8555" + }, + { + "name": "tests/bench.py::test_parse_notation_bytes", + "value": 4299.189034759652, + "unit": "iter/sec", + "range": "stddev: 0.00002782497891442754", + "extra": "mean: 232.60200747509245 usec\nrounds: 3077" + }, + { + "name": "tests/bench.py::test_parse_xml_bytes", + "value": 14303.536946792752, + "unit": "iter/sec", + "range": "stddev: 0.0000036565118611130075", + "extra": "mean: 69.91277777796265 usec\nrounds: 6579" + }, + { + "name": "tests/bench.py::test_parse_binary_bytes", + "value": 16287.337742606274, + "unit": "iter/sec", + "range": "stddev: 0.000031853820678365714", + "extra": "mean: 61.39738831497833 usec\nrounds: 8130" + }, + { + "name": "tests/bench.py::test_format_xml", + "value": 18583.04347717172, + "unit": "iter/sec", + "range": "stddev: 0.000013046413893901367", + "extra": "mean: 53.81249854085779 usec\nrounds: 8224" + }, + { + "name": "tests/bench.py::test_format_notation", + "value": 22648.759081649958, + "unit": "iter/sec", + "range": "stddev: 0.000012418730168823472", + "extra": "mean: 44.15252934586605 usec\nrounds: 9814" + }, + { + "name": "tests/bench.py::test_format_binary", + "value": 23782.58906357611, + "unit": "iter/sec", + "range": "stddev: 0.0000024632024686721427", + "extra": "mean: 42.047566702127305 usec\nrounds: 11274" + }, + { + "name": "tests/bench.py::test_format_xml_deep", + "value": 208.88704459288252, + "unit": "iter/sec", + "range": "stddev: 0.00010163733616835158", + "extra": "mean: 4.787276309782562 msec\nrounds: 184" + }, + { + "name": "tests/bench.py::test_format_xml_wide", + "value": 0.5155495379202011, + "unit": "iter/sec", + "range": "stddev: 0.007611663073909226", + "extra": "mean: 1.9396778126000072 sec\nrounds: 5" + }, + { + "name": "tests/bench.py::test_format_notation_deep", + "value": 265.2713558123921, + "unit": "iter/sec", + "range": "stddev: 0.000060017055098687275", + "extra": "mean: 3.7697247670691976 msec\nrounds: 249" + }, + { + "name": "tests/bench.py::test_format_notation_wide", + "value": 0.662776814607169, + "unit": "iter/sec", + "range": "stddev: 0.0010740100628152446", + "extra": "mean: 1.508803533799994 sec\nrounds: 5" + }, + { + "name": "tests/bench.py::test_format_notation_wide_array", + "value": 1.093607756147928, + "unit": "iter/sec", + "range": "stddev: 0.0014775191073895769", + "extra": "mean: 914.4046339999932 msec\nrounds: 5" + }, + { + "name": "tests/bench.py::test_format_binary_deep", + "value": 230.31163847318945, + "unit": "iter/sec", + "range": "stddev: 0.000009700978028588764", + "extra": "mean: 4.3419429718329665 msec\nrounds: 213" + }, + { + "name": "tests/bench.py::test_format_binary_wide", + "value": 0.5639411660726928, + "unit": "iter/sec", + "range": "stddev: 0.007065359177845713", + "extra": "mean: 1.7732346212000039 sec\nrounds: 5" + }, + { + "name": "tests/bench.py::test_format_binary_wide_array", + "value": 0.7780793318289493, + "unit": "iter/sec", + "range": "stddev: 0.0014327402783182118", + "extra": "mean: 1.2852159915999892 sec\nrounds: 5" + }, + { + "name": "tests/bench.py::test_parse_xml_deep", + "value": 278.1522972143162, + "unit": "iter/sec", + "range": "stddev: 0.000026585225235979346", + "extra": "mean: 3.595152763485899 msec\nrounds: 241" + }, + { + "name": "tests/bench.py::test_parse_binary_deep", + "value": 193.76847420924514, + "unit": "iter/sec", + "range": "stddev: 0.000037005842480491656", + "extra": "mean: 5.160798236560031 msec\nrounds: 186" + } + ] + }, + { + "commit": { + "author": { + "email": "nat@lindenlab.com", + "name": "nat-goodspeed", + "username": "nat-goodspeed" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": true, + "id": "6a83d7ffcf81b9be5ddb0ba32dfc529864901cc2", + "message": "Merge pull request #18 from secondlife/dependabot/github_actions/actions/setup-python-5\n\nBump actions/setup-python from 4 to 5", + "timestamp": "2024-01-02T17:03:40-05:00", + "tree_id": "3669117e04cb342e077caa8ea8a11a34cbf60f5a", + "url": "https://github.com/secondlife/python-llsd/commit/6a83d7ffcf81b9be5ddb0ba32dfc529864901cc2" + }, + "date": 1704233093113, + "tool": "pytest", + "benches": [ + { + "name": "tests/bench.py::test_parse_xml_stream", + "value": 15817.748850333048, + "unit": "iter/sec", + "range": "stddev: 0.0000049454232572506185", + "extra": "mean: 63.220121236085035 usec\nrounds: 5015" + }, + { + "name": "tests/bench.py::test_parse_notation_stream", + "value": 5226.305717036576, + "unit": "iter/sec", + "range": "stddev: 0.000023195145688932988", + "extra": "mean: 191.33974438966055 usec\nrounds: 3654" + }, + { + "name": "tests/bench.py::test_parse_binary_stream", + "value": 19108.36290998745, + "unit": "iter/sec", + "range": "stddev: 0.000027932258397521702", + "extra": "mean: 52.33310696005914 usec\nrounds: 3908" + }, + { + "name": "tests/bench.py::test_parse_notation_bytes", + "value": 5589.144603128502, + "unit": "iter/sec", + "range": "stddev: 0.000024681583520059786", + "extra": "mean: 178.91825511908456 usec\nrounds: 4786" + }, + { + "name": "tests/bench.py::test_parse_xml_bytes", + "value": 18952.307873543115, + "unit": "iter/sec", + "range": "stddev: 0.0000064658372761547725", + "extra": "mean: 52.76402254925225 usec\nrounds: 9446" + }, + { + "name": "tests/bench.py::test_parse_binary_bytes", + "value": 21056.190666146376, + "unit": "iter/sec", + "range": "stddev: 0.000029203695454950567", + "extra": "mean: 47.49197116683481 usec\nrounds: 14081" + }, + { + "name": "tests/bench.py::test_format_xml", + "value": 24611.46043207338, + "unit": "iter/sec", + "range": "stddev: 0.000010356354415810582", + "extra": "mean: 40.63147746798524 usec\nrounds: 11406" + }, + { + "name": "tests/bench.py::test_format_notation", + "value": 29878.910875172453, + "unit": "iter/sec", + "range": "stddev: 0.000010185781869046188", + "extra": "mean: 33.46842206457193 usec\nrounds: 14095" + }, + { + "name": "tests/bench.py::test_format_binary", + "value": 31830.31941075389, + "unit": "iter/sec", + "range": "stddev: 0.0000016043977631718027", + "extra": "mean: 31.41658703123631 usec\nrounds: 13679" + }, + { + "name": "tests/bench.py::test_format_xml_deep", + "value": 281.1184958242179, + "unit": "iter/sec", + "range": "stddev: 0.0003743238945038085", + "extra": "mean: 3.5572188057853555 msec\nrounds: 242" + }, + { + "name": "tests/bench.py::test_format_xml_wide", + "value": 0.7273139787231522, + "unit": "iter/sec", + "range": "stddev: 0.015157278237429421", + "extra": "mean: 1.3749220133999984 sec\nrounds: 5" + }, + { + "name": "tests/bench.py::test_format_notation_deep", + "value": 355.27013357330435, + "unit": "iter/sec", + "range": "stddev: 0.00010387737303301474", + "extra": "mean: 2.81475954632608 msec\nrounds: 313" + }, + { + "name": "tests/bench.py::test_format_notation_wide", + "value": 0.9181573879351806, + "unit": "iter/sec", + "range": "stddev: 0.005372596694519276", + "extra": "mean: 1.089137889799997 sec\nrounds: 5" + }, + { + "name": "tests/bench.py::test_format_notation_wide_array", + "value": 1.5495171175811437, + "unit": "iter/sec", + "range": "stddev: 0.0068435045283850165", + "extra": "mean: 645.3623445999995 msec\nrounds: 5" + }, + { + "name": "tests/bench.py::test_format_binary_deep", + "value": 307.0209478293415, + "unit": "iter/sec", + "range": "stddev: 0.00009111088945665466", + "extra": "mean: 3.257106744898244 msec\nrounds: 294" + }, + { + "name": "tests/bench.py::test_format_binary_wide", + "value": 0.7866670063368922, + "unit": "iter/sec", + "range": "stddev: 0.013415616560735342", + "extra": "mean: 1.2711858918000019 sec\nrounds: 5" + }, + { + "name": "tests/bench.py::test_format_binary_wide_array", + "value": 1.0968526390904527, + "unit": "iter/sec", + "range": "stddev: 0.008067776856191717", + "extra": "mean: 911.6994976000001 msec\nrounds: 5" + }, + { + "name": "tests/bench.py::test_parse_xml_deep", + "value": 303.20488262798125, + "unit": "iter/sec", + "range": "stddev: 0.006690864989110339", + "extra": "mean: 3.298099922839814 msec\nrounds: 324" + }, + { + "name": "tests/bench.py::test_parse_binary_deep", + "value": 251.09686230482555, + "unit": "iter/sec", + "range": "stddev: 0.000036038591848352536", + "extra": "mean: 3.982526865612618 msec\nrounds: 253" + } + ] + }, + { + "commit": { + "author": { + "email": "nat@lindenlab.com", + "name": "nat-goodspeed", + "username": "nat-goodspeed" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": true, + "id": "1b60fb99c5e343344cdd6436e63a74a0b6a46b4e", + "message": "Merge pull request #19 from secondlife/dependabot/github_actions/actions/download-artifact-4\n\nBump actions/download-artifact from 3 to 4", + "timestamp": "2024-01-02T17:04:14-05:00", + "tree_id": "79810cd2ef7929fef9a6bd62bfc0f003f88f5f09", + "url": "https://github.com/secondlife/python-llsd/commit/1b60fb99c5e343344cdd6436e63a74a0b6a46b4e" + }, + "date": 1704233127821, + "tool": "pytest", + "benches": [ + { + "name": "tests/bench.py::test_parse_xml_stream", + "value": 15771.481761552666, + "unit": "iter/sec", + "range": "stddev: 0.0000035281675703742757", + "extra": "mean: 63.40558326217488 usec\nrounds: 4672" + }, + { + "name": "tests/bench.py::test_parse_notation_stream", + "value": 5301.90037250154, + "unit": "iter/sec", + "range": "stddev: 0.000023734886571667883", + "extra": "mean: 188.61161654159497 usec\nrounds: 3591" + }, + { + "name": "tests/bench.py::test_parse_binary_stream", + "value": 19579.96773007318, + "unit": "iter/sec", + "range": "stddev: 0.000028930327560053272", + "extra": "mean: 51.07260715573521 usec\nrounds: 11124" + }, + { + "name": "tests/bench.py::test_parse_notation_bytes", + "value": 5648.875129816638, + "unit": "iter/sec", + "range": "stddev: 0.000024969177491298888", + "extra": "mean: 177.02639499352148 usec\nrounds: 2357" + }, + { + "name": "tests/bench.py::test_parse_xml_bytes", + "value": 19468.970623761248, + "unit": "iter/sec", + "range": "stddev: 0.0000028126786056716277", + "extra": "mean: 51.363783906455346 usec\nrounds: 8612" + }, + { + "name": "tests/bench.py::test_parse_binary_bytes", + "value": 21415.164843480532, + "unit": "iter/sec", + "range": "stddev: 0.000029627572069086895", + "extra": "mean: 46.69588150774531 usec\nrounds: 14406" + }, + { + "name": "tests/bench.py::test_format_xml", + "value": 24692.406232870944, + "unit": "iter/sec", + "range": "stddev: 0.00001093615219869209", + "extra": "mean: 40.4982807495198 usec\nrounds: 10675" + }, + { + "name": "tests/bench.py::test_format_notation", + "value": 30427.164021387784, + "unit": "iter/sec", + "range": "stddev: 0.000010198318836456697", + "extra": "mean: 32.86536988123778 usec\nrounds: 14164" + }, + { + "name": "tests/bench.py::test_format_binary", + "value": 32288.153137201123, + "unit": "iter/sec", + "range": "stddev: 0.0000021872234414808735", + "extra": "mean: 30.97111178055706 usec\nrounds: 15101" + }, + { + "name": "tests/bench.py::test_format_xml_deep", + "value": 284.3052779648135, + "unit": "iter/sec", + "range": "stddev: 0.00002119795391927093", + "extra": "mean: 3.5173458866414817 msec\nrounds: 247" + }, + { + "name": "tests/bench.py::test_format_xml_wide", + "value": 0.7247622618050542, + "unit": "iter/sec", + "range": "stddev: 0.016197633655692253", + "extra": "mean: 1.3797627894000073 sec\nrounds: 5" + }, + { + "name": "tests/bench.py::test_format_notation_deep", + "value": 361.24198902906744, + "unit": "iter/sec", + "range": "stddev: 0.000053709243070328815", + "extra": "mean: 2.768227477342161 msec\nrounds: 331" + }, + { + "name": "tests/bench.py::test_format_notation_wide", + "value": 0.9209113636788201, + "unit": "iter/sec", + "range": "stddev: 0.006243087274787177", + "extra": "mean: 1.085880834400001 sec\nrounds: 5" + }, + { + "name": "tests/bench.py::test_format_notation_wide_array", + "value": 1.5534795298378319, + "unit": "iter/sec", + "range": "stddev: 0.004737098711267636", + "extra": "mean: 643.716238799999 msec\nrounds: 5" + }, + { + "name": "tests/bench.py::test_format_binary_deep", + "value": 310.5755518782681, + "unit": "iter/sec", + "range": "stddev: 0.00002613502073306418", + "extra": "mean: 3.219828457044668 msec\nrounds: 291" + }, + { + "name": "tests/bench.py::test_format_binary_wide", + "value": 0.8077209945185349, + "unit": "iter/sec", + "range": "stddev: 0.01187644725045888", + "extra": "mean: 1.2380512662000058 sec\nrounds: 5" + }, + { + "name": "tests/bench.py::test_format_binary_wide_array", + "value": 1.080027477964048, + "unit": "iter/sec", + "range": "stddev: 0.001494319381775762", + "extra": "mean: 925.9023686000035 msec\nrounds: 5" + }, + { + "name": "tests/bench.py::test_parse_xml_deep", + "value": 356.75666209532955, + "unit": "iter/sec", + "range": "stddev: 0.0037188877925861496", + "extra": "mean: 2.803031046783335 msec\nrounds: 342" + }, + { + "name": "tests/bench.py::test_parse_binary_deep", + "value": 255.45211473630428, + "unit": "iter/sec", + "range": "stddev: 0.00004148517106775961", + "extra": "mean: 3.914627996062083 msec\nrounds: 254" + } + ] + }, + { + "commit": { + "author": { + "email": "nat@lindenlab.com", + "name": "nat-goodspeed", + "username": "nat-goodspeed" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": true, + "id": "5b63d1cd920b7467b1923fbe655e2bfd649bdf89", + "message": "Merge pull request #20 from secondlife/dependabot/github_actions/actions/upload-artifact-4\n\nBump actions/upload-artifact from 3 to 4", + "timestamp": "2024-01-02T17:04:54-05:00", + "tree_id": "6bedc0fbf3e18822694ca41b607b30d3739af50a", + "url": "https://github.com/secondlife/python-llsd/commit/5b63d1cd920b7467b1923fbe655e2bfd649bdf89" + }, + "date": 1704233173729, + "tool": "pytest", + "benches": [ + { + "name": "tests/bench.py::test_parse_xml_stream", + "value": 15769.089493607331, + "unit": "iter/sec", + "range": "stddev: 0.000003844633524595741", + "extra": "mean: 63.41520227945896 usec\nrounds: 5616" + }, + { + "name": "tests/bench.py::test_parse_notation_stream", + "value": 5339.77006960269, + "unit": "iter/sec", + "range": "stddev: 0.00002338108387787813", + "extra": "mean: 187.27398126983508 usec\nrounds: 3684" + }, + { + "name": "tests/bench.py::test_parse_binary_stream", + "value": 19235.995523144546, + "unit": "iter/sec", + "range": "stddev: 0.000027955748351179218", + "extra": "mean: 51.98587194495916 usec\nrounds: 8020" + }, + { + "name": "tests/bench.py::test_parse_notation_bytes", + "value": 5746.025245985933, + "unit": "iter/sec", + "range": "stddev: 0.000025343188618617877", + "extra": "mean: 174.03334604187157 usec\nrounds: 4800" + }, + { + "name": "tests/bench.py::test_parse_xml_bytes", + "value": 19347.75999133648, + "unit": "iter/sec", + "range": "stddev: 0.000002971922739142828", + "extra": "mean: 51.68556982553944 usec\nrounds: 4010" + }, + { + "name": "tests/bench.py::test_parse_binary_bytes", + "value": 21285.335754157324, + "unit": "iter/sec", + "range": "stddev: 0.000029437407713210442", + "extra": "mean: 46.98070124661698 usec\nrounds: 16927" + }, + { + "name": "tests/bench.py::test_format_xml", + "value": 25531.57909239875, + "unit": "iter/sec", + "range": "stddev: 0.000010580367957196139", + "extra": "mean: 39.16718180183847 usec\nrounds: 11573" + }, + { + "name": "tests/bench.py::test_format_notation", + "value": 30416.006564151143, + "unit": "iter/sec", + "range": "stddev: 0.000009935038166048219", + "extra": "mean: 32.877425834679364 usec\nrounds: 14043" + }, + { + "name": "tests/bench.py::test_format_binary", + "value": 32054.371901507402, + "unit": "iter/sec", + "range": "stddev: 0.0000026487228024801767", + "extra": "mean: 31.196992506129046 usec\nrounds: 14546" + }, + { + "name": "tests/bench.py::test_format_xml_deep", + "value": 285.6737382535751, + "unit": "iter/sec", + "range": "stddev: 0.00003629488954449132", + "extra": "mean: 3.5004967768943507 msec\nrounds: 251" + }, + { + "name": "tests/bench.py::test_format_xml_wide", + "value": 0.7402543352994372, + "unit": "iter/sec", + "range": "stddev: 0.014458880435002406", + "extra": "mean: 1.3508870564000062 sec\nrounds: 5" + }, + { + "name": "tests/bench.py::test_format_notation_deep", + "value": 369.97786979043366, + "unit": "iter/sec", + "range": "stddev: 0.00009672905832438023", + "extra": "mean: 2.70286436474276 msec\nrounds: 329" + }, + { + "name": "tests/bench.py::test_format_notation_wide", + "value": 0.9329679625787793, + "unit": "iter/sec", + "range": "stddev: 0.011084448751030202", + "extra": "mean: 1.071848166399991 sec\nrounds: 5" + }, + { + "name": "tests/bench.py::test_format_notation_wide_array", + "value": 1.5406054815501788, + "unit": "iter/sec", + "range": "stddev: 0.00297824455968108", + "extra": "mean: 649.0954445999932 msec\nrounds: 5" + }, + { + "name": "tests/bench.py::test_format_binary_deep", + "value": 314.42037649064304, + "unit": "iter/sec", + "range": "stddev: 0.00003219010147908321", + "extra": "mean: 3.1804554499977185 msec\nrounds: 300" + }, + { + "name": "tests/bench.py::test_format_binary_wide", + "value": 0.8083644073987081, + "unit": "iter/sec", + "range": "stddev: 0.022438961657121583", + "extra": "mean: 1.2370658466000122 sec\nrounds: 5" + }, + { + "name": "tests/bench.py::test_format_binary_wide_array", + "value": 1.0711350563990751, + "unit": "iter/sec", + "range": "stddev: 0.008000223907686208", + "extra": "mean: 933.5890876000121 msec\nrounds: 5" + }, + { + "name": "tests/bench.py::test_parse_xml_deep", + "value": 308.31658465320214, + "unit": "iter/sec", + "range": "stddev: 0.00649825421827752", + "extra": "mean: 3.2434194259280957 msec\nrounds: 324" + }, + { + "name": "tests/bench.py::test_parse_binary_deep", + "value": 259.7446542997554, + "unit": "iter/sec", + "range": "stddev: 0.00005898358736048852", + "extra": "mean: 3.849934862743937 msec\nrounds: 255" + } + ] + }, + { + "commit": { + "author": { + "email": "signal@lindenlab.com", + "name": "Signal Linden", + "username": "bennettgoble" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": true, + "id": "c806ece709dc7327f54e12cd0a85746b734bec4a", + "message": "Merge pull request #22 from secondlife/signal/codecov\n\nBump codecov-action to v4", + "timestamp": "2024-02-01T08:48:42-08:00", + "tree_id": "c78813db2067ffe9dc2b99a1a098ae8a7a7dde1b", + "url": "https://github.com/secondlife/python-llsd/commit/c806ece709dc7327f54e12cd0a85746b734bec4a" + }, + "date": 1706806195146, + "tool": "pytest", + "benches": [ + { + "name": "tests/bench.py::test_parse_xml_stream", + "value": 15347.358820702611, + "unit": "iter/sec", + "range": "stddev: 0.000005450338281102883", + "extra": "mean: 65.15779110155836 usec\nrounds: 4203" + }, + { + "name": "tests/bench.py::test_parse_notation_stream", + "value": 5288.741300860378, + "unit": "iter/sec", + "range": "stddev: 0.000023388532612794963", + "extra": "mean: 189.08090661143115 usec\nrounds: 3373" + }, + { + "name": "tests/bench.py::test_parse_binary_stream", + "value": 19293.6063103556, + "unit": "iter/sec", + "range": "stddev: 0.000027867923674971253", + "extra": "mean: 51.830641919093296 usec\nrounds: 11193" + }, + { + "name": "tests/bench.py::test_parse_notation_bytes", + "value": 5651.18047323247, + "unit": "iter/sec", + "range": "stddev: 0.000025264927663956554", + "extra": "mean: 176.9541788192089 usec\nrounds: 4032" + }, + { + "name": "tests/bench.py::test_parse_xml_bytes", + "value": 19154.890757567064, + "unit": "iter/sec", + "range": "stddev: 0.0000030005141038747245", + "extra": "mean: 52.20598815500704 usec\nrounds: 3799" + }, + { + "name": "tests/bench.py::test_parse_binary_bytes", + "value": 21184.636765771902, + "unit": "iter/sec", + "range": "stddev: 0.000030012256734448774", + "extra": "mean: 47.204019169953575 usec\nrounds: 12624" + }, + { + "name": "tests/bench.py::test_format_xml", + "value": 24718.948616065412, + "unit": "iter/sec", + "range": "stddev: 0.000010599726805374958", + "extra": "mean: 40.45479504537167 usec\nrounds: 9607" + }, + { + "name": "tests/bench.py::test_format_notation", + "value": 30007.52430801591, + "unit": "iter/sec", + "range": "stddev: 0.000010159379950217543", + "extra": "mean: 33.324975087427326 usec\nrounds: 15133" + }, + { + "name": "tests/bench.py::test_format_binary", + "value": 31920.89511089033, + "unit": "iter/sec", + "range": "stddev: 0.000001827018336480383", + "extra": "mean: 31.32744230780777 usec\nrounds: 13364" + }, + { + "name": "tests/bench.py::test_format_xml_deep", + "value": 285.4940421694391, + "unit": "iter/sec", + "range": "stddev: 0.00026207913101627413", + "extra": "mean: 3.502700064775802 msec\nrounds: 247" + }, + { + "name": "tests/bench.py::test_format_xml_wide", + "value": 0.7378949977398735, + "unit": "iter/sec", + "range": "stddev: 0.006005235618657384", + "extra": "mean: 1.3552063681999982 sec\nrounds: 5" + }, + { + "name": "tests/bench.py::test_format_notation_deep", + "value": 354.63107863916923, + "unit": "iter/sec", + "range": "stddev: 0.00005461366296289827", + "extra": "mean: 2.8198318202604065 msec\nrounds: 306" + }, + { + "name": "tests/bench.py::test_format_notation_wide", + "value": 0.9081130541100204, + "unit": "iter/sec", + "range": "stddev: 0.0023691583071882875", + "extra": "mean: 1.101184478600004 sec\nrounds: 5" + }, + { + "name": "tests/bench.py::test_format_notation_wide_array", + "value": 1.5445717578790732, + "unit": "iter/sec", + "range": "stddev: 0.004919848933425914", + "extra": "mean: 647.4286447999987 msec\nrounds: 5" + }, + { + "name": "tests/bench.py::test_format_binary_deep", + "value": 307.5966753600674, + "unit": "iter/sec", + "range": "stddev: 0.000017726619914937687", + "extra": "mean: 3.2510104305562373 msec\nrounds: 288" + }, + { + "name": "tests/bench.py::test_format_binary_wide", + "value": 0.7960918793705465, + "unit": "iter/sec", + "range": "stddev: 0.0059001806846304666", + "extra": "mean: 1.2561364157999946 sec\nrounds: 5" + }, + { + "name": "tests/bench.py::test_format_binary_wide_array", + "value": 1.1138691084497798, + "unit": "iter/sec", + "range": "stddev: 0.01276072662246532", + "extra": "mean: 897.7715535999948 msec\nrounds: 5" + }, + { + "name": "tests/bench.py::test_parse_xml_deep", + "value": 353.0252910370794, + "unit": "iter/sec", + "range": "stddev: 0.003966884895120551", + "extra": "mean: 2.83265824117674 msec\nrounds: 340" + }, + { + "name": "tests/bench.py::test_parse_binary_deep", + "value": 253.97927621589147, + "unit": "iter/sec", + "range": "stddev: 0.000036906851917373934", + "extra": "mean: 3.937329119522194 msec\nrounds: 251" + } + ] + }, + { + "commit": { + "author": { + "email": "signal@lindenlab.com", + "name": "Signal Linden", + "username": "bennettgoble" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": true, + "id": "acb55e3d613f131371d4a470f772f0e322f4bcce", + "message": "Merge pull request #23 from secondlife/signal/fix-array-indent\n\nFix array indentation of pretty xml", + "timestamp": "2024-04-01T23:35:52-07:00", + "tree_id": "bd5010d3fc8eb79caa56e9d25cdcf456ee4b3bc1", + "url": "https://github.com/secondlife/python-llsd/commit/acb55e3d613f131371d4a470f772f0e322f4bcce" + }, + "date": 1712039822653, + "tool": "pytest", + "benches": [ + { + "name": "tests/bench.py::test_parse_xml_stream", + "value": 15338.036976363388, + "unit": "iter/sec", + "range": "stddev: 0.000004331856709993063", + "extra": "mean: 65.19739139637265 usec\nrounds: 4254" + }, + { + "name": "tests/bench.py::test_parse_notation_stream", + "value": 5332.396264314848, + "unit": "iter/sec", + "range": "stddev: 0.0000245948764983066", + "extra": "mean: 187.53294962194425 usec\nrounds: 3176" + }, + { + "name": "tests/bench.py::test_parse_binary_stream", + "value": 19284.815864952467, + "unit": "iter/sec", + "range": "stddev: 0.000027818135030237138", + "extra": "mean: 51.854267471506645 usec\nrounds: 10188" + }, + { + "name": "tests/bench.py::test_parse_notation_bytes", + "value": 5854.368214946115, + "unit": "iter/sec", + "range": "stddev: 0.000025867648343098357", + "extra": "mean: 170.81262457100237 usec\nrounds: 4371" + }, + { + "name": "tests/bench.py::test_parse_xml_bytes", + "value": 19263.501614767185, + "unit": "iter/sec", + "range": "stddev: 0.000005072631884570145", + "extra": "mean: 51.9116420263599 usec\nrounds: 9199" + }, + { + "name": "tests/bench.py::test_parse_binary_bytes", + "value": 21464.343697156215, + "unit": "iter/sec", + "range": "stddev: 0.000029175897140904308", + "extra": "mean: 46.588892449224474 usec\nrounds: 12078" + }, + { + "name": "tests/bench.py::test_format_xml", + "value": 24926.216480845855, + "unit": "iter/sec", + "range": "stddev: 0.00001046848390926289", + "extra": "mean: 40.118403078478984 usec\nrounds: 10720" + }, + { + "name": "tests/bench.py::test_format_notation", + "value": 29926.614027771157, + "unit": "iter/sec", + "range": "stddev: 0.000011045711956936568", + "extra": "mean: 33.41507325459622 usec\nrounds: 13651" + }, + { + "name": "tests/bench.py::test_format_binary", + "value": 31497.118948059262, + "unit": "iter/sec", + "range": "stddev: 0.000001664586674172829", + "extra": "mean: 31.74893556610886 usec\nrounds: 10988" + }, + { + "name": "tests/bench.py::test_format_xml_deep", + "value": 285.4127173566864, + "unit": "iter/sec", + "range": "stddev: 0.000039951208860490064", + "extra": "mean: 3.5036981157019658 msec\nrounds: 242" + }, + { + "name": "tests/bench.py::test_format_xml_wide", + "value": 0.7331961327654465, + "unit": "iter/sec", + "range": "stddev: 0.004417990449218888", + "extra": "mean: 1.3638915363999957 sec\nrounds: 5" + }, + { + "name": "tests/bench.py::test_format_notation_deep", + "value": 356.5444582953051, + "unit": "iter/sec", + "range": "stddev: 0.00011026263356797399", + "extra": "mean: 2.804699320755556 msec\nrounds: 318" + }, + { + "name": "tests/bench.py::test_format_notation_wide", + "value": 0.9236691727590265, + "unit": "iter/sec", + "range": "stddev: 0.007131136617704611", + "extra": "mean: 1.0826387082000053 sec\nrounds: 5" + }, + { + "name": "tests/bench.py::test_format_notation_wide_array", + "value": 1.5434002125635595, + "unit": "iter/sec", + "range": "stddev: 0.010704752604469039", + "extra": "mean: 647.920087 msec\nrounds: 5" + }, + { + "name": "tests/bench.py::test_format_binary_deep", + "value": 297.09737409990527, + "unit": "iter/sec", + "range": "stddev: 0.00003245059419092853", + "extra": "mean: 3.3658998267138127 msec\nrounds: 277" + }, + { + "name": "tests/bench.py::test_format_binary_wide", + "value": 0.7493606807246287, + "unit": "iter/sec", + "range": "stddev: 0.004469532851903014", + "extra": "mean: 1.3344708705999948 sec\nrounds: 5" + }, + { + "name": "tests/bench.py::test_format_binary_wide_array", + "value": 1.0724915981351262, + "unit": "iter/sec", + "range": "stddev: 0.005393651071731648", + "extra": "mean: 932.408236800012 msec\nrounds: 5" + }, + { + "name": "tests/bench.py::test_parse_xml_deep", + "value": 332.52609466548023, + "unit": "iter/sec", + "range": "stddev: 0.005866860812937119", + "extra": "mean: 3.007282784847293 msec\nrounds: 330" + }, + { + "name": "tests/bench.py::test_parse_binary_deep", + "value": 252.77433834590374, + "unit": "iter/sec", + "range": "stddev: 0.00011968375288348899", + "extra": "mean: 3.956097784861258 msec\nrounds: 251" + } + ] + }, + { + "commit": { + "author": { + "email": "signal@lindenlab.com", + "name": "Signal Linden", + "username": "bennettgoble" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": true, + "id": "ee53c4c4c2af234b5e65c3cbf2a99ad852c6919d", + "message": "Merge pull request #24 from secondlife/nattylinden/pre-commit\n\nUpdate pre-commit hooks to avoid isort installation error", + "timestamp": "2024-07-24T08:24:37-07:00", + "tree_id": "4ee9147f3339f0dc142462876d8cc37a146f949b", + "url": "https://github.com/secondlife/python-llsd/commit/ee53c4c4c2af234b5e65c3cbf2a99ad852c6919d" + }, + "date": 1721834748098, + "tool": "pytest", + "benches": [ + { + "name": "tests/bench.py::test_parse_xml_stream", + "value": 15660.262442643065, + "unit": "iter/sec", + "range": "stddev: 0.000003548137417958026", + "extra": "mean: 63.85589026126338 usec\nrounds: 4210" + }, + { + "name": "tests/bench.py::test_parse_notation_stream", + "value": 5389.110365094642, + "unit": "iter/sec", + "range": "stddev: 0.00002627231306259801", + "extra": "mean: 185.5593840640223 usec\nrounds: 3627" + }, + { + "name": "tests/bench.py::test_parse_binary_stream", + "value": 19552.914686100867, + "unit": "iter/sec", + "range": "stddev: 0.000029079022158787694", + "extra": "mean: 51.14327025171583 usec\nrounds: 11530" + }, + { + "name": "tests/bench.py::test_parse_notation_bytes", + "value": 5853.644424330927, + "unit": "iter/sec", + "range": "stddev: 0.000028775779240083275", + "extra": "mean: 170.8337451867518 usec\nrounds: 4415" + }, + { + "name": "tests/bench.py::test_parse_xml_bytes", + "value": 19343.018241922593, + "unit": "iter/sec", + "range": "stddev: 0.000002707553436047833", + "extra": "mean: 51.69824003126232 usec\nrounds: 8978" + }, + { + "name": "tests/bench.py::test_parse_binary_bytes", + "value": 21605.719305060265, + "unit": "iter/sec", + "range": "stddev: 0.000030575954842135505", + "extra": "mean: 46.284041085630065 usec\nrounds: 13046" + }, + { + "name": "tests/bench.py::test_format_xml", + "value": 25084.271280482706, + "unit": "iter/sec", + "range": "stddev: 0.000011414889962911467", + "extra": "mean: 39.86561892982193 usec\nrounds: 10111" + }, + { + "name": "tests/bench.py::test_format_notation", + "value": 30488.6213954444, + "unit": "iter/sec", + "range": "stddev: 0.000010598975323594875", + "extra": "mean: 32.7991215814507 usec\nrounds: 14114" + }, + { + "name": "tests/bench.py::test_format_binary", + "value": 31890.08024579081, + "unit": "iter/sec", + "range": "stddev: 0.0000016020151060664976", + "extra": "mean: 31.35771350503235 usec\nrounds: 13906" + }, + { + "name": "tests/bench.py::test_format_xml_deep", + "value": 287.9811060890427, + "unit": "iter/sec", + "range": "stddev: 0.00008228945465947704", + "extra": "mean: 3.4724500283390243 msec\nrounds: 247" + }, + { + "name": "tests/bench.py::test_format_xml_wide", + "value": 0.7290009188003955, + "unit": "iter/sec", + "range": "stddev: 0.012525721075285342", + "extra": "mean: 1.3717403835999904 sec\nrounds: 5" + }, + { + "name": "tests/bench.py::test_format_notation_deep", + "value": 359.88609614065064, + "unit": "iter/sec", + "range": "stddev: 0.00007283429156779202", + "extra": "mean: 2.7786569437491693 msec\nrounds: 320" + }, + { + "name": "tests/bench.py::test_format_notation_wide", + "value": 0.9191501363127588, + "unit": "iter/sec", + "range": "stddev: 0.0054037221755862015", + "extra": "mean: 1.0879615424000009 sec\nrounds: 5" + }, + { + "name": "tests/bench.py::test_format_notation_wide_array", + "value": 1.5688287384750186, + "unit": "iter/sec", + "range": "stddev: 0.0018886439807905661", + "extra": "mean: 637.4182060000066 msec\nrounds: 5" + }, + { + "name": "tests/bench.py::test_format_binary_deep", + "value": 304.9049846786711, + "unit": "iter/sec", + "range": "stddev: 0.00011654443619683", + "extra": "mean: 3.2797102384333456 msec\nrounds: 281" + }, + { + "name": "tests/bench.py::test_format_binary_wide", + "value": 0.7721258429625174, + "unit": "iter/sec", + "range": "stddev: 0.025859205345833917", + "extra": "mean: 1.2951256704000058 sec\nrounds: 5" + }, + { + "name": "tests/bench.py::test_format_binary_wide_array", + "value": 1.096124343954483, + "unit": "iter/sec", + "range": "stddev: 0.014240544140814227", + "extra": "mean: 912.3052557999983 msec\nrounds: 5" + }, + { + "name": "tests/bench.py::test_parse_xml_deep", + "value": 354.3760545909131, + "unit": "iter/sec", + "range": "stddev: 0.004031652751633972", + "extra": "mean: 2.8218610909091653 msec\nrounds: 330" + }, + { + "name": "tests/bench.py::test_parse_binary_deep", + "value": 261.7145531245397, + "unit": "iter/sec", + "range": "stddev: 0.00008492952548364103", + "extra": "mean: 3.8209567945735876 msec\nrounds: 258" + } + ] + }, + { + "commit": { + "author": { + "email": "signal@lindenlab.com", + "name": "Signal Linden", + "username": "bennettgoble" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": true, + "id": "1a5ce3e8161422a5eb25df15f6b7817792c789ba", + "message": "Merge pull request #25 from secondlife/dependabot/github_actions/codecov/codecov-action-5\n\nBump codecov/codecov-action from 4 to 5", + "timestamp": "2025-03-15T09:31:29-07:00", + "tree_id": "ec5213c2c2e6a731dd5be9305a108f634c3b7f86", + "url": "https://github.com/secondlife/python-llsd/commit/1a5ce3e8161422a5eb25df15f6b7817792c789ba" + }, + "date": 1742056357497, + "tool": "pytest", + "benches": [ + { + "name": "tests/bench.py::test_parse_xml_stream", + "value": 15606.945919497297, + "unit": "iter/sec", + "range": "stddev: 0.00000506948781494805", + "extra": "mean: 64.07403505837293 usec\nrounds: 5334" + }, + { + "name": "tests/bench.py::test_parse_notation_stream", + "value": 5224.591737187256, + "unit": "iter/sec", + "range": "stddev: 0.00002452157777505783", + "extra": "mean: 191.4025153166066 usec\nrounds: 3297" + }, + { + "name": "tests/bench.py::test_parse_binary_stream", + "value": 19254.738851288545, + "unit": "iter/sec", + "range": "stddev: 0.000028661407461321518", + "extra": "mean: 51.93526683084975 usec\nrounds: 10977" + }, + { + "name": "tests/bench.py::test_parse_notation_bytes", + "value": 5653.205286006255, + "unit": "iter/sec", + "range": "stddev: 0.000025768814437985244", + "extra": "mean: 176.89079900837223 usec\nrounds: 4841" + }, + { + "name": "tests/bench.py::test_parse_xml_bytes", + "value": 19804.97420317452, + "unit": "iter/sec", + "range": "stddev: 0.0000036738295406410742", + "extra": "mean: 50.49236569264054 usec\nrounds: 9992" + }, + { + "name": "tests/bench.py::test_parse_binary_bytes", + "value": 21143.99495727199, + "unit": "iter/sec", + "range": "stddev: 0.000030447738622913495", + "extra": "mean: 47.29475210435921 usec\nrounds: 15087" + }, + { + "name": "tests/bench.py::test_format_xml", + "value": 24401.255972155745, + "unit": "iter/sec", + "range": "stddev: 0.000010862009887744549", + "extra": "mean: 40.9814970647863 usec\nrounds: 9880" + }, + { + "name": "tests/bench.py::test_format_notation", + "value": 30142.561881501522, + "unit": "iter/sec", + "range": "stddev: 0.000010583096775969268", + "extra": "mean: 33.17568041931099 usec\nrounds: 14785" + }, + { + "name": "tests/bench.py::test_format_binary", + "value": 31873.289181523698, + "unit": "iter/sec", + "range": "stddev: 0.000003014995156978838", + "extra": "mean: 31.374232960546784 usec\nrounds: 11092" + }, + { + "name": "tests/bench.py::test_format_xml_deep", + "value": 275.2747944623416, + "unit": "iter/sec", + "range": "stddev: 0.000029096590493651033", + "extra": "mean: 3.6327336178859735 msec\nrounds: 246" + }, + { + "name": "tests/bench.py::test_format_xml_wide", + "value": 0.7101375556229714, + "unit": "iter/sec", + "range": "stddev: 0.01350042453332921", + "extra": "mean: 1.408177883399992 sec\nrounds: 5" + }, + { + "name": "tests/bench.py::test_format_notation_deep", + "value": 348.896057338846, + "unit": "iter/sec", + "range": "stddev: 0.00012521640913727234", + "extra": "mean: 2.8661831481483477 msec\nrounds: 324" + }, + { + "name": "tests/bench.py::test_format_notation_wide", + "value": 0.8996757730095942, + "unit": "iter/sec", + "range": "stddev: 0.008334080178187877", + "extra": "mean: 1.1115115355999876 sec\nrounds: 5" + }, + { + "name": "tests/bench.py::test_format_notation_wide_array", + "value": 1.5374093643485627, + "unit": "iter/sec", + "range": "stddev: 0.0011989562830601419", + "extra": "mean: 650.4448478000029 msec\nrounds: 5" + }, + { + "name": "tests/bench.py::test_format_binary_deep", + "value": 309.6512729152026, + "unit": "iter/sec", + "range": "stddev: 0.000019654658458515325", + "extra": "mean: 3.22943933214138 msec\nrounds: 280" + }, + { + "name": "tests/bench.py::test_format_binary_wide", + "value": 0.7835275338398409, + "unit": "iter/sec", + "range": "stddev: 0.019479325711172318", + "extra": "mean: 1.2762793351999904 sec\nrounds: 5" + }, + { + "name": "tests/bench.py::test_format_binary_wide_array", + "value": 1.101996216578525, + "unit": "iter/sec", + "range": "stddev: 0.017138832226022648", + "extra": "mean: 907.444131800014 msec\nrounds: 5" + }, + { + "name": "tests/bench.py::test_parse_xml_deep", + "value": 310.9819956175169, + "unit": "iter/sec", + "range": "stddev: 0.006865779858470243", + "extra": "mean: 3.215620241983142 msec\nrounds: 343" + }, + { + "name": "tests/bench.py::test_parse_binary_deep", + "value": 258.31266590151614, + "unit": "iter/sec", + "range": "stddev: 0.00003206944259462411", + "extra": "mean: 3.8712774555981637 msec\nrounds: 259" + } + ] + } + ] + } +} \ No newline at end of file diff --git a/dev/bench/index.html b/dev/bench/index.html new file mode 100644 index 0000000..6c88780 --- /dev/null +++ b/dev/bench/index.html @@ -0,0 +1,281 @@ + + + + + + + Codestin Search App + + + + +
+ + + + + + + diff --git a/llsd/__init__.py b/llsd/__init__.py deleted file mode 100644 index f746ca2..0000000 --- a/llsd/__init__.py +++ /dev/null @@ -1,81 +0,0 @@ -""" -Types as well as parsing and formatting functions for handling LLSD. - -This is the llsd module -- parsers and formatters between the -supported subset of mime types and python objects. Documentation -available on the Second Life wiki: - -http://wiki.secondlife.com/wiki/LLSD -""" -from llsd.base import (_LLSD, BINARY_MIME_TYPE, NOTATION_MIME_TYPE, XML_MIME_TYPE, - BINARY_HEADER, NOTATION_HEADER, XML_HEADER, - LLSDBaseParser, LLSDParseError, LLSDSerializationError, - LongType, UnicodeType, binary, undef, uri) -from llsd.serde_binary import (LLSDBinaryParser, format_binary, parse_binary, parse_binary_nohdr, - write_binary) -from llsd.serde_notation import (LLSDNotationFormatter, write_notation, format_notation, - LLSDNotationParser, parse_notation, parse_notation_nohdr) -from llsd.serde_xml import (LLSDXMLFormatter, LLSDXMLPrettyFormatter, - write_pretty_xml, write_xml, format_pretty_xml, format_xml, - parse_xml, parse_xml_nohdr) - - -def parse(something, mime_type = None): - """ - This is the basic public interface for parsing llsd. - - :param something: The data to parse. This is expected to be bytes, not - strings, or a byte stream. - :param mime_type: The mime_type of the data if it is known. - :returns: Returns a python object. - - Python 3 Note: when reading LLSD from a file, use open()'s 'rb' mode explicitly - """ - try: - if mime_type: - # explicit mime_type -- 'something' may or may not also have a header - for mime_types, parser in ( - ({XML_MIME_TYPE, 'application/llsd'}, parse_xml), - ({BINARY_MIME_TYPE}, parse_binary), - ({NOTATION_MIME_TYPE}, parse_notation), -## ({'application/json'}, parse_notation), - ): - if mime_type.lower() in mime_types: - return parser(something) - - # no recognized mime type, look for header - baseparser = LLSDBaseParser(something) - for pattern, parser in ( - (BINARY_HEADER, parse_binary_nohdr), - (NOTATION_HEADER, parse_notation_nohdr), - (XML_HEADER, parse_xml_nohdr), - ): - if baseparser.matchseq(pattern): - # we already saw the header, don't check again - return parser(baseparser) - - # no recognized header -- does content resemble XML? - if baseparser.starts_with(b'<'): - return parse_xml_nohdr(baseparser) - else: - return parse_notation_nohdr(baseparser) - - except KeyError as e: - raise LLSDParseError('LLSD could not be parsed: %s' % (e,)) - except TypeError as e: - raise LLSDParseError('Input stream not of type bytes. %s' % (e,)) - - -class LLSD(_LLSD): - def __bytes__(self): - return self.as_xml(self.thing) - - - def __str__(self): - return self.__bytes__().decode() - - parse = staticmethod(parse) - as_xml = staticmethod(format_xml) - as_pretty_xml = staticmethod(format_pretty_xml) - as_binary = staticmethod(format_binary) - as_notation = staticmethod(format_notation) diff --git a/llsd/base.py b/llsd/base.py deleted file mode 100644 index e7204ca..0000000 --- a/llsd/base.py +++ /dev/null @@ -1,582 +0,0 @@ -import abc -import base64 -import binascii -import datetime -import io -import os -import re -import sys -import types -import uuid - -try: - # If the future package is installed, then we support it. Any clients in - # python 2 using its str builtin replacement will actually be using instances - # of newstr, so we need to properly detect that as a string type - # for details see the docs: http://python-future.org/str_object.html - from future.types.newstr import newstr -except ImportError: - # otherwise we pass over it in silence - newstr = str - -PY2 = sys.version_info[0] == 2 - -XML_MIME_TYPE = 'application/llsd+xml' -BINARY_MIME_TYPE = 'application/llsd+binary' -NOTATION_MIME_TYPE = 'application/llsd+notation' - -XML_HEADER = b'' -BINARY_HEADER = b'' -NOTATION_HEADER = b'' - -ALL_CHARS = str(bytearray(range(256))) if PY2 else bytes(range(256)) - -MAX_FORMAT_DEPTH = 200 -MAX_PARSE_DEPTH = 200 - -class _LLSD: - __metaclass__ = abc.ABCMeta - - def __init__(self, thing=None): - self.thing = thing - - -undef = _LLSD(None) - - -# 'binary' only exists so that a Python 2 caller can distinguish binary data -# from str data - since in Python 2, (bytes is str). -if PY2: - class binary(str): - "Simple wrapper for llsd.binary data." - pass -else: - binary = bytes - - -class uri(str): - "Simple wrapper for llsd.uri data." - pass - - -class LLSDParseError(Exception): - "Exception raised when the parser fails." - pass - - -class LLSDSerializationError(TypeError): - "Exception raised when serialization fails." - pass - - -# In Python 2, this expression produces (str, unicode); in Python 3 it's -# simply (str,). Either way, it's valid to test isinstance(somevar, -# StringTypes). (Some consumers test (type(somevar) in StringTypes), so we do -# want (str,) rather than plain str.) -StringTypes = tuple(set((type(''), type(u''), newstr))) - -try: - LongType = long - IntTypes = (int, long) -except NameError: - LongType = int - IntTypes = int - -try: - UnicodeType = unicode -except NameError: - UnicodeType = str - -try: - b'%s' % (b'yes',) -except TypeError: - # There's a range of Python 3 versions, up through Python 3.4, for which - # bytes interpolation (bytes value with % operator) does not work. This - # hack can be removed once we no longer care about Python 3.4 -- in other - # words, once we're beyond jessie everywhere. - class B(object): - """ - Instead of writing: - b'format string' % stuff - write: - B('format string') % stuff - This class performs the conversions necessary to support bytes - interpolation when the language doesn't natively support it. - (We considered naming this class b, but that would be too confusing.) - """ - def __init__(self, fmt): - # Instead of storing the format string as bytes and converting it - # to string every time, convert initially and store the string. - try: - self.strfmt = fmt.decode('utf-8') - except AttributeError: - # caller passed a string literal rather than a bytes literal - self.strfmt = fmt - - def __mod__(self, args): - # __mod__() is engaged for (self % args) - if not isinstance(args, tuple): - # Unify the tuple and non-tuple cases. - args = (args,) - # In principle, this is simple: convert everything to string, - # interpolate, convert back. It's complicated by the fact that we - # must handle non-bytes args. - strargs = [] - for arg in args: - try: - decoder = arg.decode - except AttributeError: - # use arg exactly as is - strargs.append(arg) - else: - # convert from bytes to string - strargs.append(decoder('utf-8')) - return (self.strfmt % tuple(strargs)).encode('utf-8') -else: - # bytes interpolation Just Works - def B(fmt): - try: - # In the usual case, caller wrote B('fmt') rather than b'fmt'. But - # s/he really wants a bytes literal here. Encode the passed string. - return fmt.encode('utf-8') - except AttributeError: - # Caller wrote B(b'fmt')? - return fmt - - -def is_integer(o): - """ portable test if an object is like an int """ - return isinstance(o, IntTypes) - - -def is_unicode(o): - """ portable check if an object is unicode and not bytes """ - return isinstance(o, UnicodeType) - - -def is_string(o): - """ portable check if an object is string-like """ - return isinstance(o, StringTypes) - - -#date: d"YYYY-MM-DDTHH:MM:SS.FFFFFFZ" -_date_regex = re.compile(r"(?P\d{4})-(?P\d{2})-(?P\d{2})T" - r"(?P\d{2}):(?P\d{2}):(?P\d{2})" - r"(?P(\.\d+)?)Z") - - -def _str_to_bytes(s): - if is_unicode(s): - return s.encode('utf-8') - else: - return s - - -def _format_datestr(v): - """ - Formats a datetime or date object into the string format shared by - xml and notation serializations. - """ - if not isinstance(v, datetime.date) and not isinstance(v, datetime.datetime): - raise LLSDSerializationError("invalid date string %s passed to date formatter" % v) - - if not isinstance(v, datetime.datetime): - v = datetime.datetime.combine(v, datetime.time(0)) - - return _str_to_bytes(v.isoformat() + 'Z') - - -def _parse_datestr(datestr): - """ - Parses a datetime object from the string format shared by - xml and notation serializations. - """ - if datestr == "": - return datetime.datetime(1970, 1, 1) - - match = re.match(_date_regex, datestr) - if not match: - raise LLSDParseError("invalid date string '%s'." % datestr) - - year = int(match.group('year')) - month = int(match.group('month')) - day = int(match.group('day')) - hour = int(match.group('hour')) - minute = int(match.group('minute')) - second = int(match.group('second')) - seconds_float = match.group('second_float') - usec = 0 - if seconds_float: - usec = int(float('0' + seconds_float) * 1e6) - return datetime.datetime(year, month, day, hour, minute, second, usec) - - -def _bool_to_python(node, depth=0): - "Convert boolean node to a python object." - val = node.text or '' - try: - # string value, accept 'true' or 'True' or whatever - return (val.lower() in ('true', '1', '1.0')) - except AttributeError: - # not a string (no lower() method), use normal Python rules - return bool(val) - - -def _int_to_python(node, depth=0): - "Convert integer node to a python object." - val = node.text or '' - if not val.strip(): - return 0 - return int(val) - - -def _real_to_python(node, depth=0): - "Convert floating point node to a python object." - val = node.text or '' - if not val.strip(): - return 0.0 - return float(val) - - -def _uuid_to_python(node, depth=0): - "Convert uuid node to a python object." - if node.text: - return uuid.UUID(hex=node.text) - return uuid.UUID(int=0) - - -def _str_to_python(node, depth=0): - "Convert string node to a python object." - return node.text or '' - - -def _bin_to_python(node, depth=0): - base = node.get('encoding') or 'base64' - try: - if base == 'base16': - # parse base16 encoded data - return binary(base64.b16decode(node.text or '')) - elif base == 'base64': - # parse base64 encoded data - return binary(base64.b64decode(node.text or '')) - elif base == 'base85': - return LLSDParseError("Parser doesn't support base85 encoding") - except binascii.Error as exc: - # convert exception class so it's more catchable - return LLSDParseError("Encoded binary data: " + str(exc)) - except TypeError as exc: - # convert exception class so it's more catchable - return LLSDParseError("Bad binary data: " + str(exc)) - - -def _date_to_python(node, depth=0): - "Convert date node to a python object." - val = node.text or '' - if not val: - val = "1970-01-01T00:00:00Z" - return _parse_datestr(val) - - -def _uri_to_python(node, depth=0): - "Convert uri node to a python object." - val = node.text or '' - return uri(val) - - -def _map_to_python(node, depth=0): - "Convert map node to a python object." - result = {} - for index in range(len(node))[::2]: - if node[index].text is None: - result[''] = _to_python(node[index+1], depth+1) - else: - result[node[index].text] = _to_python(node[index+1], depth+1) - return result - - -def _array_to_python(node, depth=0): - "Convert array node to a python object." - return [_to_python(child, depth+1) for child in node] - - -NODE_HANDLERS = dict( - undef=lambda x,y: None, - boolean=_bool_to_python, - integer=_int_to_python, - real=_real_to_python, - uuid=_uuid_to_python, - string=_str_to_python, - binary=_bin_to_python, - date=_date_to_python, - uri=_uri_to_python, - map=_map_to_python, - array=_array_to_python, -) - - -def _to_python(node, depth=0): - "Convert node to a python object." - if depth > MAX_PARSE_DEPTH: - raise LLSDParseError("Cannot parse depth of more than %d" % MAX_PARSE_DEPTH) - - return NODE_HANDLERS[node.tag](node, depth) - - -class LLSDBaseFormatter(object): - """ - This base class cannot be instantiated on its own: it assumes a subclass - containing methods with canonical names specified in self.__init__(). The - role of this base class is to provide self.type_map based on the methods - defined in its subclass. - """ - __slots__ = ['stream', 'type_map'] - - def __init__(self): - "Construct a new formatter dispatch table." - self.stream = None - self.type_map = { - type(None): self._UNDEF, - undef: self._UNDEF, - bool: self._BOOLEAN, - int: self._INTEGER, - LongType: self._INTEGER, - float: self._REAL, - uuid.UUID: self._UUID, - binary: self._BINARY, - str: self._STRING, - UnicodeType: self._STRING, - newstr: self._STRING, - uri: self._URI, - datetime.datetime: self._DATE, - datetime.date: self._DATE, - list: self._ARRAY, - tuple: self._ARRAY, - types.GeneratorType: self._ARRAY, - dict: self._MAP, - _LLSD: self._LLSD, - } - - - def format(self, something): - """ - Pure Python implementation of the formatter. - Format a python object according to subclass formatting. - - :param something: A python object (typically a dict) to be serialized. - :returns: A serialized bytes object. - """ - stream = io.BytesIO() - self.write(stream, something) - return stream.getvalue() - - def write(self, stream, something): - """ - Serialize a python object to the passed binary 'stream' according to - subclass formatting. - - :param stream: A binary file-like object to which to serialize 'something'. - :param something: A python object (typically a dict) to be serialized. - """ - self.stream = stream - try: - return self._write(something) - finally: - self.stream = None - - -_X_ORD = ord(b'x') -_BACKSLASH_ORD = ord(b'\\') -_DECODE_BUFF_ALLOC_SIZE = 1024 - - -class LLSDBaseParser(object): - """ - Utility methods useful for parser subclasses. - """ - __slots__ = ['_stream', '_decode_buff'] - - def __init__(self, something=b''): - self._reset(something) - # Scratch space for decoding delimited strings - self._decode_buff = bytearray(_DECODE_BUFF_ALLOC_SIZE) - - def _reset(self, something): - if isinstance(something, LLSDBaseParser): - # When passed an existing LLSDBaseParser (subclass) instance, just - # borrow its existing _stream. - self._stream = something._stream - elif isinstance(something, bytes): - # Wrap an incoming bytes string into a stream. If the passed bytes - # string is so large that the overhead of copying it into a - # BytesIO is significant, advise caller to pass a stream instead. - self._stream = io.BytesIO(something) - elif something.seekable(): - # 'something' is already a seekable stream, use directly - self._stream = something - else: - # 'something' isn't seekable, wrap in BufferedReader - # (let BufferedReader handle the problem of passing an - # inappropriate object) - self._stream = io.BufferedReader(something) - - def starts_with(self, pattern): - """ - Like matchseq(), except that starts_with() doesn't consume what it - matches: it always resets our input stream to its previous position. - """ - oldpos = self._stream.tell() - try: - return self.matchseq(pattern) - finally: - self._stream.seek(oldpos) - - def matchseq(self, pattern): - """ - Match bytes object 'pattern' after skipping arbitrary leading - whitespace. After successfully matching 'pattern', skip trailing - whitespace as well. - - 'pattern' is NOT a regular expression, but a bytes string in which - each space character matches zero or more whitespace characters in the - stream. Non-space characters are matched case-insensitively. - - If 'pattern' matches, return True and leave our input stream advanced - past the last byte examined. - - If 'pattern' does not match, return False and reset our input stream - to its previous read position. - """ - oldpos = self._stream.tell() - for chunk in pattern.split(): - # skip leading space before this chunk - c = self._next_nonblank() - # if we hit EOF, no match - if not c: - self._stream.seek(oldpos) - return False - # not EOF: try to match non-empty chunk, - # not forgetting that 'c' is a lookahead byte - # (split() never produces a zero-length chunk) - maybe = c + self._stream.read(len(chunk)-1) - if maybe.lower() != chunk.lower(): - # mismatch, reset - self._stream.seek(oldpos) - return False - # so far so good, back for next chunk - - # here we've matched every chunk, with the read pointer just at the end of - # the last matched chunk -- skip trailing space - if self._next_nonblank(): - # back up one character, i.e. put back the nonblank - self._stream.seek(-1, io.SEEK_CUR) - # success! - return True - - def remainder(self): - # return a stream object representing the parse input (from last - # _reset() call), whose read position is set past scanned input - return self._stream - - def _next_nonblank(self): - # we directly call read() rather than getc() because our caller is - # prepared to handle empty string, meaning EOF - # (YES we want the walrus operator) - c = self._stream.read(1) - while c.isspace(): - c = self._stream.read(1) - return c - - def _getc(self, num=1, full=True): - got = self._stream.read(num) - if full and len(got) < num: - self._error("Trying to read past end of stream") - return got - - def _putback(self, cc): - # if this test fails, it's not a user error, it's a coding error - assert self._stream.tell() >= len(cc) - self._stream.seek(-len(cc), io.SEEK_CUR) - - def _error(self, message, offset=0): - oldpos = self._stream.tell() - # 'offset' is relative to current pos - self._stream.seek(offset, io.SEEK_CUR) - raise LLSDParseError("%s at byte %d: %r" % - (message, oldpos+offset, self._getc(1, full=False))) - - # map char following escape char to corresponding character - _escaped = { - ord(b'a'): ord(b'\a'), - ord(b'b'): ord(b'\b'), - ord(b'f'): ord(b'\f'), - ord(b'n'): ord(b'\n'), - ord(b'r'): ord(b'\r'), - ord(b't'): ord(b'\t'), - ord(b'v'): ord(b'\v'), - } - - def _parse_string_delim(self, delim): - "Parse a delimited string." - insert_idx = 0 - delim_ord = ord(delim) - # Preallocate a working buffer for the decoded string output - # to avoid allocs in the hot loop. - decode_buff = self._decode_buff - # Cache this in locals, otherwise we have to perform a lookup on - # `self` in the hot loop. - getc = self._getc - cc = 0 - while True: - try: - cc = ord(getc()) - - if cc == _BACKSLASH_ORD: - # Backslash, figure out if this is an \xNN hex escape or - # something like \t - cc = ord(getc()) - if cc == _X_ORD: - # It's a hex escape. char is the value of the two - # following hex nybbles. This slice may result in - # a short read (0 or 1 bytes), but either a - # `ValueError` will be triggered by the first case, - # and the second will cause an `IndexError` on the - # next iteration of the loop. - hex_bytes = getc(2) - try: - # int() can parse a `bytes` containing hex, - # no explicit `bytes.decode("ascii")` required. - cc = int(hex_bytes, 16) - except ValueError as e: - # One of the hex characters was likely invalid. - # Wrap the ValueError so that we can provide a - # byte offset in the error. - self._error(e, offset=-2) - else: - # escape char preceding anything other than the chars - # in _escaped just results in that same char without - # the escape char - cc = self._escaped.get(cc, cc) - elif cc == delim_ord: - break - except IndexError: - # We can be reasonably sure that any IndexErrors inside here - # were caused by an out-of-bounds `buff[read_idx]`. - self._error("Trying to read past end of buffer") - - try: - decode_buff[insert_idx] = cc - except IndexError: - # Oops, that overflowed the decoding buffer, make a - # new expanded buffer containing the existing contents. - decode_buff = bytearray(decode_buff) - decode_buff.extend(b"\x00" * _DECODE_BUFF_ALLOC_SIZE) - decode_buff[insert_idx] = cc - - insert_idx += 1 - - # Sync our local read index with the canonical one - try: - # Slice off only what we used of the working decode buffer - return decode_buff[:insert_idx].decode('utf-8') - except UnicodeDecodeError as exc: - self._error(exc) diff --git a/llsd/fastest_elementtree.py b/llsd/fastest_elementtree.py deleted file mode 100644 index 0f89f8e..0000000 --- a/llsd/fastest_elementtree.py +++ /dev/null @@ -1,65 +0,0 @@ -""" -Concealing some gnarly import logic in here. This should export -the interface of elementtree. - -The parsing exception raised by the underlying library depends on the -ElementTree implementation we're using, so we provide an alias here. - -Generally, you can use this module as a drop in replacement for how -you would use ElementTree or cElementTree. - -
-from fastest_elementtree import fromstring
-fromstring(...)
-
- -Use ElementTreeError as the exception type for catching parsing -errors. -""" - -# TODO: drop version sensitivity, replacing entire module with: -#from xml.etree.ElementTree import * -#ElementTreeError = ParseError - -## -# Using cElementTree might cause some unforeseen problems, so here's a -# convenient off switch during development and testing. -_use_celementree = True - -# xml.etree.cElementTree has been deprecated since Python 3.3. -# For speed in the common case of Python 3.3+, don't even start with that. -import sys -if sys.version_info[:2] >= (3, 3): - _use_celementree = False - -try: - # nat wishes for a nicer way to skip even attempting cElementTree than by - # explicitly raising ImportError. The problem is that we want to be able - # to 'import *' into the global namespace, which forbids packaging any of - # this logic in a function. Nor can we 'return' early from a module. It - # seems the only way to avoid the explicit exception would be to restate - # the entirety of each 'except ImportError' clause, which would be worse. - if not _use_celementree: - raise ImportError() - # Python 2.5 and above. - from xml.etree.cElementTree import * - ElementTreeError = SyntaxError -except ImportError: - try: - # Python 2.5 and above: the common case. - from xml.etree.ElementTree import * - try: - # Python 3 - ElementTreeError = ParseError - except NameError: - # The older Python ElementTree module uses Expat for parsing. - from xml.parsers.expat import ExpatError as ElementTreeError - except ImportError: - # Python 2.3 and 2.4. - try: - if not _use_celementree: - raise ImportError() - from cElementTree import * - ElementTreeError = SyntaxError - except ImportError: - from elementtree.ElementTree import * diff --git a/llsd/serde_binary.py b/llsd/serde_binary.py deleted file mode 100644 index e4ac7c5..0000000 --- a/llsd/serde_binary.py +++ /dev/null @@ -1,262 +0,0 @@ -import calendar -import datetime -import io -import struct -import uuid - -from llsd.base import (_LLSD, LLSDBaseParser, LLSDSerializationError, BINARY_HEADER, - MAX_FORMAT_DEPTH, MAX_PARSE_DEPTH, _str_to_bytes, binary, is_integer, is_string, uri) - - -try: - # Python 2: make 'range()' lazy like Python 3 - range = xrange -except NameError: - # Python 3: 'range()' is already lazy - pass - -class LLSDBinaryParser(LLSDBaseParser): - """ - Parse application/llsd+binary to a python object. - - See http://wiki.secondlife.com/wiki/LLSD#Binary_Serialization - """ - __slots__ = ['_dispatch', '_keep_binary', '_depth'] - - def __init__(self): - super(LLSDBinaryParser, self).__init__() - # One way of dispatching based on the next character we see would be a - # dict lookup, and indeed that's the best way to express it in source. - _dispatch_dict = { - b'{': self._parse_map, - b'[': self._parse_array, - b'!': lambda: None, - b'0': lambda: False, - b'1': lambda: True, - # 'i' = integer - b'i': lambda: struct.unpack("!i", self._getc(4))[0], - # 'r' = real number - b'r': lambda: struct.unpack("!d", self._getc(8))[0], - # 'u' = uuid - b'u': lambda: uuid.UUID(bytes=self._getc(16)), - # 's' = string - b's': self._parse_string, - # delimited/escaped string - b"'": lambda: self._parse_string_delim(b"'"), - b'"': lambda: self._parse_string_delim(b'"'), - # 'l' = uri - b'l': lambda: uri(self._parse_string()), - # 'd' = date in seconds since epoch - b'd': self._parse_date, - # 'b' = binary - # *NOTE: if not self._keep_binary, maybe have a binary placeholder - # which has the length. - b'b': lambda: bytes(self._parse_string_raw()) if self._keep_binary else None, - } - # But in fact it should be even faster to construct a list indexed by - # ord(char). Start by filling it with the 'else' case. Use offset=-1 - # because by the time we perform this lookup, we've scanned past the - # lookup char. - self._dispatch = 256*[lambda: self._error("invalid binary token", -1)] - # Now use the entries in _dispatch_dict to set the corresponding - # entries in _dispatch. - for c, func in _dispatch_dict.items(): - self._dispatch[ord(c)] = func - self._depth = 0 - - def parse(self, something, ignore_binary = False): - """ - This is the basic public interface for parsing. - - :param something: serialized LLSD to parse: a bytes object, a binary - stream or an LLSDBaseParser subclass. - :param ignore_binary: parser throws away data in llsd binary nodes. - :returns: returns a python object. - """ - self._reset(something) - self._keep_binary = not ignore_binary - try: - return self._parse() - except struct.error as exc: - self._error(exc) - - def _parse(self): - "The actual parser which is called recursively when necessary." - if self._depth > MAX_PARSE_DEPTH: - self._error("Parse depth exceeded maximum depth of %d." % MAX_PARSE_DEPTH) - - cc = self._getc() - try: - func = self._dispatch[ord(cc)] - except IndexError: - self._error("invalid binary token", -1) - else: - return func() - - def _parse_map(self): - "Parse a single llsd map" - rv = {} - size = struct.unpack("!i", self._getc(4))[0] - count = 0 - cc = self._getc() - key = b'' - self._depth += 1 - while (cc != b'}') and (count < size): - if cc == b'k': - key = self._parse_string() - elif cc in (b"'", b'"'): - key = self._parse_string_delim(cc) - else: - self._error("invalid map key", -1) - value = self._parse() - rv[key] = value - count += 1 - cc = self._getc() - if cc != b'}': - self._error("invalid map close token") - self._depth -= 1 - return rv - - def _parse_array(self): - "Parse a single llsd array" - rv = [] - self._depth += 1 - size = struct.unpack("!i", self._getc(4))[0] - for count in range(size): - rv.append(self._parse()) - if self._getc() != b']': - self._error("invalid array close token") - self._depth -= 1 - return rv - - def _parse_string(self): - try: - return self._parse_string_raw().decode('utf-8') - except UnicodeDecodeError as exc: - self._error(exc) - - def _parse_string_raw(self): - "Parse a string which has the leadings size indicator" - try: - size = struct.unpack("!i", self._getc(4))[0] - except struct.error as exc: - # convert exception class for client convenience - self._error("struct " + str(exc)) - rv = self._getc(size) - return rv - - def _parse_date(self): - seconds = struct.unpack("\n') - _write_binary_recurse(stream, something, 0) - - -def _write_binary_recurse(stream, something, depth): - "Binary formatter workhorse." - - if depth > MAX_FORMAT_DEPTH: - raise LLSDSerializationError("Cannot serialize depth of more than %d" % MAX_FORMAT_DEPTH) - - if something is None: - stream.write(b'!') - elif isinstance(something, _LLSD): - _write_binary_recurse(stream, something.thing, depth) - elif isinstance(something, bool): - stream.write(b'1' if something else b'0') - elif is_integer(something): - try: - stream.writelines([b'i', struct.pack('!i', something)]) - except (OverflowError, struct.error) as exc: - raise LLSDSerializationError(str(exc), something) - elif isinstance(something, float): - try: - stream.writelines([b'r', struct.pack('!d', something)]) - except SystemError as exc: - raise LLSDSerializationError(str(exc), something) - elif isinstance(something, uuid.UUID): - stream.writelines([b'u', something.bytes]) - elif isinstance(something, binary): - stream.writelines([b'b', struct.pack('!i', len(something)), something]) - elif is_string(something): - something = _str_to_bytes(something) - stream.writelines([b's', struct.pack('!i', len(something)), something]) - elif isinstance(something, uri): - stream.writelines([b'l', struct.pack('!i', len(something)), something]) - elif isinstance(something, datetime.datetime): - seconds_since_epoch = calendar.timegm(something.utctimetuple()) \ - + something.microsecond // 1e6 - stream.writelines([b'd', struct.pack(' MAX_PARSE_DEPTH: - self._error("Parse depth exceeded max of %d" % MAX_PARSE_DEPTH) - try: - func = self._dispatch[ord(cc)] - except IndexError: - # output error if the token was out of range - self._error("Invalid notation token") - else: - # pass the lookahead character that selected this func - return func(cc) - - def _parse_binary(self, cc): - "parse a single binary object." - - # skip the beginning 'b' - cc = self._getc() - if cc == b'(': - # parse raw binary - # grab the 'expected' size of the binary data - size = self._get_until(b')') - if size == None: - self._error("Invalid binary size") - size = int(size) - - # grab the opening quote - q = self._getc() - if q != b'"': - self._error('Expected " to start binary value') - - # grab the data - data = self._getc(size) - - # grab the closing quote - q = self._getc() - if q != b'"': - self._error('Expected " to end binary value') - - return binary(data) - - else: - # get the encoding base - base = cc + self._getc() - try: - decoder = { - b'16': base64.b16decode, - b'64': base64.b64decode, - }[base] - except KeyError: - self._error("Parser doesn't support base %s encoding" % - base.decode('latin-1')) - - # grab the double quote - q = self._getc() - if q != b'"': - self._error('Expected " to start binary value') - - # grab the encoded data - encoded = self._get_until(q) - - try: - return binary(decoder(encoded or b'')) - except binascii.Error as exc: - # convert exception class so it's more catchable - self._error("Encoded binary data: " + str(exc)) - except TypeError as exc: - # convert exception class so it's more catchable - self._error("Bad binary data: " + str(exc)) - - def _parse_map(self, cc): - """ - parse a single map - - map: { string:object, string:object } - """ - rv = {} - key = b'' - found_key = False - self._depth += 1 - # skip the beginning '{' - cc = self._getc() - while (cc != b'}'): - if cc is None: - self._error("Unclosed map") - if not found_key: - if cc in (b"'", b'"', b's'): - key = self._parse_string(cc) - found_key = True - elif cc.isspace() or cc == b',': - # ignore space or comma - pass - else: - self._error("Invalid map key") - elif cc.isspace(): - # ignore space - pass - elif cc == b':': - # skip the ':' - value = self._parse(self._getc()) - rv[key] = value - found_key = False - else: - self._error("missing separator") - cc = self._getc() - self._depth -= 1 - - return rv - - def _parse_array(self, cc): - """ - parse a single array. - - array: [ object, object, object ] - """ - rv = [] - self._depth += 1 - # skip the beginning '[' - cc = self._getc() - while (cc != b']'): - if cc is None: - self._error('Unclosed array') - if cc.isspace() or cc == b',': - cc = self._getc() - continue - rv.append(self._parse(cc)) - cc = self._getc() - self._depth -= 1 - return rv - - def _parse_uuid(self, cc): - "Parse a uuid." - # ignore the beginning 'u' - # see comment on LLSDNotationFormatter._UUID() re use of latin-1 - return uuid.UUID(hex=self._getc(36).decode('latin-1')) - - def _parse_uri(self, cc): - "Parse a URI." - # skip the beginning 'l' - return uri(self._parse_string(self._getc())) - - def _parse_date(self, cc): - "Parse a date." - # skip the beginning 'd' - datestr = self._parse_string(self._getc()) - return _parse_datestr(datestr) - - def _parse_real(self, cc): - "Parse a floating point number." - # recognize: - # [+-]?inf - # [+-]?nan - # [+-]?basepart([eE][+-]?\d+)? - # where basepart could be either: - # \d+(\.\d*)? or - # \d*\.\d+ - digits = [] - # skip the beginning 'r' - cc = self._collect_sign(self._getc(), digits) - try: - rest = {b'i': b'nf', b'n': b'an'}[cc] - except KeyError: - # cc is neither 'i' nor 'n', must be a digit: - # collect integer digits - idigits = [] - fdigits = [] - edigits = [] - cc = self._collect_digits(cc, idigits) - digits.extend(idigits) - if cc == b'.': - digits.append(cc) - # skip decimal point and collect fractional digits - cc = self._collect_digits(self._getc(full=False), fdigits) - digits.extend(fdigits) - # Fun fact: (cc in b'eE') is True even when cc is b''! - if cc in (b'e', b'E'): - digits.append(cc) - # skip 'e' and check for exponent sign - cc = self._collect_sign(self._getc(), digits) - cc = self._collect_digits(cc, edigits) - digits.extend(edigits) - if not edigits: - # if 'e' is present, there MUST be an exponent - self._error('Invalid real exponent') - # Whether this real number ended after the integer part, after the - # decimal point, after the fractional part or after the exponent, - # cc is now one character PAST the end -- put it back. - self._putback(cc) - # The reason we collected idigits and fdigits separately is that - # while either may be empty, they may not BOTH be empty. - if not (idigits or fdigits): - self._error('Invalid real number') - else: - # cc is either 'i' for 'inf' or 'n' for 'nan', - # rest is 'nf' or 'an' - digits.extend([cc, self._expect(cc + rest, rest)]) - - return float(b''.join(digits)) - - def _parse_integer(self, cc): - "Parse an integer." - digits = [] - # skip the beginning 'i' - cc = self._collect_sign(self._getc(), digits) - cc = self._collect_digits(cc, digits) - if not digits: - self._error('Invalid integer token') - - # cc is now the next _getc() after the last digit -- back up - self._putback(cc) - - return int(b''.join(digits)) - - def _collect_sign(self, cc, digits): - if cc in (b'+', b'-'): - digits.append(cc) - cc = self._getc() - return cc - - def _collect_digits(self, cc, digits): - while cc.isdigit(): - digits.append(cc) - # we can accept EOF happening here - cc = self._getc(full=False) - return cc - - def _parse_string(self, delim): - """ - Parse a string - - string: "g\'day" | 'have a "nice" day' | s(size)"raw data" - """ - rv = "" - if delim in (b"'", b'"'): - rv = self._parse_string_delim(delim) - elif delim == b's': - rv = self._parse_string_raw() - else: - self._error("invalid string token") - - return rv - - def _parse_string_raw(self): - """ - Parse a sized specified string. - - string: s(size)"raw data" - """ - # Read the (size) portion. - cc = self._getc() - if cc != b'(': - self._error("Invalid string token") - - size = self._get_until(b')') - if size == None: - self._error("Invalid string size") - size = int(size) - - delim = self._getc() - if delim not in (b"'", b'"'): - self._error("Invalid string token") - - rv = self._getc(size) - cc = self._getc() - if cc != delim: - self._error("Invalid string closure token") - try: - return rv.decode('utf-8') - except UnicodeDecodeError as exc: - raise LLSDParseError(exc) - - def _parse_true(self, cc): - # match t, T, true, TRUE -- not mixed-case - return self._parse_bool(cc, True, (b'true', b'TRUE')) - - def _parse_false(self, cc): - # match f, F, false, FALSE -- not mixed-case - return self._parse_bool(cc, False, (b'false', b'FALSE')) - - def _parse_bool(self, cc, result, tokens): - try: - # Index on first character to find expected rest. - # Beware, token is bytes, so token[0] is an int! - rest = {token[:1]: token[1:] for token in tokens}[cc] - except KeyError: - self._error("Invalid '%s' token" % tokens[0]) - - cc = self._getc(full=False) - if cc != rest[:1]: - # legal to have only first char, put back cc and carry on - self._putback(cc) - return result - - # saw 'tr' or 'TR' (or 'fa' or 'FA'), cc is the second char: - # MUST be followed by the rest of 'rest' - self._expect(tokens[0], rest[1:]) - return result - - def _expect(self, token, match): - # verify that the next several chars are exactly what we expect - if self._getc(len(match), full=False) != match: - self._error("Invalid '%s' token" % token) - return match - - -class LLSDNotationFormatter(LLSDBaseFormatter): - """ - Serialize a python object as application/llsd+notation - - See http://wiki.secondlife.com/wiki/LLSD#Notation_Serialization - """ - - def __init__(self): - super(LLSDNotationFormatter, self).__init__() - self._depth = 0 - - def _LLSD(self, v): - return self._generate(v.thing) - def _UNDEF(self, v): - self.stream.write(b'!') - def _BOOLEAN(self, v): - self.stream.write(b'true' if v else b'false') - def _INTEGER(self, v): - self.stream.write(B("i%d") % v) - def _REAL(self, v): - self.stream.write(B("r%r") % v) - def _UUID(self, v): - # latin-1 is the byte-to-byte encoding, mapping \x00-\xFF -> - # \u0000-\u00FF. It's also the fastest encoding, I believe, from - # https://docs.python.org/3/library/codecs.html#encodings-and-unicode - # UUID doesn't like the hex to be a bytes object, so I have to - # convert it to a string. I chose latin-1 to exactly match the old - # error behavior in case someone passes an invalid hex string, with - # things other than 0-9a-fA-F, so that they will fail in the UUID - # decode, rather than with a UnicodeError. - self.stream.writelines([b"u", str(v).encode('latin-1')]) - def _BINARY(self, v): - self.stream.writelines([b'b64"', base64.b64encode(v).strip(), b'"']) - - def _STRING(self, v): - self.stream.writelines([b"'", self._esc(v), b"'"]) - def _URI(self, v): - self.stream.writelines([b'l"', self._esc(v, b'"'), b'"']) - def _DATE(self, v): - self.stream.writelines([b'd"', _format_datestr(v), b'"']) - def _ARRAY(self, v): - self.stream.write(b'[') - delim = b'' - self._depth += 1 - for item in v: - self.stream.write(delim) - self._generate(item) - delim = b',' - self._depth -= 1 - self.stream.write(b']') - def _MAP(self, v): - self.stream.write(b'{') - delim = b'' - self._depth += 1 - for key, value in v.items(): - self.stream.writelines([delim, b"'", self._esc(UnicodeType(key)), b"':"]) - self._generate(value) - delim = b',' - self._depth -= 1 - self.stream.write(b'}') - - def _esc(self, data, quote=b"'"): - return _str_to_bytes(data).replace(b"\\", b"\\\\").replace(quote, b'\\'+quote) - - def _generate(self, something): - """ - Serialize a python object to self.stream as application/llsd+notation - - :param something: a python object (typically a dict) to be serialized. - """ - if self._depth > MAX_FORMAT_DEPTH: - raise LLSDSerializationError("Cannot serialize depth of more than %d" % MAX_FORMAT_DEPTH) - - t = type(something) - handler = self.type_map.get(t) - if handler: - return handler(something) - elif isinstance(something, _LLSD): - return self.type_map[_LLSD](something) - else: - try: - return self._ARRAY(iter(something)) - except TypeError: - raise LLSDSerializationError( - "Cannot serialize unknown type: %s (%s)" % (t, something)) - - # _write() method is an alias for _generate() - _write = _generate - - -def format_notation(something): - """ - Format a python object as application/llsd+notation - - :param something: a python object (typically a dict) to be serialized. - :returns: Returns a LLSD notation formatted string. - - See http://wiki.secondlife.com/wiki/LLSD#Notation_Serialization - """ - return LLSDNotationFormatter().format(something) - - -def write_notation(stream, something): - """ - Serialize to passed binary 'stream' a python object 'something' as - application/llsd+notation. - - :param stream: a binary stream open for writing. - :param something: a python object (typically a dict) to be serialized. - - See http://wiki.secondlife.com/wiki/LLSD#Notation_Serialization - """ - return LLSDNotationFormatter().write(stream, something) - - -def parse_notation(something): - """ - This is the basic public interface for parsing llsd+notation. - - :param something: The data to parse. - :returns: Returns a python object. - """ - # Try to match header, and if matched, skip past it. - parser = LLSDBaseParser(something) - parser.matchseq(NOTATION_HEADER) - # If we matched the header, then parse whatever follows, else parse the - # original bytes object or stream. - return parse_notation_nohdr(parser) - - -def parse_notation_nohdr(baseparser): - """ - Parse llsd+notation known to be without a header. - - :param baseparser: LLSDBaseParser instance wrapping the data to parse. - :returns: Returns a python object. - """ - return LLSDNotationParser().parse(baseparser) diff --git a/llsd/serde_xml.py b/llsd/serde_xml.py deleted file mode 100644 index 3833f57..0000000 --- a/llsd/serde_xml.py +++ /dev/null @@ -1,299 +0,0 @@ -import base64 -import io -import re - -from llsd.base import (_LLSD, ALL_CHARS, LLSDBaseParser, LLSDBaseFormatter, XML_HEADER, - MAX_FORMAT_DEPTH, LLSDParseError, LLSDSerializationError, UnicodeType, - _format_datestr, _str_to_bytes, _to_python, is_unicode, PY2) -from llsd.fastest_elementtree import ElementTreeError, fromstring, parse as _parse - -INVALID_XML_BYTES = b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x0b\x0c'\ - b'\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18'\ - b'\x19\x1a\x1b\x1c\x1d\x1e\x1f' -INVALID_XML_RE = re.compile(r'[\x00-\x08\x0b\x0c\x0e-\x1f]') - - -XML_ESC_TRANS = {} -if not PY2: - XML_ESC_TRANS = str.maketrans({'&': '&', - '<':'<', - '>':'>', - u'\uffff':None, # cannot be parsed - u'\ufffe':None}) # cannot be parsed - - for x in INVALID_XML_BYTES: - XML_ESC_TRANS[x] = None - -def remove_invalid_xml_bytes(b): - """ - Remove characters that aren't allowed in xml. - """ - try: - # Dropping chars that cannot be parsed later on. The - # translate() function was benchmarked to be the fastest way - # to do this. - return b.translate(ALL_CHARS, INVALID_XML_BYTES) - except TypeError: - # we get here if s is a unicode object (should be limited to - # unit tests) - return INVALID_XML_RE.sub('', b) - -# only python2, which is not covered by coverage tests -def xml_esc(v): # pragma: no cover - "Escape string or unicode object v for xml output" - - # Use is_unicode() instead of is_string() because in python 2, str is - # bytes, not unicode, and should not be "encode()"d. Attempts to - # encode("utf-8") a bytes type will result in an implicit - # decode("ascii") that will throw a UnicodeDecodeError if the string - # contains non-ascii characters. - if is_unicode(v): - # we need to drop these invalid characters because they - # cannot be parsed (and encode() doesn't drop them for us) - v = v.replace(u'\uffff', u'') - v = v.replace(u'\ufffe', u'') - v = v.encode('utf-8') - v = remove_invalid_xml_bytes(v) - return v.replace(b'&',b'&').replace(b'<',b'<').replace(b'>',b'>') - - -class LLSDXMLFormatter(LLSDBaseFormatter): - """ - Class which implements LLSD XML serialization. - - http://wiki.secondlife.com/wiki/LLSD#XML_Serialization - - This class serializes a limited subset of python objects as - application/llsd+xml. You do not generally need to make an instance of - this class since the module level format_xml() is the most convenient - interface to this functionality. - """ - - def __init__(self, indent_atom = b'', eol = b''): - "Construct a serializer." - # Call the super class constructor so that we have the type map - super(LLSDXMLFormatter, self).__init__() - self._indent_atom = indent_atom - self._eol = eol - self._depth = 1 - - def _indent(self): - pass - - def _LLSD(self, v): - return self._generate(v.thing) - def _UNDEF(self, _v): - self.stream.writelines([b'', self._eol]) - def _BOOLEAN(self, v): - if v: - return self.stream.writelines([b'true', self._eol]) - self.stream.writelines([b'false', self._eol]) - def _INTEGER(self, v): - self.stream.writelines([b'', str(v).encode('utf-8'), b'', self._eol]) - def _REAL(self, v): - self.stream.writelines([b'', str(v).encode('utf-8'), b'', self._eol]) - def _UUID(self, v): - if v.int == 0: - return self.stream.writelines([b'', self._eol]) - self.stream.writelines([b'', str(v).encode('utf-8'), b'', self._eol]) - def _BINARY(self, v): - self.stream.writelines([b'', base64.b64encode(v).strip(), b'', self._eol]) - - if PY2: - def _STRING(self, v): - return self.stream.writelines([b'', _str_to_bytes(xml_esc(v)), b'', self._eol]) - def _URI(self, v): - return self.stream.writelines([b'', _str_to_bytes(xml_esc(v)), b'', self._eol]) - else: - def _STRING(self, v): - self.stream.writelines([b'', v.translate(XML_ESC_TRANS).encode('utf-8'), b'', self._eol]) - def _URI(self, v): - self.stream.writelines([b'', str(v).translate(XML_ESC_TRANS).encode('utf-8'), b'', self._eol]) - - def _DATE(self, v): - self.stream.writelines([b'', _format_datestr(v), b'', self._eol]) - def _ARRAY(self, v): - self.stream.writelines([b'', self._eol]) - self._depth += 1 - for item in v: - self._indent() - self._generate(item) - self._depth -= 1 - self._indent() - self.stream.writelines([b'', self._eol]) - def _MAP(self, v): - self.stream.writelines([b'', self._eol]) - self._depth += 1 - for key, value in v.items(): - self._indent() - if PY2: # pragma: no cover - self.stream.writelines([b'', - xml_esc(UnicodeType(key)), - b'', - self._eol]) - else: - self.stream.writelines([b'', - UnicodeType(key).translate(XML_ESC_TRANS).encode('utf-8'), - b'', - self._eol]) - self._indent() - self._generate(value) - self._depth -= 1 - self._indent() - self.stream.writelines([b'', self._eol]) - - def _generate(self, something): - "Generate xml from a single python object." - if self._depth - 1 > MAX_FORMAT_DEPTH: - raise LLSDSerializationError("Cannot serialize depth of more than %d" % MAX_FORMAT_DEPTH) - t = type(something) - if t in self.type_map: - return self.type_map[t](something) - elif isinstance(something, _LLSD): - return self.type_map[_LLSD](something) - else: - raise LLSDSerializationError( - "Cannot serialize unknown type: %s (%s)" % (t, something)) - - def _write(self, something): - """ - Serialize a python object to self.stream as application/llsd+xml. - :param something: A python object (typically a dict) to be serialized. - """ - self.stream.writelines([b'', self._eol, - b'', self._eol]) - self._generate(something) - self.stream.write(b'' + self._eol) - - -class LLSDXMLPrettyFormatter(LLSDXMLFormatter): - """ - Class which implements 'pretty' LLSD XML serialization.. - - See http://wiki.secondlife.com/wiki/LLSD#XML_Serialization - - The output conforms to the LLSD DTD, unlike the output from the - standard python xml.dom DOM::toprettyxml() method which does not - preserve significant whitespace. - - This class is not necessarily suited for serializing very large objects. - It sorts on dict (llsd map) keys alphabetically to ease human reading. - """ - def __init__(self, indent_atom = b' ', eol = b'\n'): - "Construct a pretty serializer." - # Call the super class constructor so that we have the type map - super(LLSDXMLPrettyFormatter, self).__init__(indent_atom = indent_atom, eol = eol) - - def _indent(self): - "Write an indentation based on the atom and indentation level." - self.stream.writelines([self._indent_atom] * self._depth) - - -def format_pretty_xml(something): - """ - Serialize a python object as 'pretty' application/llsd+xml. - - :param something: a python object (typically a dict) to be serialized. - :returns: Returns an XML formatted string. - - See http://wiki.secondlife.com/wiki/LLSD#XML_Serialization - - The output conforms to the LLSD DTD, unlike the output from the - standard python xml.dom DOM::toprettyxml() method which does not - preserve significant whitespace. - This function is not necessarily suited for serializing very large - objects. It sorts on dict (llsd map) keys alphabetically to ease human - reading. - """ - return LLSDXMLPrettyFormatter().format(something) - - -def write_pretty_xml(stream, something): - """ - Serialize to passed 'stream' the python object 'something' as 'pretty' - application/llsd+xml. - - :param stream: a binary stream open for writing. - :param something: a python object (typically a dict) to be serialized. - - See http://wiki.secondlife.com/wiki/LLSD#XML_Serialization - - The output conforms to the LLSD DTD, unlike the output from the - standard python xml.dom DOM::toprettyxml() method which does not - preserve significant whitespace. - This function is not necessarily suited for serializing very large - objects. It sorts on dict (llsd map) keys alphabetically to ease human - reading. - """ - return LLSDXMLPrettyFormatter().write(stream, something) - - -def parse_xml(something): - """ - This is the basic public interface for parsing llsd+xml. - - :param something: The data to parse. - :returns: Returns a python object. - """ - # Try to match header, and if matched, skip past it. - parser = LLSDBaseParser(something) - parser.matchseq(XML_HEADER) - # If we matched the header, then parse whatever follows, else parse the - # original bytes object or stream. - return parse_xml_nohdr(parser) - - -def parse_xml_nohdr(baseparser): - """ - Parse llsd+xml known to be without an header. May still - have a normal XML declaration, e.g. . - - :param baseparser: LLSDBaseParser instance wrapping the data to parse. - :returns: Returns a python object. - """ - # Python 3.9's xml.etree.ElementTree.fromstring() does not like whitespace - # before XML declaration. Since we explicitly test support for that case, - # skip initial whitespace. - baseparser.matchseq(b'') - stream = baseparser.remainder() - try: - if isinstance(stream, io.BytesIO): - # Empirically, fromstring() seems faster than _parse(). If passed - # a BytesIO, extract its contents and skip to BytesIO read pos. - element = fromstring(stream.getvalue()[stream.tell():]) - else: - # Not a BytesIO, parse the stream - element = _parse(stream).getroot() - except ElementTreeError as err: - raise LLSDParseError(*err.args) - - # We expect that the outer-level XML element is .... - if element.tag != 'llsd': - raise LLSDParseError("Invalid XML Declaration") - # Extract its contents. - return _to_python(element[0]) - - -def format_xml(something): - """ - Format a python object as application/llsd+xml - - :param something: a python object (typically a dict) to be serialized. - :returns: Returns an XML formatted string. - - See http://wiki.secondlife.com/wiki/LLSD#XML_Serialization - """ - return LLSDXMLFormatter().format(something) - - -def write_xml(stream, something): - """ - Serialize to passed 'stream' the python object 'something' as - application/llsd+xml. - - :param stream: a binary stream open for writing. - :param something: a python object (typically a dict) to be serialized. - - See http://wiki.secondlife.com/wiki/LLSD#XML_Serialization - """ - return LLSDXMLFormatter().write(stream, something) diff --git a/setup.py b/setup.py deleted file mode 100644 index db8beb0..0000000 --- a/setup.py +++ /dev/null @@ -1,35 +0,0 @@ -import os - -from setuptools import find_packages, setup - -root_dir = os.path.dirname(__file__) -with open(os.path.join(root_dir, "README.md")) as f: - long_description = f.read() - - -setup( - name="llsd", - url="https://github.com/secondlife/python-llsd", - license="MIT", - author="Linden Research, Inc.", - author_email="opensource-dev@lists.secondlife.com", - description="Linden Lab Structured Data (LLSD) serialization library", - long_description=long_description, - long_description_content_type="text/markdown", - packages=find_packages(exclude=("tests",)), - setup_requires=["setuptools_scm<6"], - use_scm_version={ - 'local_scheme': 'no-local-version', # disable local-version to allow uploads to test.pypi.org - }, - extras_require={ - "dev": ["pytest", "pytest-benchmark", "pytest-cov<3"], - }, - classifiers=[ - "Intended Audience :: Developers", - "License :: OSI Approved :: MIT License", - "Programming Language :: Python :: 2", - "Programming Language :: Python :: 3", - "Topic :: Software Development :: Libraries :: Python Modules", - "Topic :: Software Development", - ], -) diff --git a/tests/__init__.py b/tests/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/bench.py b/tests/bench.py deleted file mode 100644 index 722e67b..0000000 --- a/tests/bench.py +++ /dev/null @@ -1,196 +0,0 @@ -from tempfile import TemporaryFile -import unittest - -import pytest - -import llsd - -BENCH_DATA_XML = b""" - - - integer - 5000 - real - 0.096970 - binary - ff55ca5cef2f477f92e011c7de95bb11 - uri - https://secondlife.com - date - 2006-02-01T14:29:53Z - children - - - integer - 5000 - real - 0.096970 - binary - ff55ca5cef2f477f92e011c7de95bb11 - string - AjWwhTHctcuAxhxK - id - ff55ca5cef2f477f92e011c7de95bb11 - int_32 - -1147906088 - bool - 1 - - 0.156519 - ff55ca5cef2f477f92e011c7de95bb11 - https://secondlife.com - 2006-02-01T14:29:53Z - - -""" - -_bench_data = llsd.parse_xml(BENCH_DATA_XML) - - - -BENCH_DATA_BINARY = llsd.format_binary(_bench_data) -BENCH_DATA_NOTATION = llsd.format_notation(_bench_data) - -_tc = unittest.TestCase() -_tc.maxDiff = 5000 -assertDictEqual = _tc.assertDictEqual - - -@pytest.fixture -def xml_stream(): - with TemporaryFile() as f: - f.write(BENCH_DATA_XML) - f.seek(0) - yield f - - -@pytest.fixture -def notation_stream(): - with TemporaryFile() as f: - d = llsd.format_notation(_bench_data) - f.write(d) - f.seek(0) - yield f - - -@pytest.fixture -def binary_stream(): - with TemporaryFile() as f: - d = llsd.format_binary(_bench_data) - f.write(d) - f.seek(0) - yield f - -def build_deep_xml(): - deep_data = {} - curr_data = deep_data - for i in range(198): - curr_data["curr_data"] = {} - curr_data["integer"] = 7 - curr_data["string"] = "string" - curr_data["map"] = { "item1": 2.345, "item2": [1,2,3], "item3": {"item4": llsd.uri("http://foo.bar.com")}} - curr_data = curr_data["curr_data"] - - return deep_data -_deep_bench_data = build_deep_xml() - -def build_wide_xml(): - - wide_xml = b""" -wide_array" -""" - wide_data = {} - for i in range(100000): - wide_data["item"+str(i)] = {"item1":2.345, "item2": [1,2,3], "item3": "string", "item4":{"subitem": llsd.uri("http://foo.bar.com")}} - return wide_data -_wide_bench_data = build_wide_xml() - -def build_wide_array_xml(): - - wide_xml = b""" -wide_array" -""" - wide_data = [] - for i in range(100000): - wide_data.append([2.345,[1,2,3], "string", [llsd.uri("http://foo.bar.com")]]) - return wide_data -_wide_array_bench_data = build_wide_array_xml() - -def bench_stream(parse, stream): - ret = parse(stream) - stream.seek(0) - return ret - - -def test_parse_xml_stream(benchmark, xml_stream): - ret = benchmark(bench_stream, llsd.parse_xml, xml_stream) - assertDictEqual(ret, _bench_data) - - -def test_parse_notation_stream(benchmark, notation_stream): - ret = benchmark(bench_stream, llsd.parse_notation, notation_stream) - assertDictEqual(ret, _bench_data) - - -def test_parse_binary_stream(benchmark, binary_stream): - ret = benchmark(bench_stream, llsd.parse_binary, binary_stream) - assertDictEqual(ret, _bench_data) - - -def test_parse_notation_bytes(benchmark): - ret = benchmark(llsd.parse_notation, BENCH_DATA_NOTATION) - assertDictEqual(ret, _bench_data) - - -def test_parse_xml_bytes(benchmark): - res = benchmark(llsd.parse_xml, BENCH_DATA_XML) - assertDictEqual(res, _bench_data) - - -def test_parse_binary_bytes(benchmark): - res = benchmark(llsd.parse_binary, BENCH_DATA_BINARY) - assertDictEqual(res, _bench_data) - - -def test_format_xml(benchmark): - benchmark(llsd.format_xml, _bench_data) - - -def test_format_notation(benchmark): - benchmark(llsd.format_notation, _bench_data) - - -def test_format_binary(benchmark): - benchmark(llsd.format_binary, _bench_data) - -def test_format_xml_deep(benchmark): - benchmark(llsd.format_xml, _deep_bench_data) - -def test_format_xml_wide(benchmark): - benchmark(llsd.format_xml, _wide_bench_data) - -def test_format_notation_deep(benchmark): - benchmark(llsd.format_notation, _deep_bench_data) - -def test_format_notation_wide(benchmark): - benchmark(llsd.format_notation, _wide_bench_data) - -def test_format_notation_wide_array(benchmark): - benchmark(llsd.format_notation, _wide_array_bench_data) - -def test_format_binary_deep(benchmark): - benchmark(llsd.format_binary, _deep_bench_data) - -def test_format_binary_wide(benchmark): - benchmark(llsd.format_binary, _wide_bench_data) - -def test_format_binary_wide_array(benchmark): - benchmark(llsd.format_binary, _wide_array_bench_data) - -def test_parse_xml_deep(benchmark): - deep_data = llsd.format_xml(_deep_bench_data) - benchmark(llsd.parse_xml, deep_data) - -def test_parse_binary_deep(benchmark): - deep_data = llsd.format_binary(_deep_bench_data) - benchmark(llsd.parse_binary, deep_data) diff --git a/tests/fixtures/viewer-autobuild.xml b/tests/fixtures/viewer-autobuild.xml deleted file mode 100644 index 2e4397a..0000000 --- a/tests/fixtures/viewer-autobuild.xml +++ /dev/null @@ -1,3977 +0,0 @@ - - - - installables - - SDL - - copyright - Copyright (C) 1997-2012 Sam Lantinga - description - Simple DirectMedia Layer is a cross-platform multimedia library designed to provide low level access to audio, keyboard, mouse, joystick, 3D hardware via OpenGL, and 2D video framebuffer. - license - lgpl - license_file - LICENSES/SDL.txt - name - SDL - platforms - - linux - - archive - - hash - 459cdc8d7c19a8025f98f61db95622ff - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/sdl_3p-update-sdl/rev/297546/arch/Linux/installer/SDL-1.2.15-linux-297546.tar.bz2 - - name - linux - - linux64 - - archive - - hash - 7ea2df03bfc35c06acf23dd9e734adac - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/1103/2554/SDL-1.2.15-linux64-501092.tar.bz2 - - name - linux64 - - - version - 1.2.15 - - apr_suite - - copyright - Copyright Β© 2012 The Apache Software Foundation, Licensed under the Apache License, Version 2.0. - description - Apache portable runtime project - license - apache - license_file - LICENSES/apr_suite.txt - name - apr_suite - platforms - - darwin - - archive - - hash - 0c53148aa00e51c06fa246c4130915be - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/apr_3p-update-apr/rev/297252/arch/Darwin/installer/apr_suite-1.4.5.297252-darwin-297252.tar.bz2 - - name - darwin - - darwin64 - - archive - - hash - b6357ef3a0ec37877a5831820f25094e - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/80557/759704/apr_suite-1.4.5.558565-darwin64-558565.tar.bz2 - - name - darwin64 - - linux - - archive - - hash - 1aa2e5355bb9df09f9196d14a72b6705 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/p64_3p-apr/rev/314241/arch/Linux/installer/apr_suite-1.4.5.314241-linux-314241.tar.bz2 - - name - linux - - linux64 - - archive - - hash - 84a1a140f20b25d714949185e854d14b - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/4811/15302/apr_suite-1.4.5.504800-linux64-504800.tar.bz2 - - name - linux64 - - windows - - archive - - hash - cb48ac069440f6dcd564cfa9fd02a4c2 - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/80556/759710/apr_suite-1.4.5.558565-windows-558565.tar.bz2 - - name - windows - - windows64 - - archive - - hash - 646dc3828d9c39fb1e77c4eec44ed739 - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/80555/759709/apr_suite-1.4.5.558565-windows64-558565.tar.bz2 - - name - windows64 - - - version - 1.4.5.558565 - - boost - - copyright - (see individual source files) - description - Boost C++ Libraries - license - boost 1.0 - license_file - LICENSES/boost.txt - name - boost - platforms - - darwin - - archive - - hash - c296845cad075250c1ae2620f175a957 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/boost_3p-update-boost/rev/297445/arch/Darwin/installer/boost-1.57-darwin-297445.tar.bz2 - - name - darwin - - darwin64 - - archive - - hash - 35cc090d942b85c9126ceac9912d52d6 - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/78585/744021/boost-1.72-darwin64-557045.tar.bz2 - - name - darwin64 - - linux - - archive - - hash - f1fdb548fd6c09a083c86f3a23d7f041 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/p64_3p-boost/rev/317807/arch/Linux/installer/boost-1.57-linux-317807.tar.bz2 - - name - linux - - linux64 - - archive - - hash - 038853b97307a9b65de20c4c50098023 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/9675/45694/boost-1.65.1-linux64-509640.tar.bz2 - - name - linux64 - - windows - - archive - - hash - 9aa4ce32df5f5e36124c990e2d77b885 - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/78586/743982/boost-1.72-windows-557045.tar.bz2 - - name - windows - - windows64 - - archive - - hash - a79511c9d8b956767ebaa405155d4238 - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/78584/743961/boost-1.72-windows64-557045.tar.bz2 - - name - windows64 - - - version - 1.72 - - bugsplat - - copyright - Copyright 2003-2017, BugSplat - description - Bugsplat crash reporting package - license - Proprietary - license_file - LICENSES/BUGSPLAT_LICENSE.txt - name - bugsplat - platforms - - darwin64 - - archive - - hash - ae90d19cdcddf539f6d0b41cab12f918 - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/72773/702861/bugsplat-1.0.7.552580-darwin64-552580.tar.bz2 - - name - darwin64 - - windows - - archive - - hash - f5936eceb6a33ff0f1cc31996a40f29c - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/72774/702905/bugsplat-3.6.0.8.552580-windows-552580.tar.bz2 - - name - windows - - windows64 - - archive - - hash - 9cd940754e53e0670030b3da5ba8f373 - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/72775/702906/bugsplat-3.6.0.8.552580-windows64-552580.tar.bz2 - - name - windows64 - - - version - 3.6.0.8.552580 - - colladadom - - copyright - Copyright 2006 Sony Computer Entertainment Inc. - license - SCEA - license_file - LICENSES/collada.txt - name - colladadom - platforms - - darwin - - archive - - hash - 726bc31e562752f081e95e8fcc70e405 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/colladadom_3p-update-colladadom/rev/297450/arch/Darwin/installer/colladadom-2.3.297450-darwin-297450.tar.bz2 - - name - darwin - - darwin64 - - archive - - hash - 1d063cf1783e7788f17486c234adb1db - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/78635/744249/colladadom-2.3.557064-darwin64-557064.tar.bz2 - - name - darwin64 - - linux - - archive - - hash - 78b9a6506fb7d53da166f7a65f2278f4 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/p64_3p-colladadom/rev/317826/arch/Linux/installer/colladadom-2.3.317826-linux-317826.tar.bz2 - - name - linux - - linux64 - - archive - - hash - c90613240ba3e3a171d3379275ae4ee3 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/9695/45732/colladadom-2.3.509683-linux64-509683.tar.bz2 - - name - linux64 - - windows - - archive - - hash - e78ecf919eee01567556787c3a358d15 - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/78637/744269/colladadom-2.3.557064-windows-557064.tar.bz2 - - name - windows - - windows64 - - archive - - hash - 7e63a212c8909a25236138422fe01298 - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/78636/744273/colladadom-2.3.557064-windows64-557064.tar.bz2 - - name - windows64 - - - version - 2.3.557064 - - curl - - copyright - Copyright (c) 1996 - 2014, Daniel Stenberg, (daniel@haxx.se). - description - Library for transferring data specified with URL syntax - license - curl - license_file - LICENSES/curl.txt - name - curl - platforms - - darwin - - archive - - hash - ad0061db7188a1b9a974eb0512eeeb8d - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-curl/rev/312763/arch/Darwin/installer/curl-7.47.0.312763-darwin-312763.tar.bz2 - - name - darwin - - darwin64 - - archive - - hash - 13f74f43a6363ec998569f731fd869c5 - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/82637/774617/curl-7.54.1.560191-darwin64-560191.tar.bz2 - - name - darwin64 - - linux - - archive - - hash - 9430c08954c00736117099046694e1b1 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/p64_3p-curl/rev/314230/arch/Linux/installer/curl-7.47.0.314230-linux-314230.tar.bz2 - - name - linux - - linux64 - - archive - - hash - 630a2ddf43bba6e5b6e171dc68921dcb - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/8663/36142/curl-7.54.1.508652-linux64-508652.tar.bz2 - - name - linux64 - - windows - - archive - - hash - 0df99bd685dc3561ca8ea347b2921987 - hash_algorithm - md5 - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/82639/774610/curl-7.54.1.560191-windows-560191.tar.bz2 - - name - windows - - windows64 - - archive - - hash - 50db2a9e6b74ec4b0c38b1ea8f135735 - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/82638/774608/curl-7.54.1.560191-windows64-560191.tar.bz2 - - name - windows64 - - - version - 7.54.1.560191 - - db - - copyright - Copyright (c) 1990, 2010 Oracle and/or its affiliates. All rights reserved. - description - Berkeley DB (libdb) is a programmatic toolkit that provides embedded database support for both traditional and client/server applications. - license - bsd - license_file - LICENSES/db.txt - name - db - platforms - - linux - - archive - - hash - 1cc7940e500858a9754e9a3cc3ba2237 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/db_3p-update-db/rev/295315/arch/Linux/installer/db-5.1.25-linux-295315.tar.bz2 - - name - linux - - - version - 5.1.25 - - dbus_glib - - copyright - Copyright (C) Red Hat Inc. - description - D-Bus bindings for glib - license - Academic Free License v. 2.1 - license_file - LICENSES/dbus-glib.txt - name - dbus_glib - platforms - - linux - - archive - - hash - 6d676abd9ad8d2883b855dbe397d9034 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/p64_3p-dbus-glib/rev/314266/arch/Linux/installer/dbus_glib-0.76-linux-314266.tar.bz2 - - name - linux - - linux64 - - archive - - hash - 7ee7b9aed3c0c8c09e7bf26bba7af8e1 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/p64_3p-dbus-glib/rev/314266/arch/Linux/installer/dbus_glib-0.76-linux64-314266.tar.bz2 - - name - linux64 - - - version - 0.76 - - dictionaries - - copyright - Copyright 2014 Apache OpenOffice software - description - Spell checking dictionaries to bundled into the viewer - license - various open source - license_file - LICENSES/dictionaries.txt - name - dictionaries - platforms - - common - - archive - - hash - d778c6a3475bc35ee8b9615dfc38b4a9 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/55025/511964/dictionaries-1.538984-common-538984.tar.bz2 - - name - common - - - version - 1.538984 - - dullahan - - copyright - Copyright (c) 2017, Linden Research, Inc. - description - A headless browser SDK that uses the Chromium Embedded Framework (CEF). It is designed to make it easier to write applications that render modern web content directly to a memory buffer, inject synthesized mouse and keyboard events as well as interact with web based features like JavaScript or cookies. - license - MPL - license_file - LICENSES/LICENSE.txt - name - dullahan - platforms - - darwin64 - - archive - - hash - 45dedb5b09995cd794304150e94fcf21 - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/87950/806969/dullahan-1.12.2.202109170444_91.1.21_g9dd45fe_chromium-91.0.4472.114-darwin64-563968.tar.bz2 - - name - darwin64 - - windows - - archive - - hash - d0fd9d7086699da4bb5ccc935622a717 - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/88276/809277/dullahan-1.12.2.202109230751_91.1.21_g9dd45fe_chromium-91.0.4472.114-windows-563968.tar.bz2 - - name - windows - - windows64 - - archive - - hash - 7e8c3ccd420ff5aef24ff72d609ba394 - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/88275/809281/dullahan-1.12.2.202109230751_91.1.21_g9dd45fe_chromium-91.0.4472.114-windows64-563968.tar.bz2 - - name - windows64 - - - version - 1.12.2.202109230751_91.1.21_g9dd45fe_chromium-91.0.4472.114 - - elfio - - license - lgpl - license_file - LICENSES/elfio.txt - name - elfio - platforms - - linux - - archive - - hash - 031e6315a5c0829c9b9a2ec18aeb7ae3 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-elfio/rev/222074/arch/Linux/installer/elfio-1.0.3-linux-20110225.tar.bz2 - - name - linux - - - - expat - - copyright - Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd and Clark Cooper - Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006 Expat maintainers. - description - Expat is an XML parser library written in C - license - expat - license_file - LICENSES/expat.txt - name - expat - platforms - - darwin - - archive - - hash - 452d1910ef853329cd59858e6c5b2c48 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/expat_3p-update-expat/rev/297014/arch/Darwin/installer/expat-2.0.1.297014-darwin-297014.tar.bz2 - - name - darwin - - darwin64 - - archive - - hash - f4e80e0dfcab713a3da90cd8f7f23e7b - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/76341/727265/expat-2.1.1.555519-darwin64-555519.tar.bz2 - - name - darwin64 - - linux - - archive - - hash - 387c90b9bb5ec412587fbe7a56261dd1 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/p64_3p-expat/rev/314211/arch/Linux/installer/expat-2.1.1.314211-linux-314211.tar.bz2 - - name - linux - - linux64 - - archive - - hash - 5e1f025d1cebd12db542080aa755257f - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/380/943/expat-2.1.1.500375-linux64-500375.tar.bz2 - - name - linux64 - - windows - - archive - - hash - cd4fe03473076c324d80ae3bd91a85bb - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/76343/727273/expat-2.1.1.555519-windows-555519.tar.bz2 - - name - windows - - windows64 - - archive - - hash - d2d74d73b914150982b1883a3b96e60b - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/76344/727279/expat-2.1.1.555519-windows64-555519.tar.bz2 - - name - windows64 - - - version - 2.1.1.555519 - - fmodstudio - - copyright - FMOD Studio by Firelight Technologies Pty Ltd. - description - FMOD Studio API - license - fmod - license_file - LICENSES/fmodstudio.txt - name - fmodstudio - platforms - - darwin64 - - archive - - hash - d5528538e67c710387ae0c061a90cb23 - url - https://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/76868/730756/fmodstudio-2.01.07.555883-darwin64-555883.tar.bz2 - - name - darwin64 - - linux - - archive - - hash - 5283050c22d31877cd9e0afbe6feb9fc - url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/65398/612630/fmodstudio-2.00.11.546392-linux-546392.tar.bz2 - - name - linux - - linux64 - - archive - - hash - 5a3c78f4a77ae6477986e33836725e8b - url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/65399/612631/fmodstudio-2.00.11.546392-linux64-546392.tar.bz2 - - name - linux64 - - windows - - archive - - hash - a2bb6eaf51f933993b26a5fe7503a761 - url - https://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/76869/730763/fmodstudio-2.01.07.555883-windows-555883.tar.bz2 - - name - windows - - windows64 - - archive - - hash - 138d07dd516a9ad5b9787192fe6134dd - url - https://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/76867/730751/fmodstudio-2.01.07.555883-windows64-555883.tar.bz2 - - name - windows64 - - - version - 2.01.07.555883 - - fontconfig - - copyright - Copyright (C) 2000,2001,2002,2003,2004,2006,2007 Keith Packard, 2005 Patrick Lam, 2009 Roozbeh Pournader, 2008,2009 Red Hat, Inc., 2008 Danilo Ε egan, 2012 Google, Inc. - description - Fontconfig is a library for configuring and customizing font access. - license - bsd - license_file - LICENSES/fontconfig.txt - name - fontconfig - platforms - - linux - - archive - - hash - a20a3d0ab7fc3401bc2ca81e9309f630 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/p64_3p-fontconfig/rev/314281/arch/Linux/installer/fontconfig-2.11.0-linux-314281.tar.bz2 - - name - linux - - linux64 - - archive - - hash - e2419d56960c160670051fbb055fb729 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/p64_3p-fontconfig/rev/314281/arch/Linux/installer/fontconfig-2.11.0-linux64-314281.tar.bz2 - - name - linux64 - - - version - 2.11.0 - - freetype - - copyright - Copyright 2006, 2007, 2008, 2009, 2010 by David Turner, Robert Wilhelm, and Werner Lemberg. - description - Font rendering library - license - FreeType - license_file - LICENSES/freetype.txt - name - freetype - platforms - - darwin - - archive - - hash - 83618d16d974eb0af93926a10ac13297 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/freetype_3p-update-freetype/rev/297053/arch/Darwin/installer/freetype-2.4.4.297053-darwin-297053.tar.bz2 - - name - darwin - - darwin64 - - archive - - hash - 3a478d6c8a10d49d9161ef864394b03c - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/78592/744013/freetype-2.4.4.557047-darwin64-557047.tar.bz2 - - name - darwin64 - - linux - - archive - - hash - 1b401394106cedc86926bd488f5aa45e - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/p64_3p-freetype/rev/314215/arch/Linux/installer/freetype-2.4.4.314215-linux-314215.tar.bz2 - - name - linux - - linux64 - - archive - - hash - 94cf61dfdbc86aae5bbaf0b5cb8a366c - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/874/1914/freetype-2.4.4.500865-linux64-500865.tar.bz2 - - name - linux64 - - windows - - archive - - hash - 7ee200d6b5fa282c7f973ade5615aa86 - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/78594/744011/freetype-2.4.4.557047-windows-557047.tar.bz2 - - name - windows - - windows64 - - archive - - hash - 69307aaba16ac71531c9c4d930ace993 - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/78593/744010/freetype-2.4.4.557047-windows64-557047.tar.bz2 - - name - windows64 - - - version - 2.4.4.557047 - - glext - - copyright - Copyright (c) 2007-2010 The Khronos Group Inc. - description - glext headers define function prototypes and constants for OpenGL extensions - license - Copyright (c) 2007-2010 The Khronos Group Inc. - license_file - LICENSES/glext.txt - name - glext - platforms - - darwin64 - - archive - - hash - 1bd3214ac23474ea4c869e386970a1be - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54835/510029/glext-68-darwin64-538965.tar.bz2 - - name - darwin64 - - linux - - archive - - hash - baf1fd13e1fe6aef586200fc87a70f53 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/p64_3p-glext/rev/314200/arch/Linux/installer/glext-68-linux-314200.tar.bz2 - - name - linux - - linux64 - - archive - - hash - 5f3c9d61b620f949b199ebd8885218ed - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/p64_3p-glext/rev/314200/arch/Linux/installer/glext-68-linux64-314200.tar.bz2 - - name - linux64 - - windows - - archive - - hash - 6a311615bce59b01cf73ee65012a9b38 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54951/511711/glext-68-windows-538965.tar.bz2 - - name - windows - - windows64 - - archive - - hash - daf619dab1cf7518af6532b18800c4b0 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54924/511490/glext-68-windows64-538965.tar.bz2 - - name - windows64 - - - version - 68 - - glh_linear - - copyright - Copyright (c) 2000 Cass Everitt - description - glh - is a platform-indepenedent C++ OpenGL helper library - license - BSD - license_file - LICENSES/glh-linear.txt - name - glh_linear - platforms - - common - - archive - - hash - 650e836255b6c2ecb93d3f1f7220051c - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/55011/511905/glh_linear-0.0.0-common-538981.tar.bz2 - - name - common - - - version - 0.0.0 - - glod - - copyright - Copyright 2003 Jonathan Cohen, Nat Duca, David Luebke, Brenden Schubert - Johns Hopkins University and University of Virginia - license - GLOD Open-Source License Version 1.0 - license_file - LICENSES/GLOD.txt - name - glod - platforms - - darwin - - archive - - hash - 71e678d70e276fc42a56926fc28a7abd - hash_algorithm - md5 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/glod_3p-update-glod/rev/296895/arch/Darwin/installer/glod-1.0pre4.296895-darwin-296895.tar.bz2 - - name - darwin - - darwin64 - - archive - - hash - a9eaa005ff9d387f946283fbcb69b3c8 - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/76353/727324/glod-1.0pre3.555522-darwin64-555522.tar.bz2 - - name - darwin64 - - linux - - archive - - hash - 58113bcbbacbaeb2d278f745867ae6f0 - hash_algorithm - md5 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/p64_3p-glod/rev/314201/arch/Linux/installer/glod-1.0pre4.314201-linux-314201.tar.bz2 - - name - linux - - linux64 - - archive - - hash - 9aef5cd576ace19568da01d9bc3db29c - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/1625/3628/glod-1.0pre3.501614-linux64-501614.tar.bz2 - - name - linux64 - - windows - - archive - - hash - e36c95b0d0fbaa3ff3392facaf5de447 - hash_algorithm - md5 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/55008/511893/glod-1.0pre3.538980-windows-538980.tar.bz2 - - name - windows - - windows64 - - archive - - hash - 6302ee1903ab419e76565d9eb6acd274 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/55004/511885/glod-1.0pre3.538980-windows64-538980.tar.bz2 - - name - windows64 - - - version - 1.0pre3.555522 - - googlemock - - copyright - Copyright 2008, Google Inc. - description - a library for writing and using C++ mock classes - license - BSD - license_file - LICENSES/gmock.txt - name - googlemock - platforms - - darwin - - archive - - hash - 022649e284163b8ee23e3c9a81302fa7 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/googlemock_3p-update-googlemock/rev/297460/arch/Darwin/installer/googlemock-1.7.0.297460-darwin-297460.tar.bz2 - - name - darwin - - darwin64 - - archive - - hash - 19e925604bc1a91efb4b130e1edd8bf2 - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/78620/744140/googlemock-1.7.0.557057-darwin64-557057.tar.bz2 - - name - darwin64 - - linux - - archive - - hash - ad51f68702f25ba245fff312c50c8876 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/p64_3p-googlemock/rev/317828/arch/Linux/installer/googlemock-1.7.0.317828-linux-317828.tar.bz2 - - name - linux - - linux64 - - archive - - hash - ff459b58695c76838782847a0b792104 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/9697/45717/googlemock-1.7.0.509686-linux64-509686.tar.bz2 - - name - linux64 - - windows - - archive - - hash - eed7b41d0d1f41b24f315349ef78c728 - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/78622/744148/googlemock-1.7.0.557057-windows-557057.tar.bz2 - - name - windows - - windows64 - - archive - - hash - a6ad6fe722d2fe4e8137495af3f374c9 - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/78621/744152/googlemock-1.7.0.557057-windows64-557057.tar.bz2 - - name - windows64 - - - version - 1.7.0.557057 - - gstreamer - - copyright - Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> - license - LGPL - license_file - LICENSES/gstreamer.txt - name - gstreamer - platforms - - linux - - archive - - hash - 5017b3e95d2c6f47bb111c3f9c075522 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/p64_3p-gstreamer/rev/314267/arch/Linux/installer/gstreamer-0.10.6.314267-linux-314267.tar.bz2 - - name - linux - - linux64 - - archive - - hash - 7c9d7cc88add7831a6afeedc20cad2fe - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/p64_3p-gstreamer/rev/314267/arch/Linux/installer/gstreamer-0.10.6.314267-linux64-314267.tar.bz2 - - name - linux64 - - - version - 0.10.6.314267 - - gtk-atk-pango-glib - - copyright - Copyright (various, see sources) - license - lgpl - license_file - LICENSES/gtk-atk-pango-glib.txt - name - gtk-atk-pango-glib - platforms - - linux - - archive - - hash - a6431df705526501684d9050e04bfa5b - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/p64_3p-gtk-atk-pango-glib/rev/314220/arch/Linux/installer/gtk_atk_pango_glib-0.1-linux-314220.tar.bz2 - - name - linux - - linux64 - - archive - - hash - de7bba8fd2275a11b077b124413065d0 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/p64_3p-gtk-atk-pango-glib/rev/314220/arch/Linux/installer/gtk_atk_pango_glib-0.1-linux64-314220.tar.bz2 - - name - linux64 - - - version - 0.1 - - havok-source - - copyright - Uses Havok (TM) Physics. (c)Copyright 1999-2010 Havok.com Inc. (and its Licensors). All Rights Reserved. See www.havok.com for details. - description - Havok source code for libs and demos - license - havok - license_file - LICENSES/havok.txt - name - havok-source - platforms - - darwin - - archive - - hash - 5c5b4820999ae9e398801d6a46f45897 - url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/havok-source_3p-update-havok-source/rev/297312/arch/Darwin/installer/havok_source-2012.1-darwin-297312.tar.bz2 - - name - darwin - - darwin64 - - archive - - hash - ba229348c1d9d58519cd854ff9d8ef3d - url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/55213/512968/havok_source-2012.1-2-darwin64-539117.tar.bz2 - - name - darwin64 - - linux - - archive - - hash - 03c1c5f7c3e93e905f635ca22b607494 - url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/p64_3p-havok-source/rev/314226/arch/Linux/installer/havok_source-2012.1-2-linux-314226.tar.bz2 - - name - linux - - linux64 - - archive - - hash - 00d0333936a67059a43a6ec8ac38d564 - url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/748/1563/havok_source-2012.1-2-linux64-500739.tar.bz2 - - name - linux64 - - windows - - archive - - hash - 4ff2af85106907acb171bb1e38a3757e - url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/55214/512993/havok_source-2012.1-2-windows-539117.tar.bz2 - - name - windows - - windows64 - - archive - - hash - bcaf4631ea10f7d09eecb73e8f5bef6c - url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/55212/512962/havok_source-2012.1-2-windows64-539117.tar.bz2 - - name - windows64 - - - version - 2012.1-2 - - jpeglib - - copyright - Copyright (C) 1991-2011, Thomas G. Lane, Guido Vollbeding. - description - JPEG encoding, decoding library - license - jpeglib - license_file - LICENSES/jpeglib.txt - name - jpeglib - platforms - - darwin - - archive - - hash - 4d7658997fd0f93a9c55e40e40b1b0e5 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/jpeglib_3p-update-jpeglib/rev/296854/arch/Darwin/installer/jpeglib-8c.296854-darwin-296854.tar.bz2 - - name - darwin - - darwin64 - - archive - - hash - 3f2e34e3a2dac8eea957cad143a71dc5 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54847/510113/jpeglib-8c.538977-darwin64-538977.tar.bz2 - - name - darwin64 - - linux - - archive - - hash - 32560d3200da72fea2922371fcef25f5 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/p64_3p-jpeglib/rev/314202/arch/Linux/installer/jpeglib-8c.314202-linux-314202.tar.bz2 - - name - linux - - linux64 - - archive - - hash - ba9c62863ec338a049de83c24639f57c - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/3151/7568/jpeglib-8c.503140-linux64-503140.tar.bz2 - - name - linux64 - - windows - - archive - - hash - c8dee00ef13af40ec68becc25830e195 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54992/511854/jpeglib-8c.538977-windows-538977.tar.bz2 - - name - windows - - windows64 - - archive - - hash - 6f40620e86f3c9b91b6b5fe3c81776fc - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54991/511847/jpeglib-8c.538977-windows64-538977.tar.bz2 - - name - windows64 - - - version - 8c.538977 - - jsoncpp - - copyright - Copyright (c) 2007-2010 Baptiste Lepilleur - description - jsoncpp is an implementation of a JSON (http://json.org) reader and writer in C++. - license - public domain - license_file - LICENSES/jsoncpp.txt - name - jsoncpp - platforms - - darwin - - archive - - hash - b25a4f480e07c670ffef00c3da578f87 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/jsoncpp_3p-update-jsoncpp/rev/297281/arch/Darwin/installer/jsoncpp-0.5.0.297281-darwin-297281.tar.bz2 - - name - darwin - - darwin64 - - archive - - hash - 87d32aaac4183590c96edd0b6d9bf3e4 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54846/510106/jsoncpp-0.5.0.538976-darwin64-538976.tar.bz2 - - name - darwin64 - - linux - - archive - - hash - 9d5d9fec28cbbb1651b95728173f8af7 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/p64_3p-jsoncpp/rev/314229/arch/Linux/installer/jsoncpp-0.5.0.314229-linux-314229.tar.bz2 - - name - linux - - linux64 - - archive - - hash - 9a658ae561c75e60bd9c0cee56731d21 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/1475/3274/jsoncpp-0.5.0.501464-linux64-501464.tar.bz2 - - name - linux64 - - windows - - archive - - hash - b73d9addab278eacc100bd312ab6ec5c - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54990/511840/jsoncpp-0.5.0.538976-windows-538976.tar.bz2 - - name - windows - - windows64 - - archive - - hash - 1b9ac5708cc526d2c5358ef0a427109d - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54989/511833/jsoncpp-0.5.0.538976-windows64-538976.tar.bz2 - - name - windows64 - - - version - 0.5.0.538976 - - kdu - - copyright - Kakadu software - description - JPEG2000 library by Kakadu - license - Kakadu - license_file - LICENSES/kdu.txt - name - kdu - platforms - - darwin - - archive - - hash - 3855bd40f950e3c22739ae8f3ee2afc9 - url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/15258/98444/kdu-7.10.4.513518-darwin-513518.tar.bz2 - - name - darwin - - darwin64 - - archive - - hash - ccfd8eacd1ebe92715944094064ba2e4 - url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/55187/512570/kdu-7.10.4.539108-darwin64-539108.tar.bz2 - - name - darwin64 - - linux - - archive - - hash - 43d7a6a69a54534a736f132e9c81795b - url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/15255/98451/kdu-7.10.4.513518-linux-513518.tar.bz2 - - name - linux - - linux64 - - archive - - hash - a705a665810a71e7b0114a97ae9a2224 - url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/15256/98457/kdu-7.10.4.513518-linux64-513518.tar.bz2 - - name - linux64 - - windows - - archive - - hash - 38574fbcb6c94c42745ef48748002e58 - url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/55189/512583/kdu-7.10.4.539108-windows-539108.tar.bz2 - - name - windows - - windows64 - - archive - - hash - 3dfeb869c781a766874f0aedc7d4fcef - url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/55188/512576/kdu-7.10.4.539108-windows64-539108.tar.bz2 - - name - windows64 - - - version - 7.10.4.539108 - - libhunspell - - copyright - See hunspell.txt - description - Spell checking library - license - LGPL - license_file - LICENSES/hunspell.txt - name - libhunspell - platforms - - darwin - - archive - - hash - 05eda16106df26a211f8bdd874d1fca5 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/hunspell_3p-update-hunspell/rev/296916/arch/Darwin/installer/libhunspell-1.3.2.296916-darwin-296916.tar.bz2 - - name - darwin - - darwin64 - - archive - - hash - 2021ea3a19b81c82993e733709683303 - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/76371/727419/libhunspell-1.3.2.555528-darwin64-555528.tar.bz2 - - name - darwin64 - - linux - - archive - - hash - 0d8009c3b6c1eb510593476dd1d821b5 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/p64_3p-hunspell/rev/314217/arch/Linux/installer/libhunspell-1.3.2.314217-linux-314217.tar.bz2 - - name - linux - - linux64 - - archive - - hash - ffbdd109356d66ddfefd8a5d57f63f1f - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/533/1144/libhunspell-1.3.2.500526-linux64-500526.tar.bz2 - - name - linux64 - - windows - - archive - - hash - 2253ec09136cc7c208481030d78d9dd7 - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/76369/727412/libhunspell-1.3.2.555528-windows-555528.tar.bz2 - - name - windows - - windows64 - - archive - - hash - 858d1708f6b3a74738a3d57a5387e20f - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/76370/727413/libhunspell-1.3.2.555528-windows64-555528.tar.bz2 - - name - windows64 - - - version - 1.3.2.555528 - - libndofdev - - copyright - Copyright (c) 2007, 3Dconnexion, Inc. - All rights reserved. - description - 3DConnexion SDK - license - BSD - license_file - LICENSES/libndofdev.txt - name - libndofdev - platforms - - darwin - - archive - - hash - a01b411433dbf8a4b481de9e76d9a652 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/libndofdev_3p-update-libndofdev/rev/297264/arch/Darwin/installer/libndofdev-0.1.297264-darwin-297264.tar.bz2 - - name - darwin - - darwin64 - - archive - - hash - a487fff84208a45844602c4a1f68c974 - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/76356/727333/libndofdev-0.1.555523-darwin64-555523.tar.bz2 - - name - darwin64 - - windows - - archive - - hash - 4c839555bf0ed9ae60ffc3f8a7c96f9b - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/76354/727340/libndofdev-0.1.555523-windows-555523.tar.bz2 - - name - windows - - windows64 - - archive - - hash - cbc033ae3b034b992b59f6de1034247c - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/76355/727341/libndofdev-0.1.555523-windows64-555523.tar.bz2 - - name - windows64 - - - version - 0.1.555523 - - libpng - - copyright - Copyright (c) 2004, 2006-2013 Glenn Randers-Pehrson - description - PNG Reference library - license - libpng - license_file - LICENSES/libpng.txt - name - libpng - platforms - - darwin - - archive - - hash - 14cb5c8686a472e9e60179e46cd196f7 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/libpng_3p-update-libpng/rev/297708/arch/Darwin/installer/libpng-1.6.8.297708-darwin-297708.tar.bz2 - - name - darwin - - darwin64 - - archive - - hash - 2a41acc3116ce19a443873216cb882ad - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/78587/743948/libpng-1.6.8.557046-darwin64-557046.tar.bz2 - - name - darwin64 - - linux - - archive - - hash - 0758f3cb4c02ebab61854b811b0894e9 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/p64_3p-libpng/rev/314214/arch/Linux/installer/libpng-1.6.8.314214-linux-314214.tar.bz2 - - name - linux - - linux64 - - archive - - hash - 13de93ea11544051b69f238eeb644fd3 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/882/1946/libpng-1.6.8.500873-linux64-500873.tar.bz2 - - name - linux64 - - windows - - archive - - hash - b935b440947f63c69700bdcf5095a8e1 - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/78591/743970/libpng-1.6.8.557046-windows-557046.tar.bz2 - - name - windows - - windows64 - - archive - - hash - d1cc8354ac4e877eefedf16b1be3aac6 - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/78589/743991/libpng-1.6.8.557046-windows64-557046.tar.bz2 - - name - windows64 - - - version - 1.6.8.557046 - - libuuid - - copyright - Copyright (c) 2004-2008 The OSSP Project <http://www.ossp.org/> - description - OSSP uuid is a ISO-C:1999 application programming interface (API) and corresponding command line interface (CLI) for the generation of DCE 1.1, ISO/IEC 11578:1996 and RFC 4122 compliant Universally Unique Identifier (UUID). - license - UUID - license_file - LICENSES/uuid.txt - name - libuuid - platforms - - linux - - archive - - hash - a2eaf9515cd129f3e21a08e92689006b - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/p64_3p-libuuid/rev/314269/arch/Linux/installer/libuuid-1.6.2-linux-314269.tar.bz2 - - name - linux - - linux64 - - archive - - hash - fb89f1281dd54d8b99b339fc5b712b27 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/p64_3p-libuuid/rev/314269/arch/Linux/installer/libuuid-1.6.2-linux64-314269.tar.bz2 - - name - linux64 - - - version - 1.6.2 - - libxml2 - - copyright - Copyright (C) 1998-2012 Daniel Veillard. All Rights Reserved. - description - Libxml2 is the XML C parser and toolkit developed for the Gnome project. - license - mit - license_file - LICENSES/libxml2.txt - name - libxml2 - platforms - - darwin - - archive - - hash - 9303f0dd174129e297eca6cc2eb1ab3f - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/libxml_3p-update-libxml/rev/297050/arch/Darwin/installer/libxml2-2.9.1.297050-darwin-297050.tar.bz2 - - name - darwin - - darwin64 - - archive - - hash - 6677173bbbb0ea32369b5e9b6c9aa641 - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/78631/744225/libxml2-2.9.4.557062-darwin64-557062.tar.bz2 - - name - darwin64 - - linux - - archive - - hash - 6954173a141d928f2614076577d952de - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/p64_3p-libxml/rev/314197/arch/Linux/installer/libxml2-2.9.1.314197-linux-314197.tar.bz2 - - name - linux - - linux64 - - archive - - hash - 740fc93f195c77b3a0c0800b31878ecb - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/890/1968/libxml2-2.9.4.500877-linux64-500877.tar.bz2 - - name - linux64 - - windows - - archive - - hash - ad6a596fbf0e83a21d95762da78437bc - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/78633/744239/libxml2-2.9.4.557062-windows-557062.tar.bz2 - - name - windows - - windows64 - - archive - - hash - 6b5bb230684ecf28386d7c91c47bb6e1 - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/78634/744240/libxml2-2.9.4.557062-windows64-557062.tar.bz2 - - name - windows64 - - - version - 2.9.4.557062 - - llappearance_utility - - copyright - Copyright (c) 2000-2012, Linden Research, Inc. - description - Linden Lab appearance utility for server-side avatar baking services. - license - Proprietary - license_file - LICENSES/llappearanceutility.txt - name - llappearance_utility - platforms - - linux - - archive - - hash - fddd634dec5ec03924d62cc774f7f8ea - url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/p64_viewer-llappearance-utility/rev/317266/arch/Linux/installer/llappearance_utility-0.0.1-linux-317266.tar.bz2 - - name - linux - - - version - 0.0.1 - - llca - - copyright - Copyright (c) 2016, Linden Research, Inc.; data provided by the Mozilla NSS Project. - - license - mit - license_file - LICENSES/ca-license.txt - name - llca - platforms - - common - - archive - - hash - 0a6349b11c8e9d34f0c80b8081736e75 - hash_algorithm - md5 - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/79438/751815/llca-202104010215.557744-common-557744.tar.bz2 - - name - common - - - version - 202104010215.557744 - - llphysicsextensions_source - - copyright - Copyright (c) 2010, Linden Research, Inc. - license - internal - license_file - LICENSES/llphysicsextensions.txt - name - llphysicsextensions_source - platforms - - darwin64 - - archive - - hash - 14fac452271ebfba37ba5ddcf5bffa54 - url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/54842/510078/llphysicsextensions_source-1.0.538972-darwin64-538972.tar.bz2 - - name - darwin64 - - linux64 - - archive - - hash - c1b43e99c5ddccc18b0e9cb288bf75e1 - url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/4721/14828/llphysicsextensions_source-1.0.504710-linux64-504710.tar.bz2 - - name - linux64 - - windows - - archive - - hash - f3c066c1aebed8a6519a3e5ce64b9a3c - url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/54982/511796/llphysicsextensions_source-1.0.538972-windows-538972.tar.bz2 - - name - windows - - - version - 1.0.538972 - - llphysicsextensions_stub - - copyright - Copyright (c) 2010, Linden Research, Inc. - license - internal - license_file - LICENSES/llphysicsextensions.txt - name - llphysicsextensions_stub - platforms - - darwin64 - - archive - - hash - f290b000b31f9e36f2489946cbc99f5e - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/59995/563653/llphysicsextensions_stub-1.0.542456-darwin64-542456.tar.bz2 - - name - darwin64 - - linux64 - - archive - - hash - 711f4ec769e4b5f59ba25ee43c11bcbc - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/4724/14846/llphysicsextensions_stub-1.0.504712-linux64-504712.tar.bz2 - - name - linux64 - - windows - - archive - - hash - 2e5f1f7046a49d8b0bc295aa878116bc - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/60043/564063/llphysicsextensions_stub-1.0.542456-windows-542456.tar.bz2 - - name - windows - - - version - 1.0.542456 - - llphysicsextensions_tpv - - copyright - Copyright (c) 2010, Linden Research, Inc. - license - internal - license_file - LICENSES/HavokSublicense.pdf - name - llphysicsextensions_tpv - platforms - - darwin64 - - archive - - hash - 2aa4ec0d72bbe4b755730f1bf92b39e7 - url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/30340/257304/llphysicsextensions_tpv-1.0.542327-darwin64-542327.tar.bz2 - - name - darwin64 - - linux64 - - archive - - hash - 711f4ec769e4b5f59ba25ee43c11bcbc - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/4724/14846/llphysicsextensions_stub-1.0.504712-linux64-504712.tar.bz2 - - name - linux64 - - windows - - archive - - hash - ad9aba5e2c43a37b6530a0d2de64df1c - url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/30341/257307/llphysicsextensions_tpv-1.0.542327-windows-542327.tar.bz2 - - name - windows - - windows64 - - archive - - hash - 46689ff1442a8eccac3a7f3258308e1e - url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/30341/257307/llphysicsextensions_tpv-1.0.542327-windows64-542327.tar.bz2 - - name - windows - - - version - 1.0.542327 - - mesa - - license - mesa - license_file - LICENSES/mesa.txt - name - mesa - platforms - - linux - - archive - - hash - 22c50a5d362cad311b4f413cfcffbba2 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/mesa_3p-update-mesa/rev/297294/arch/Linux/installer/mesa-7.11.1.297294-linux-297294.tar.bz2 - - name - linux - - - version - 7.11.1.297294 - - nghttp2 - - copyright - Copyright (c) 2012, 2014, 2015, 2016 Tatsuhiro Tsujikawa -Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors - description - Library providing HTTP 2 support for libcurl - license - MIT - license_file - LICENSES/nghttp2.txt - name - nghttp2 - platforms - - darwin64 - - archive - - hash - e4f784d8a035c51921a1562ca7a1bab6 - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/76357/727350/nghttp2-1.40.0.555524-darwin64-555524.tar.bz2 - - name - darwin64 - - linux - - archive - - hash - 079c1a1bdb3ce1cda8ce3d7f75eeced3 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/9258/41585/nghttp2-1.25.0.509246-linux-509246.tar.bz2 - - name - linux - - linux64 - - archive - - hash - c3c5ff7d2f7ac1143ef8d888192d4a53 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/9257/41579/nghttp2-1.25.0.509246-linux64-509246.tar.bz2 - - name - linux64 - - windows - - archive - - hash - af05aa2994c9845308fecd094b7b2d25 - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/76359/727360/nghttp2-1.40.0.555524-windows-555524.tar.bz2 - - name - windows - - windows64 - - archive - - hash - 5a55cede40eef16b9d1e47c418a2b77a - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/76358/727359/nghttp2-1.40.0.555524-windows64-555524.tar.bz2 - - name - windows64 - - - source_type - hg - version - 1.40.0.555524 - - nvapi - - copyright - Copyright Β© 2012 NVIDIA Corporation. All rights reserved. - description - NVAPI provides an interface to NVIDIA devices. - license - NVIDIA Corporation Software License Agreement – NVAPI SDK - license_file - LICENSES/NVAPI_SDK_License_Agreement.pdf - name - nvapi - platforms - - windows - - archive - - hash - 4305515ad326c911a390388366a9107b - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54947/511704/nvapi-352.539058-windows-539058.tar.bz2 - - name - windows - - windows64 - - archive - - hash - 25c8ac919f24b8952653d38ec43640e5 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54945/511697/nvapi-352.539058-windows64-539058.tar.bz2 - - name - windows64 - - - version - 352.539058 - - ogg_vorbis - - copyright - Copyright (c) 2002, Xiph.org Foundation - description - Audio encoding library - license - ogg-vorbis - license_file - LICENSES/ogg-vorbis.txt - name - ogg_vorbis - platforms - - darwin - - archive - - hash - 07fca1531a27915f642a5c1d95008d54 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/oggvorbis_3p-update-oggvorbis/rev/296878/arch/Darwin/installer/ogg_vorbis-1.2.2-1.3.2.296878-darwin-296878.tar.bz2 - - name - darwin - - darwin64 - - archive - - hash - a066f1d12caee1d87fc72f48169f9677 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54841/510071/ogg_vorbis-1.3.3-1.3.6.538971-darwin64-538971.tar.bz2 - - name - darwin64 - - linux - - archive - - hash - 5c9d94dce4551b19790057766ff939ea - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/p64_3p-oggvorbis/rev/314224/arch/Linux/installer/ogg_vorbis-1.2.2-1.3.2.314224-linux-314224.tar.bz2 - - name - linux - - linux64 - - archive - - hash - 45ebd074053dc9cae8c5c74b52085d4b - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/465/990/ogg_vorbis-1.2.2-1.3.2.500397-linux64-500397.tar.bz2 - - name - linux64 - - windows - - archive - - hash - d4b8ed3fd679a2b484d2d1a66c063908 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54981/511789/ogg_vorbis-1.3.3-1.3.6.538971-windows-538971.tar.bz2 - - name - windows - - windows64 - - archive - - hash - ec4a657fe639bb458ee5132062146a7a - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54980/511782/ogg_vorbis-1.3.3-1.3.6.538971-windows64-538971.tar.bz2 - - name - windows64 - - - version - 1.3.3-1.3.6.538971 - - open-libndofdev - - copyright - Copyright (c) 2008, Jan Ciger (jan.ciger (at) gmail.com) - description - Open Source replacement for 3DConnection SDK - license - BSD - license_file - LICENSES/libndofdev.txt - name - open-libndofdev - platforms - - linux - - archive - - hash - b1245d467d5914a266efa16afeb55406 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/libndofdev_3p-update-libndofdev/rev/297553/arch/Linux/installer/open_libndofdev-0.3-linux-297553.tar.bz2 - - name - linux - - - version - 0.3 - - openal - - copyright - Creative Labs - description - OpenAL is a cross-platform 3D audio API appropriate for use with gaming applications and many other types of audio applications. - license - lgpl - license_file - LICENSES/openal.txt - name - openal - platforms - - linux - - archive - - hash - 24b91eda3831a51c7774644016c4cb09 - hash_algorithm - md5 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/p64_3p-openal/rev/314223/arch/Linux/installer/openal-1.12.854-1.1.0.314223-linux-314223.tar.bz2 - - name - linux - - linux64 - - archive - - hash - 7530fab3979312da75a903d87b73e3a9 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/p64_3p-openal/rev/314223/arch/Linux/installer/openal-1.12.854-1.1.0.314223-linux64-314223.tar.bz2 - - name - linux64 - - windows - - archive - - hash - d9c86f79a6bb56a670e2801c33fd2dd1 - hash_algorithm - md5 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/p64_3p-openal/rev/314223/arch/CYGWIN/installer/openal-1.12.854-1.1.0.314223-windows-314223.tar.bz2 - - name - windows - - windows64 - - archive - - hash - e0fdd9394a8cd8c6360b922f6f237e57 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/p64_3p-openal/rev/314223/arch/CYGWIN/installer/openal-1.12.854-1.1.0.314223-windows64-314223.tar.bz2 - - name - windows64 - - - version - 1.12.854-1.1.0.314223 - - openjpeg - - copyright - Copyright (c) 2002-2007, Communications and Remote Sensing Laboratory, Universite catholique de Louvain (UCL), Belgium; Copyright (c) 2002-2007, Professor Benoit Macq; Copyright (c) 2001-2003, David Janssens; Copyright (c) 2002-2003, Yannick Verschueren; Copyright (c) 2003-2007, Francois-Olivier Devaux and Antonin Descampe; Copyright (c) 2005, Herve Drolon, FreeImage Team; Copyright (c) 2006-2007, Parvatha Elangovan; Copyright (c) 2008, Jerome Fimes, Communications & Systemes <jerome.fimes@c-s.fr>; Copyright (c) 2010-2011, Kaori Hagihara; Copyright (c) 2011-2012, Centre National d'Etudes Spatiales (CNES), France; Copyright (c) 2012, CS Systemes d'Information, France; - description - The OpenJPEG library is an open-source JPEG 2000 codec written in C language. - license - BSD - license_file - LICENSES/openjpeg.txt - name - openjpeg - platforms - - darwin - - archive - - hash - 2adb5b8bd2493d576c5d02b992d8f819 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/openjpeg_3p-update-openjpeg/rev/297018/arch/Darwin/installer/openjpeg-1.4.297018-darwin-297018.tar.bz2 - - name - darwin - - darwin64 - - archive - - hash - 5abf2d9c0b250821c59cc60cd94fd8af - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54840/510064/openjpeg-1.5.1.538970-darwin64-538970.tar.bz2 - - name - darwin64 - - linux - - archive - - hash - e82317482647559d46a818ba48e9423a - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/p64_3p-openjpeg/rev/314205/arch/Linux/installer/openjpeg-2.0.0.314205-linux-314205.tar.bz2 - - name - linux - - linux64 - - archive - - hash - ac66f3197010b1549a5e4467aebbc27d - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/1113/2571/openjpeg-1.5.1.501102-linux64-501102.tar.bz2 - - name - linux64 - - windows - - archive - - hash - 222a406ecb4071a9cc9635353afa337e - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54977/511775/openjpeg-1.5.1.538970-windows-538970.tar.bz2 - - name - windows - - windows64 - - archive - - hash - 5b5c80807fa8161f3480be3d89fe9516 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54974/511767/openjpeg-1.5.1.538970-windows64-538970.tar.bz2 - - name - windows64 - - - version - 1.5.1.538970 - - openssl - - copyright - Copyright (c) 1998-2011 The OpenSSL Project. All rights reserved; Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) - description - Secure Sockets Layer (SSL v2/v3) and Transport Layer Security (TLS v1) Library - license - openssl - license_file - LICENSES/openssl.txt - name - openssl - platforms - - darwin - - archive - - hash - 0a77d56769e6075957f614be6575423e - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/openssl_3p-update-openssl/rev/297168/arch/Darwin/installer/openssl-1.0.1h.297168-darwin-297168.tar.bz2 - - name - darwin - - darwin64 - - archive - - hash - 5503e4928bcdb0a29685b3242c4a409b - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/82619/774464/openssl-1.1.1l.560177-darwin64-560177.tar.bz2 - - name - darwin64 - - linux - - archive - - hash - f46a601d60b7dbcfde32afc0cb64453e - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/p64_3p-openssl/rev/314227/arch/Linux/installer/openssl-1.0.1h.314227-linux-314227.tar.bz2 - - name - linux - - linux64 - - archive - - hash - d50ccfbf0c1d249392919e2c46ad8d5c - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/8339/33495/openssl-1.0.2l.508328-linux64-508328.tar.bz2 - - name - linux64 - - windows - - archive - - hash - d2153f20dc2d35c609b876a9f019a748 - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/82623/774521/openssl-1.1.1l.560177-windows-560177.tar.bz2 - - name - windows - - windows64 - - archive - - hash - f40b8622ba38084b0962e273988d748f - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/82624/774520/openssl-1.1.1l.560177-windows64-560177.tar.bz2 - - name - windows64 - - - version - 1.1.1l.560177 - - pcre - - copyright - Copyright (c) 1997-2014 University of Cambridge; Copyright(c) 2009-2014 Zoltan Herczeg; Copyright (c) 2007-2012, Google Inc. - description - PCRE Perl-compatible regular expression library - license - bsd - license_file - LICENSES/pcre-license.txt - name - pcre - platforms - - darwin - - archive - - hash - 6d2b38897f1adf354b299345d5fc759b - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/pcre_3p-update-pcre/rev/297155/arch/Darwin/installer/pcre-8.35.-darwin-297155.tar.bz2 - - name - darwin - - darwin64 - - archive - - hash - d8c0f97fe5abef43e72b6f84aba698b2 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54856/510176/pcre-8.35.538986-darwin64-538986.tar.bz2 - - name - darwin64 - - linux - - archive - - hash - 24a119b18e63017ad932ad54df8161bc - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/p64_3p-pcre/rev/314136/arch/Linux/installer/pcre-8.35.314136-linux-314136.tar.bz2 - - name - linux - - linux64 - - archive - - hash - 0f058ca2176e7d02d51e54c66a96f336 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/908/2010/pcre-8.35.500898-linux64-500898.tar.bz2 - - name - linux64 - - windows - - archive - - hash - 3660db45793df3050b63920bfb7d8479 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/55041/512002/pcre-8.35.538986-windows-538986.tar.bz2 - - name - linux - - windows64 - - archive - - hash - cdee8e8b48a66266550bf279c40abc22 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/55038/511992/pcre-8.35.538986-windows64-538986.tar.bz2 - - name - windows64 - - - version - 8.35.538986 - - slvoice - - copyright - 2010 Vivox, including audio coding using PolycomΒ¨ Siren14TM (ITU-T Rec. G.722.1 Annex C) - description - Vivox SDK components - license - Mixed - license_file - LICENSES/vivox_licenses.txt - name - slvoice - platforms - - darwin - - archive - - hash - 511a9c3fd4b6c76a8a737d06bba1c291 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/oz-426-slvoice/rev/330003/arch/Darwin/installer/slvoice-4.9.0002.27586.330003-darwin-330003.tar.bz2 - - name - darwin - - darwin64 - - archive - - hash - 6ce3cbaed968a69fb7a2cca80220874d - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/80380/758537/slvoice-4.10.0000.32327.5fc3fe7c.558436-darwin64-558436.tar.bz2 - - name - darwin64 - - linux - - archive - - hash - 785c86999b56e1838cefb430f674cba7 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/oz-426-slvoice/rev/330003/arch/Linux/installer/slvoice-3.2.0002.10426.330003-linux-330003.tar.bz2 - - name - linux - - linux64 - - archive - - hash - 92b0ae08832bd0e99c34ef8f3e6346ad - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/613/1289/slvoice-3.2.0002.10426.500605-linux64-500605.tar.bz2 - - name - linux64 - - windows - - archive - - hash - 2eb38c5eff4d0f18fbb89d0c30c4f0a4 - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/80382/758550/slvoice-4.10.0000.32327.5fc3fe7c.558436-windows-558436.tar.bz2 - - name - windows - - windows64 - - archive - - hash - 9ee8f3cbc5369c598a998c61961ed16d - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/80381/758551/slvoice-4.10.0000.32327.5fc3fe7c.558436-windows64-558436.tar.bz2 - - name - windows64 - - - version - 4.10.0000.32327.5fc3fe7c.558436 - - tracy - - canonical_repo - https://bitbucket.org/lindenlab/3p-tracy - copyright - Copyright (c) 2017-2021, Bartosz Taudul (wolf@nereid.pl) - description - Tracy Profiler Library - license - bsd - license_file - LICENSES/tracy_license.txt - name - tracy - platforms - - darwin64 - - archive - - hash - da7317e4a81609f624f84780f28b07de - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/86972/801630/tracy-v0.7.8.563351-darwin64-563351.tar.bz2 - - name - darwin64 - - windows - - archive - - hash - 47c696cd2966c5cc3c8ba6115dd1f886 - hash_algorithm - md5 - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/86973/801641/tracy-v0.7.8.563351-windows-563351.tar.bz2 - - name - windows - - windows64 - - archive - - hash - b649ee6591e67d2341e886b3fc3484a7 - hash_algorithm - md5 - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/86974/801642/tracy-v0.7.8.563351-windows64-563351.tar.bz2 - - name - windows64 - - - source - https://bitbucket.org/lindenlab/3p-tracy - source_type - git - version - v0.7.8.563351 - - tut - - copyright - Copyright 2002-2006 Vladimir Dyuzhev, Copyright 2007 Denis Kononenko, Copyright 2008-2009 MichaΕ‚ Rzechonek - description - TUT is a small and portable unit test framework for C++. - license - bsd - license_file - LICENSES/tut.txt - name - tut - platforms - - common - - archive - - hash - 64e1c979aea2f74fe9c2d9d04573336d - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/55001/511871/tut-2008.11.30-common-539059.tar.bz2 - - name - common - - - version - 2008.11.30 - - uriparser - - copyright - Copyright (C) 2007, Weijia Song <songweijia@gmail.com>, Sebastian Pipping <webmaster@hartwork.org> - description - uriparser is a strictly RFC 3986 compliant URI parsing and handling library written in C. uriparser is cross-platform, fast, supports Unicode and is licensed under the New BSD license. - license - New BSD license - license_file - LICENSES/uriparser.txt - name - uriparser - platforms - - darwin - - archive - - hash - 22608adaf54e8ddc9182a719ba6e2b32 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/uriparser_3p-update-uriparser/rev/299435/arch/Darwin/installer/uriparser-0.8.0.1-darwin-299435.tar.bz2 - - name - darwin - - darwin64 - - archive - - hash - c42575ac8997de979eadb082c33a578e - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/81322/765512/uriparser-0.9.4-darwin64-559132.tar.bz2 - - name - darwin64 - - linux - - archive - - hash - dddfc8dea540801f93ba0382cb1e3685 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/uriparser_3p-update-uriparser/rev/299435/arch/Linux/installer/uriparser-0.8.0.1-linux-299435.tar.bz2 - - name - linux - - linux64 - - archive - - hash - 087375378f104cdac0cb0fe0ca43dd4d - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/346/880/uriparser-0.8.0.1-linux64-500342.tar.bz2 - - name - linux64 - - windows - - archive - - hash - 901b1063556fc6b2575e745eef2bf744 - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/81323/765528/uriparser-0.9.4-windows-559132.tar.bz2 - - name - windows - - windows64 - - archive - - hash - 962c01d553f286c430102998129fb0d6 - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/81324/765527/uriparser-0.9.4-windows64-559132.tar.bz2 - - name - windows64 - - - version - 0.9.4 - - viewer-manager - - copyright - Copyright (c) 2000-2012, Linden Research, Inc. - description - Linden Lab Viewer Management Process suite. - license - viewerlgpl - license_file - LICENSE - name - viewer-manager - platforms - - darwin64 - - archive - - hash - 6989053898b8e81e904e75553e378820 - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/77523/735051/viewer_manager-2.0.556340-darwin64-556340.tar.bz2 - - name - darwin64 - - linux - - archive - - hash - 8c7f32f85850248809ae811ba8e47d81 - url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3428/8686/viewer_manager-1.0-linux-503417.tar.bz2 - - name - linux - - windows - - archive - - hash - 3446c1e54bb32542677caad0ec0d42ac - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/77525/735058/viewer_manager-2.0.556340-windows-556340.tar.bz2 - - name - windows - - - source - https://bitbucket.org/lindenlab/vmp-standalone - source_type - hg - version - 2.0.556340 - - vlc-bin - - copyright - Copyright (C) 1998-2016 VLC authors and VideoLAN - license - GPL2 - license_file - LICENSES/vlc.txt - name - vlc-bin - platforms - - darwin64 - - archive - - hash - b639d0035f4a8c9b4973be428a1b7e61 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/69569/671323/vlc_bin-3.0.9.549888-darwin64-549888.tar.bz2 - - name - darwin64 - - linux - - archive - - hash - 2f410640df3f9812d1abff02a414cfa8 - url - https://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-vlc-bin/rev/315283/arch/Linux/vlc_bin-2.2.3-linux-201606011750-r10.tar.bz2 - - name - linux - - windows - - archive - - hash - 4f50b0c47daa081dd4fcb83763d5b0b2 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/69567/671314/vlc_bin-3.0.9.549888-windows-549888.tar.bz2 - - name - windows - - windows64 - - archive - - hash - c2f8c01fb6c261b72beb07f0c4cd423f - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/69568/671315/vlc_bin-3.0.9.549888-windows64-549888.tar.bz2 - - name - windows64 - - - version - 3.0.9.549888 - - xmlrpc-epi - - copyright - Copyright: (C) 2000 Epinions, Inc. - description - XMLRPC Library - license - xmlrpc-epi - license_file - LICENSES/xmlrpc-epi.txt - name - xmlrpc-epi - platforms - - darwin - - archive - - hash - ffd3aab8e0c0ff6dadbce49ca2809078 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/xmlrpc-emi_3p-update-xmlrpc-epi/rev/297075/arch/Darwin/installer/xmlrpc_epi-0.54.1.297075-darwin-297075.tar.bz2 - - name - darwin - - darwin64 - - archive - - hash - 922a0dea32266897ed1911200438e1e1 - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/76372/727426/xmlrpc_epi-0.54.1.555529-darwin64-555529.tar.bz2 - - name - darwin64 - - linux - - archive - - hash - b63f828e798287d475991134cdcfbca3 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/p64_3p-xmlrpc-epi/rev/314240/arch/Linux/installer/xmlrpc_epi-0.54.1.314240-linux-314240.tar.bz2 - - name - linux - - linux64 - - archive - - hash - 35df17c3eb673030dea4bde9191aa506 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/727/1489/xmlrpc_epi-0.54.1.500719-linux64-500719.tar.bz2 - - name - linux64 - - windows - - archive - - hash - 34b847e6b280048465fe7c6ce67fe05c - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/76374/727436/xmlrpc_epi-0.54.1.555529-windows-555529.tar.bz2 - - name - windows - - windows64 - - archive - - hash - 8fbe7c4ea22bb7f23a93c73884ebb34c - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/76373/727435/xmlrpc_epi-0.54.1.555529-windows64-555529.tar.bz2 - - name - windows64 - - - version - 0.54.1.555529 - - zlib - - copyright - Copyright (C) 1995-2013 Jean-loup Gailly and Mark Adler - description - Zlib Data Compression Library - license - zlib - license_file - LICENSES/zlib.txt - name - zlib - platforms - - darwin - - archive - - hash - 1a79eeac199c2d94e4ae4e5d0194e25f - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/zlib_3p-update-zlib/rev/296881/arch/Darwin/installer/zlib-1.2.8.296881-darwin-296881.tar.bz2 - - name - darwin - - darwin64 - - archive - - hash - 9181bc8229f1a8e480d2a40a2744ec28 - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/78578/743913/zlib-1.2.11.557041-darwin64-557041.tar.bz2 - - name - darwin64 - - linux - - archive - - hash - 98a8c775c581ca80bb559e8b4e8eaae7 - hash_algorithm - md5 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/p64_3p-zlib/rev/314131/arch/Linux/installer/zlib-1.2.8.314131-linux-314131.tar.bz2 - - name - linux - - linux64 - - archive - - hash - dab6be8b0596c1e3354f2b6d41335131 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/866/1898/zlib-1.2.8.500857-linux64-500857.tar.bz2 - - name - linux64 - - windows - - archive - - hash - 8308cbd2ea0fe290541698b0f63482e2 - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/78579/743926/zlib-1.2.11.557041-windows-557041.tar.bz2 - - name - windows - - windows64 - - archive - - hash - 36bdc34f67d3ad3c57125dc1b16a3129 - url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/78577/743920/zlib-1.2.11.557041-windows64-557041.tar.bz2 - - name - windows64 - - - version - 1.2.11.557041 - - - package_description - - canonical_repo - https://bitbucket.org/lindenlab/viewer - copyright - Copyright (c) 2020, Linden Research, Inc. - description - Second Life Viewer - license - LGPL - license_file - docs/LICENSE-source.txt - name - Second Life Viewer - platforms - - common - - configurations - - RelWithDebInfo - - build - - - configure - - command - cmake - options - - -DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo - -DADDRESS_SIZE:STRING=$AUTOBUILD_ADDRSIZE - -DROOT_PROJECT_NAME:STRING=SecondLife - -DINSTALL_PROPRIETARY=TRUE - - - name - RelWithDebInfo - - RelWithDebInfoOS - - configure - - arguments - - ../indra - - command - cmake - options - - -DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo - -DADDRESS_SIZE:STRING=$AUTOBUILD_ADDRSIZE - -DROOT_PROJECT_NAME:STRING=SecondLife - -DINSTALL_PROPRIETARY=FALSE - - - name - RelWithDebInfoOS - - Release - - build - - - configure - - command - cmake - options - - -DCMAKE_BUILD_TYPE:STRING=Release - -DADDRESS_SIZE:STRING=$AUTOBUILD_ADDRSIZE - -DROOT_PROJECT_NAME:STRING=SecondLife - -DINSTALL_PROPRIETARY=TRUE - - - name - Release - - ReleaseOS - - configure - - arguments - - ../indra - - command - cmake - options - - -DCMAKE_BUILD_TYPE:STRING=Release - -DADDRESS_SIZE:STRING=$AUTOBUILD_ADDRSIZE - -DROOT_PROJECT_NAME:STRING=SecondLife - -DINSTALL_PROPRIETARY=FALSE - - - name - ReleaseOS - - - name - common - - darwin64 - - build_directory - build-darwin-x86_64 - configurations - - RelWithDebInfo - - build - - command - xcodebuild - options - - -configuration - RelWithDebInfo - -project - SecondLife.xcodeproj - - - configure - - arguments - - ../indra - - options - - -G - Xcode - - - default - True - name - RelWithDebInfo - - RelWithDebInfoOS - - build - - command - xcodebuild - options - - -configuration - RelWithDebInfo - -project - SecondLife.xcodeproj - - - configure - - options - - -G - Xcode - - - name - RelWithDebInfoOS - - Release - - build - - command - xcodebuild - options - - -configuration - Release - -project - SecondLife.xcodeproj - - - configure - - arguments - - ../indra - - options - - -G - Xcode - - - name - Release - - ReleaseOS - - build - - command - xcodebuild - options - - -configuration - Release - -project - SecondLife.xcodeproj - - - configure - - options - - -G - Xcode - - - name - ReleaseOS - - - name - darwin64 - - linux - - build_directory - build-linux-i686 - configurations - - RelWithDebInfo - - build - - command - make - options - - -j - 12 - - - configure - - arguments - - ../indra - - options - - -G - Unix Makefiles - - - default - True - name - RelWithDebInfo - - RelWithDebInfoOS - - build - - command - make - options - - -j - 7 - - - configure - - options - - -G - Unix Makefiles - - - name - RelWithDebInfoOS - - Release - - build - - command - make - options - - -j - 12 - - - configure - - arguments - - ../indra - - options - - -G - Unix Makefiles - - - name - Release - - ReleaseOS - - build - - command - make - options - - -j - 7 - - - configure - - options - - -G - Unix Makefiles - - - name - ReleaseOS - - default - - build - - - name - default - - - name - linux - - windows - - build_directory - build-vc${AUTOBUILD_VSVER|150}-$AUTOBUILD_ADDRSIZE - configurations - - RelWithDebInfo - - build - - arguments - - SecondLife.sln - - command - devenv - options - - /build - RelWithDebInfo|${AUTOBUILD_WIN_VSPLATFORM|NOTWIN} - - - configure - - arguments - - ..\indra - - options - - -G - ${AUTOBUILD_WIN_CMAKE_GEN|NOTWIN} - - - default - True - name - RelWithDebInfo - - RelWithDebInfoOS - - build - - arguments - - SecondLife.sln - - command - msbuild.exe - options - - /p:Configuration=RelWithDebInfo - /p:Platform=${AUTOBUILD_WIN_VSPLATFORM|NOTWIN} - /t:Build - /p:useenv=true - /verbosity:minimal - /toolsversion:4.0 - /p:VCBuildAdditionalOptions= /incremental - - - configure - - arguments - - ..\indra - - options - - -G - ${AUTOBUILD_WIN_CMAKE_GEN|NOTWIN} - -DINSTALL_PROPRIETARY=FALSE - -DUSE_KDU=FALSE - -DOPENAL:BOOL=ON - - - name - RelWithDebInfoOS - - Release - - build - - arguments - - SecondLife.sln - - command - devenv - options - - /build - Release|${AUTOBUILD_WIN_VSPLATFORM|NOTWIN} - - - configure - - arguments - - ..\indra - - options - - -G - ${AUTOBUILD_WIN_CMAKE_GEN|NOTWIN} - - - name - Release - - ReleaseOS - - build - - arguments - - SecondLife.sln - - command - msbuild.exe - options - - /p:Configuration=Release - /p:Platform=${AUTOBUILD_WIN_VSPLATFORM|NOTWIN} - /t:Build - /p:useenv=true - /verbosity:minimal - /toolsversion:4.0 - /p:VCBuildAdditionalOptions= /incremental - - - configure - - arguments - - ..\indra - - options - - -G - ${AUTOBUILD_WIN_CMAKE_GEN|NOTWIN} - -DUNATTENDED:BOOL=ON - -DINSTALL_PROPRIETARY=FALSE - -DUSE_KDU=FALSE - -DOPENAL:BOOL=ON - - - name - ReleaseOS - - - name - windows - - - version_file - newview/viewer_version.txt - - type - autobuild - version - 1.3 - - diff --git a/tests/fuzz.py b/tests/fuzz.py deleted file mode 100644 index fb48ff0..0000000 --- a/tests/fuzz.py +++ /dev/null @@ -1,465 +0,0 @@ -from __future__ import division - -import random -import string -import struct -import time -import uuid -from datetime import date, datetime, timedelta - -# Ridiculous optimization -- compute once ahead of time so choice() doesn't -# call len(string.printable) a bajillion times. Amazing it makes a difference. -printable_len = len(string.printable) - - -import llsd -from llsd.base import is_string - - -class LLSDFuzzer(object): - """Generator of fuzzed LLSD objects. - - The :class:`LLSDFuzzer` constructor accepts a *seed* argument, which becomes the - seed of the PRNG driving the fuzzer. If *seed* is None, the current time is - used. The seed is also stored in the seed attribute on the object, which - is useful for producing deterministic trials. - """ - def __init__(self, seed=None): - self.r = random.Random() - if seed is None: - seed = time.time() - self.seed = seed - self.r.seed(seed) - - def random_boolean(self): - """Returns a random boolean.""" - return bool(self.r.getrandbits(1)) - - def random_integer(self): - """Returns a random integral value.""" - return self.r.getrandbits(32) - 2**31 - - def random_real(self): - """Returns a random floating-point value.""" - return self.r.uniform(-1000000.0, 1000000.0) - - def random_uuid(self): - """Returns a random UUID object.""" - # use bytes from our local Random, instead of the various uuid - # constructors, so as to be completely deterministic - return uuid.UUID(int= self.r.getrandbits(128)) - - def _string_length(self): - """Returns a 'reasonable' random length for a string. The current - distribution is such that it usually returns a number less than 25, but - occasionally can return numbers as high as a few thousand.""" - return int(self.r.lognormvariate(2.7, 1.3)) - - def random_printable(self, length = None): - """Returns a string of random printable characters with length *length*. - Uses a random length if none is specified.""" - if length is None: - length = self._string_length() - return ''.join([string.printable[int(self.r.random() * printable_len)] - for x in range(length)]) - - def random_unicode(self, length = None): - """Returns a string of random unicode characters with length *length*. - Uses a random length if none is specified.""" - # length of the unicode string will be only vaguely - # close to the specified length because we're not bothering - # to generate valid utf-16 and therefore many of our - # bytes may be discarded as invalid - if length is None: - length = self._string_length() - bytes = self.random_bytes(length * 2) - # utf-16 instead of utf-8 or 32 because it's way faster - return bytes.decode('utf-16', 'ignore') - - def random_bytes(self, length = None): - """Returns a string of random bytes with length *length*. - Uses a random length if none is specified.""" - if length is None: - length = self._string_length() - - if length % 8 == 0: - num_chunks = length // 8 - else: - num_chunks = length // 8 + 1 - - # this appears to be the fastest way to generate random byte strings - packstr = 'Q'*num_chunks - randchunks = [self.r.getrandbits(64) for i in range(num_chunks)] - return struct.pack(packstr, *randchunks)[:length] - - def random_binary(self, length=None): - """Returns a random llsd.binary object containing *length* random - bytes. Uses a random length if none is specified.""" - return llsd.binary(self.random_bytes(length)) - - def random_date(self): - """Returns a random date within the range of allowed LLSD dates (1970 - to 2038)""" - return datetime.utcfromtimestamp(0) + \ - timedelta(seconds=self.r.getrandbits(31)) - - def random_uri(self, length=None): - """Returns a random llsd.uri object containing *length* random - printable characters (so, not necessarily a legal uri). Uses a random - length if none is specified.""" - return llsd.uri(self.random_printable(length)) - - def _container_size(self): - "Returns a random 'reasonable' container size." - return int(round(self.r.expovariate(0.3)+1)) - - def random_map(self): - """Returns a Python dictionary which has string keys and - randomly-chosen values. It is single-level; none of the - values are themselves maps or arrays.""" - retval = {} - for x in range(self._container_size()): - if self.random_boolean(): - key = self.random_unicode() - else: - key = self.random_printable() - value = self.random_atom(include_containers=False) - retval[key] = value - return retval - - def random_array(self): - """Returns a random Python array which is populated - with random values. It is single-level; none of the - values are themselves maps or arrays.""" - return [self.random_atom(include_containers=False) - for x in range(self._container_size())] - - random_generators = [ - lambda self: None, - random_boolean, - random_integer, - random_real, - random_uuid, - random_printable, - random_unicode, - random_binary, - random_date, - random_uri, - random_map, - random_array] - - def random_atom(self, include_containers=True): - """Returns a random LLSD atomic value.""" - if include_containers: - return self.r.choice(self.random_generators)(self) - else: - return self.r.choice(self.random_generators[:-2])(self) - - def permute_undef(self, val): - """Permutes undef, the return value is always None.""" - return None - - def permute_boolean(self, val): - """Permutes booleans, the return value is always a boolean.""" - return self.random_boolean() - - integer_options = [ - lambda s, v: -v, - lambda s, v: 0, - lambda s, v: v + s.r.randint(-(v**4), (v**4)), - lambda s, v: 4294967296, # 2^32 - lambda s, v: -2147483649, # -2^31 - 1 - lambda s, v: 18446744073709551616, # 2^64 - lambda s, v: s.random_integer(), - lambda s, v: v * ((s.r.getrandbits(16) - 2**15) or 1), - lambda s, v: v // ((s.r.getrandbits(16) - 2**15) or 1), - lambda s, v: v + s.random_integer(), - lambda s, v: v - s.random_integer(), - lambda s, v: v * ((s.r.getrandbits(8) - 2**7) or 1), - lambda s, v: v // ((s.r.getrandbits(8) - 2**7) or 1) - ] - - def permute_integer(self, val): - """Generates variations on a given int or long, - returned value is an int or a long.""" - return self.r.choice(self.integer_options)(self, val) - - real_options = [ - lambda s, v: -v, - lambda s, v: 0.0, - lambda s, v: v/float(2**64), - lambda s, v: v*float(2**64), - lambda s, v: 1E400, - lambda s, v: -1E400, - lambda s, v: float('nan'), - lambda s, v: s.random_real(), - lambda s, v: v * (s.r.random() - 0.5), - lambda s, v: v / (s.r.random() - 0.5), - lambda s, v: v + s.random_real(), - lambda s, v: v - s.random_real(), - lambda s, v: v * s.random_real(), - lambda s, v: v / s.random_real() - ] - - def permute_real(self, val): - """Generates variations on a float, the returned - value is a float.""" - return self.r.choice(self.real_options)(self, val) - - def permute_uuid(self, val): - """ Generates variations on a uuid, the returned value is a uuid.""" - return uuid.UUID(int=self.r.getrandbits(128) ^ val.int) - - def rand_idx(self, val): - """Return a random index into the value.""" - if len(val) == 0: - return 0 - return self.r.randrange(0, len(val)) - - stringlike_options = [ - lambda s,v,strgen: strgen() + v, - lambda s,v,strgen: v + strgen(), - lambda s,v,strgen: v[s.rand_idx(v):], - lambda s,v,strgen: v[:s.rand_idx(v)], - lambda s,v,strgen: v[:s.rand_idx(v)] + strgen() + v[s.rand_idx(v):] - ] - - def _permute_stringlike(self, val, strgen): - if len(val) == 0: - return strgen() - else: - return self.r.choice(self.stringlike_options)(self, val, strgen) - - def permute_string(self, val): - """Generates variations on a given string or unicode. All - generated values are strings/unicodes.""" - assert is_string(val) - def string_strgen(length = None): - if self.random_boolean(): - return self.random_printable(length) - else: - return self.random_unicode(length) - return self._permute_stringlike(val, string_strgen) - - def permute_binary(self, val): - """Generates variations on a given binary value. All - generated values are llsd.binary.""" - assert isinstance(val, llsd.binary) - return llsd.binary(self._permute_stringlike(val, self.random_bytes)) - - def _date_clamp(self, val): - if val.year >= 2038: - raise OverflowError() - elif val.year < 1970: - return date(1970, val.month, val.day) - else: - return val - - date_options = [ - lambda s, v: s._date_clamp(v + timedelta( - seconds=s.r.getrandbits(21) - 2**20, - microseconds=s.r.getrandbits(20))), - lambda s, v: datetime.utcfromtimestamp(0), - lambda s, v: date(v.year, v.month, v.day), - lambda s, v: datetime.utcfromtimestamp(2**31-86400), - ] - - def permute_date(self, val): - """Generates variations on a given datetime. All generated - values are datetimes within the valid llsd daterange.""" - assert isinstance(val, (datetime, date)) - # have to retry a few times because the random-delta option - # sometimes gets out of range - while True: - try: - return self.r.choice(self.date_options)(self, val) - except (OverflowError, OSError): - continue - - def permute_uri(self, val): - """Generates variations on a given uri. All - generated values are llsd.uri.""" - assert isinstance(val, llsd.uri) - return llsd.uri(self._permute_stringlike(val, self.random_printable)) - - def _permute_map_permute_value(self, val): - if len(val) == 0: - return {} - # choose one of the keys from val - k = self.r.choice(list(val)) - permuted = val.copy() - permuted[k] = next(self.structure_fuzz(val[k])) - return permuted - - def _permute_map_key_delete(self, val): - if len(val) == 0: - return {} - # choose one of the keys from val - k = self.r.choice(list(val)) - permuted = val.copy() - permuted.pop(k, None) - return permuted - - def _permute_map_new_key(self, val): - permuted = val.copy() - if len(val) > 0 and self.random_boolean(): - # choose one of the keys from val - new_key = self.permute_string(self.r.choice(list(val))) - else: - new_key = self.random_unicode() - - permuted[new_key] = self.random_atom() - return permuted - - def _permute_map_permute_key_names(self, val): - if len(val) == 0: - return {} - # choose one of the keys from val - k = self.r.choice(list(val)) - k = self.permute_string(k) - permuted = val.copy() - v = permuted.pop(k, None) - permuted[k] = v - return permuted - - - map_sub_permuters = (_permute_map_permute_value, - _permute_map_permute_value, - _permute_map_key_delete, - _permute_map_new_key, - _permute_map_permute_key_names) - - def permute_map(self, val): - """ Generates variations on an input dict via a variety of steps. - The return value is a dict.""" - assert isinstance(val, dict) - permuted = self.r.choice(self.map_sub_permuters)(self, val) - for i in range(int(self.r.expovariate(0.5)) + 1): - permuted = self.r.choice(self.map_sub_permuters)(self, permuted) - return permuted - - def _permute_array_permute_value(self, val): - idx = self.r.randrange(0, len(val)) - permuted = list(val) - permuted[idx] = next(self.structure_fuzz(val[idx])) - return permuted - - def _permute_array_subsets(self, val): - return self.r.sample(val, self.r.randint(1, len(val))) - - def _permute_array_inserting(self, val): - new_idx = self.r.randint(0, len(val)) - inserted = self.random_atom() - permuted = list(val[:new_idx]) + [inserted] + list(val[new_idx:]) - return permuted - - def _permute_array_reorder(self, val): - permuted = list(val) - swaps = self.r.randrange(0, len(val)) - for s in range(swaps): - i = self.r.randrange(0, len(val)) - j = self.r.randrange(0, len(val)) - permuted[i], permuted[j] = permuted[j], permuted[i] - return permuted - - array_sub_permuters = (_permute_array_permute_value, - _permute_array_permute_value, - _permute_array_subsets, - _permute_array_inserting, - _permute_array_reorder) - - def permute_array(self, val): - """ Generates variations on an input array via a variety of steps. - The return value is a dict.""" - assert isinstance(val, (list, tuple)) - permuted = self.r.choice(self.array_sub_permuters)(self, val) - for i in range(int(self.r.expovariate(0.5)) + 1): - permuted = self.r.choice(self.array_sub_permuters)(self, permuted) - if self.random_boolean(): - permuted = tuple(permuted) - return permuted - - permuters = { - type(None): permute_undef, - bool: permute_boolean, - int: permute_integer, - llsd.LongType: permute_integer, - float: permute_real, - uuid.UUID: permute_uuid, - str: permute_string, - llsd.UnicodeType: permute_string, - llsd.binary: permute_binary, - datetime: permute_date, - date: permute_date, - llsd.uri: permute_uri, - dict: permute_map, - list: permute_array, - tuple: permute_array} - - def structure_fuzz(self, starting_structure): - """ Generates a series of Python structures - based on the input structure.""" - permuter = self.permuters[type(starting_structure)] - while True: - if self.r.getrandbits(2) == 0: - yield self.random_atom() - else: - yield permuter(self, starting_structure) - - def _random_numeric(self, length): - return ''.join([self.r.choice(string.digits) - for x in range(length)]) - - def _dirty(self, val): - idx1 = self.rand_idx(val) - idx2 = idx1 + int(round(self.r.expovariate(0.1))) - if self.random_boolean(): - # replace with same-length string - subst_len = idx2 - idx1 - else: - subst_len = int(round(self.r.expovariate(0.1))) - - if self.random_boolean(): - # use printable - if self.random_boolean(): - replacement = self._random_numeric(subst_len).encode('latin-1') - else: - replacement = self.random_printable(subst_len).encode('latin-1') - else: - replacement = self.random_bytes(subst_len) - - return val[:idx1] + replacement + val[idx2:] - - def _serialized_fuzz(self, it, formatter): - while True: - struct = next(it) - try: - text = formatter(struct) - except llsd.LLSDSerializationError: - continue - yield text - dirtied = self._dirty(text) - for i in range(int(round(self.r.expovariate(0.3)))): - dirtied = self._dirty(dirtied) - yield dirtied - - def binary_fuzz(self, starting_structure): - """ Generates a series of strings which are meant to be tested against - a service that parses binary LLSD.""" - return self._serialized_fuzz( - self.structure_fuzz(starting_structure), - llsd.format_binary) - - def xml_fuzz(self, starting_structure): - """ Generates a series of strings which are meant to be tested against - a service that parses XML LLSD.""" - return self._serialized_fuzz( - self.structure_fuzz(starting_structure), - llsd.format_xml) - - def notation_fuzz(self, starting_structure): - """ Generates a series of strings which are meant to be tested against - a service that parses the LLSD notation serialization.""" - return self._serialized_fuzz( - self.structure_fuzz(starting_structure), - llsd.format_notation) \ No newline at end of file diff --git a/tests/llsd_test.py b/tests/llsd_test.py deleted file mode 100644 index 073a974..0000000 --- a/tests/llsd_test.py +++ /dev/null @@ -1,1979 +0,0 @@ - -# -*- coding: utf-8 -*- -from __future__ import print_function - -import base64 -from datetime import date, datetime -import io -from itertools import islice -import pprint -import re -import struct -import time -import unittest -import uuid -from os import path - -import pytest - -import llsd -from llsd.base import PY2, is_integer, is_string, is_unicode, MAX_FORMAT_DEPTH -from llsd.serde_xml import remove_invalid_xml_bytes -from tests.fuzz import LLSDFuzzer - -FIXTURES_DIR = path.join(path.dirname(__file__), 'fixtures') - - -class Foo(object): - """ - Simple Mock Class used for testing. - """ - pass - -try: - from math import isnan as _isnan - def isnan(x): - if isinstance(x, float): - return _isnan(x) - else: - return False -except ImportError: - def isnan(x): - return x != x - - -class LLSDNotationUnitTest(unittest.TestCase): - """ - This class aggregates all the tests for parse_notation(something), - LLSD.as_notation(something) and format_notation (i.e. same as - LLSD.as_notation(something). Note that test scenarios for the - same input type are all put into single test method. And utility - method assert_notation_roundtrip is used to test parse_notation - and as_notation at the same time. - """ - def setUp(self): - """ - Set up the test class - """ - self.llsd = llsd.LLSD() - - def strip(self, the_string): - """ - Remove any whitespace characters from the input string. - - :Parameters: - - 'the_string': string to remove the whitespaces. - """ - return re.sub(br'\s', b'', the_string) - - def assertNotationRoundtrip(self, py_in, str_in, is_alternate_notation=False): - """ - Utility method to check the result of parse_notation and - LLSD.as_notation. - """ - # use parse to check here - py_out = self.llsd.parse(str_in) - py_out2 = self.llsd.parse(io.BytesIO(str_in)) - self.assertEqual(py_out2, py_out) - str_out = self.llsd.as_notation(py_in) - py_roundtrip = self.llsd.parse(str_out) - py_roundtrip2 = self.llsd.parse(io.BytesIO(str_out)) - self.assertEqual(py_roundtrip2, py_roundtrip) - str_roundtrip = self.llsd.as_notation(py_out) - # compare user-passed Python data with parsed user-passed string - self.assertEqual(py_in, py_out) - # compare user-passed Python data with parsed (serialized data) - self.assertEqual(py_in, py_roundtrip) - -## # Comparing serialized data invites exasperating spurious test -## # failures. Most interesting LLSD data is contained in dicts, and -## # Python has never guaranteed the serialization order of dict keys. -## # If str_in is an alternate notation, we can't compare it directly. -## if not is_alternate_notation: -## self.assertEqual(self.strip(str_out), self.strip(str_in)) -## self.assertEqual(self.strip(str_out), self.strip(str_roundtrip)) - - # use parse_notation to check again - py_out = llsd.parse_notation(str_in) - str_out = self.llsd.as_notation(py_in) - py_roundtrip = llsd.parse_notation(str_out) - str_roundtrip = self.llsd.as_notation(py_out) - self.assertEqual(py_in, py_out) - self.assertEqual(py_in, py_roundtrip) - -## # Disabled for the same reason as above. -## # If str_in is an alternate notation, we can't compare it directly. -## if not is_alternate_notation: -## self.assertEqual(self.strip(str_out), self.strip(str_in)) -## self.assertEqual(self.strip(str_out), self.strip(str_roundtrip)) - - def testInteger(self): - """ - Test the input type integer. - Maps to test scenarios module:llsd:test#4-6 - """ - pos_int_notation = b"i123456" - neg_int_notation = b"i-123457890" - blank_int_notation = b"i0" - - python_pos_int = 123456 - python_neg_int = -123457890 - - self.assertNotationRoundtrip(python_pos_int, - pos_int_notation) - self.assertNotationRoundtrip(python_neg_int, - neg_int_notation) - self.assertEqual(0, self.llsd.parse(blank_int_notation)) - - def testUndefined(self): - """ - Test the input type : undef - Maps to test scenarios module:llsd:test#7 - """ - undef_notation = b"!" - self.assertNotationRoundtrip(None, undef_notation) - - def testBoolean(self): - """ - Test the input type : Boolean - Maps to test scenarios module:llsd:test#8-17 - """ - sample_data = [(True, b"TRUE"), - (True, b"true"), - (True, b"T"), - (True, b"t"), - (True, b"1"), - (False, b"FALSE"), - (False, b"false"), - (False, b"F"), - (False, b"f"), - (False, b"0") - ] - for py, notation in sample_data: - is_alternate_notation = False - if notation not in (b"true", b"false"): - is_alternate_notation = True - self.assertNotationRoundtrip(py, notation, is_alternate_notation) - - blank_notation = b"" - self.assertEqual(False, self.llsd.parse(blank_notation)) - - def testReal(self): - """ - Test the input type: real. - Maps to test scenarios module:llsd:test#18-20 - """ - pos_real_notation = b"r2983287453.3000002" - neg_real_notation = b"r-2983287453.3000002" - blank_real_notation = b"r0" - - python_pos_real = 2983287453.3 - python_neg_real = -2983287453.3 - - self.assertNotationRoundtrip(python_pos_real, - pos_real_notation, True) - self.assertNotationRoundtrip(python_neg_real, - neg_real_notation, True) - self.assertEqual(0, self.llsd.parse(blank_real_notation)) - - - def testUUID(self): - """ - Test the input type : UUID. - Maps to test scenarios module:llsd:test#21 - """ - uuid_tests = { - uuid.UUID(hex='d7f4aeca-88f1-42a1-b385-b9db18abb255'):b"ud7f4aeca-88f1-42a1-b385-b9db18abb255", - uuid.UUID(hex='00000000-0000-0000-0000-000000000000'):b"u00000000-0000-0000-0000-000000000000"} - - for py, notation in uuid_tests.items(): - self.assertNotationRoundtrip(py, notation) - - def testString(self): - """ - Test the input type: String. - Maps to test scenarios module:llsd:test#22-24 - """ - sample_data = [('foo bar magic" go!', b"'foo bar magic\" go!'"), - ("foo bar magic's go!", b'"foo bar magic\'s go!"'), - ('have a nice day', b"'have a nice day'"), - ('have a nice day', b'"have a nice day"'), - ('have a nice day', b's(15)"have a nice day"'), - ('have a "nice" day', b'\'have a "nice" day\''), - ('have a "nice" day', b'"have a \\"nice\\" day"'), - ('have a "nice" day', b's(17)"have a "nice" day"'), - ("have a 'nice' day", b"'have a \\'nice\\' day'"), - ("have a 'nice' day", b'"have a \'nice\' day"'), - ("have a 'nice' day", b's(17)"have a \'nice\' day"'), - (u"Kanji: '\u5c0f\u5fc3\u8005'", - b"'Kanji: \\'\xe5\xb0\x8f\xe5\xbf\x83\xe8\x80\x85\\''"), - (u"Kanji: '\u5c0f\u5fc3\u8005'", - b"\"Kanji: '\\xe5\\xb0\\x8f\\xE5\\xbf\\x83\\xe8\\x80\\x85'\""), - ('\a\b\f\n\r\t\v', b'"\\a\\b\\f\\n\\r\\t\\v"') - ] - for py, notation in sample_data: - is_alternate_notation = False - if notation[0:1] != "'": - is_alternate_notation = True - self.assertNotationRoundtrip(py, notation, is_alternate_notation) - - def testURI(self): - """ - Test the input type: URI. - Maps to test scenarios module:llsd:test#25 - 26 - """ - uri_tests = { - llsd.uri('http://www.topcoder.com/tc/projects?id=1230'):b'l"http://www.topcoder.com/tc/projects?id=1230"', - llsd.uri('http://www.topcoder.com/tc/projects?id=1231'):b"l'http://www.topcoder.com/tc/projects?id=1231'"} - - blank_uri_notation = b'l""' - - for py, notation in uri_tests.items(): - is_alternate_notation = False - if notation[1:2] != b'"': - is_alternate_notation = True - self.assertNotationRoundtrip(py, notation, is_alternate_notation) - self.assertEqual('', self.llsd.parse(blank_uri_notation)) - - def testDate(self): - """ - Test the input type : Date. - Maps to test scenarios module:llsd:test#27 - 30 - """ - valid_date_notation = b'd"2006-02-01T14:29:53.460000Z"' - valid_date_notation_no_float = b'd"2006-02-01T14:29:53Z"' - valid_date_notation_zero_seconds = b'd"2006-02-01T14:29:00Z"' - valid_date_notation_filled = b'd"2006-02-01T14:29:05Z"' - valid_date_19th_century = b'd"1833-02-01T00:00:00Z"' - - blank_date_notation = b'd""' - - python_valid_date = datetime(2006, 2, 1, 14, 29, 53, 460000) - python_valid_date_no_float = datetime(2006, 2, 1, 14, 29, 53) - python_valid_date_zero_seconds = datetime(2006, 2, 1, 14, 29, 0) - python_valid_date_filled = datetime(2006, 2, 1, 14, 29, 5) - python_valid_date_19th_century = datetime(1833,2,1) - - python_blank_date = datetime(1970, 1, 1) - - self.assertNotationRoundtrip(python_valid_date, - valid_date_notation) - self.assertNotationRoundtrip(python_valid_date_no_float, - valid_date_notation_no_float) - self.assertNotationRoundtrip(python_valid_date_zero_seconds, - valid_date_notation_zero_seconds) - self.assertNotationRoundtrip(python_valid_date_filled, - valid_date_notation_filled) - - self.assertNotationRoundtrip(python_valid_date_filled, - valid_date_notation_filled) - self.assertNotationRoundtrip(python_valid_date_19th_century, - valid_date_19th_century) - - self.assertEqual(python_blank_date, self.llsd.parse(blank_date_notation)) - - def testArray(self): - """ - Test the input type : Array. - Maps to test scenarios module:llsd:test#31-33 - """ - # simple array - array_notation = b"['foo', 'bar']" - # composite array - array_within_array_notation = b"['foo', 'bar',['foo', 'bar']]" - # blank array - blank_array_notation = b"[]" - - python_array = [str("foo"), "bar"] - python_array_within_array = ["foo", "bar", ["foo", "bar"]] - python_blank_array = [] - - self.assertNotationRoundtrip(python_array, array_notation) - self.assertNotationRoundtrip(python_array_within_array, - array_within_array_notation) - self.assertNotationRoundtrip(python_blank_array, blank_array_notation) - - def testMap(self): - """ - Test the input type : Map. - Maps to test scenarios module:llsd:test#34-36 - """ - # simple map - map_notation = b"{'foo':'bar'}" - - # composite map - map_within_map_notation = b"{'foo':'bar','doo':{'goo':'poo'}}" - - # blank map - blank_map_notation = b"{}" - - python_map = {"foo":"bar"} - python_map_within_map = {"foo":"bar", "doo":{"goo":"poo"}} - python_blank_map = {} - - self.assertNotationRoundtrip(python_map, map_notation) - self.assertNotationRoundtrip(python_map_within_map, - map_within_map_notation) - self.assertNotationRoundtrip(python_blank_map, blank_map_notation) - - def testBinary(self): - """ - Test the input type: binary. - Maps to test scenarios module:llsd:test#37 - """ - string_data1 = b"quick brown fox!!" - string_data2 = b""" -

"Take some more tea ," the March Hare said to Alice, very earnestly.

- """ - python_binary1 = llsd.binary(string_data1) - python_binary2 = llsd.binary(string_data2) - - notation1 = b'b64' + b'"' + base64.b64encode(string_data1).strip() + b'"' - notation2 = b'b64' + b'"' + base64.b64encode(string_data2).strip() + b'"' - notation3 = b'b16' + b'"' + base64.b16encode(string_data1).strip() + b'"' - notation4 = b'b16' + b'"' + base64.b16encode(string_data2).strip() + b'"' - notation5 = b'b85' + b'"<~EHPu*CER),Dg-(AAoDo;+T~>"' - notation6 = b'b85' +br'"<~4E*J.<+0QR+EMIu4+@0gX@q@26G%G]>+D"u%DImm2Cj@Wq05s)~>"' - - self.assertNotationRoundtrip(python_binary1, notation1, True) - self.assertNotationRoundtrip(python_binary2, notation2, True) - self.assertNotationRoundtrip(python_binary1, notation3, True) - self.assertNotationRoundtrip(python_binary2, notation4, True) - self.assertRaises(llsd.LLSDParseError, self.llsd.parse, notation5) - self.assertRaises(llsd.LLSDParseError, self.llsd.parse, notation6) - - ''' - def testProblemMap(self): - """ - This is some data that the fuzzer generated that caused a parse error - """ - string_data = b"{'$g7N':!,'3r=h':true,'\xe8\x88\xbc\xe9\xa7\xb9\xe1\xb9\xa6\xea\xb3\x95\xe0\xa8\xb3\xe1\x9b\x84\xef\xb2\xa7\xe8\x8f\x99\xe8\x94\xa0\xe9\x90\xb9\xe6\x88\x9b\xe0\xaf\x84\xe8\xb8\xa2\xe4\x94\x83\xea\xb5\x8b\xed\x8c\x8a\xe5\xb5\x97':'\xe6\xbb\xa6\xe3\xbf\x88\xea\x9b\x82\xea\x9f\x8d\xee\xbb\xba\xe4\xbf\x87\xe3\x8c\xb5\xe3\xb2\xb0\xe7\x90\x91\xee\x8f\xab\xee\x81\xa5\xea\x94\x98'}" - python_obj = {} - - import pdb; pdb.set_trace() - self.assertNotationRoundtrip(python_obj, string_data, True) - ''' - - def testNotationOfAllTypes(self): - """ - Test notation with mixed with all kinds of simple types. - Maps to test scenarios module:llsd:test#38 - """ - python_object = [{'destination': 'http://secondlife.com'}, {'version': - 1}, {'modification_date': datetime(2006, 2, 1, 14, 29, 53, - 460000)}, {'first_name': 'Phoenix', 'last_name': 'Linden', 'granters': - [uuid.UUID('a2e76fcd-9360-4f6d-a924-000000000003')], 'look_at': [-0.043753, - -0.999042, 0.0], 'attachment_data': [{'attachment_point': - 2, 'item_id': uuid.UUID('d6852c11-a74e-309a-0462-50533f1ef9b3'), - 'asset_id': uuid.UUID('c69b29b1-8944-58ae-a7c5-2ca7b23e22fb')}, - {'attachment_point': 10, 'item_id': - uuid.UUID('ff852c22-a74e-309a-0462-50533f1ef900'), 'asset_id': - uuid.UUID('5868dd20-c25a-47bd-8b4c-dedc99ef9479')}], 'session_id': - uuid.UUID('2c585cec-038c-40b0-b42e-a25ebab4d132'), 'agent_id': - uuid.UUID('3c115e51-04f4-523c-9fa6-98aff1034730'), 'circuit_code': 1075, - 'position': [70.9247, 254.378, - 38.7304]}] - - notation = b"""[ - {'destination':'http://secondlife.com'}, - {'version':i1}, - {'modification_date':d"2006-02-01T14:29:53.460000Z"} - { - 'agent_id':u3c115e51-04f4-523c-9fa6-98aff1034730, - 'session_id':u2c585cec-038c-40b0-b42e-a25ebab4d132, - 'circuit_code':i1075, - 'first_name':'Phoenix', - 'last_name':'Linden', - 'position':[r70.9247,r254.378,r38.7304], - 'look_at':[r-0.043753,r-0.999042,r0.0], - 'granters':[ua2e76fcd-9360-4f6d-a924-000000000003], - 'attachment_data':[ - { - 'attachment_point':i2, - 'item_id':ud6852c11-a74e-309a-0462-50533f1ef9b3, - 'asset_id':uc69b29b1-8944-58ae-a7c5-2ca7b23e22fb - }, - { - 'attachment_point':i10, - 'item_id':uff852c22-a74e-309a-0462-50533f1ef900, - 'asset_id':u5868dd20-c25a-47bd-8b4c-dedc99ef9479 - } - ] - }]""" - - result = self.llsd.parse(notation) - self.assertEqual(python_object, result) - - # roundtrip test - notation_result = self.llsd.as_notation(python_object) - python_object_roundtrip = self.llsd.parse(notation_result) - self.assertEqual(python_object_roundtrip, python_object) - - def testLLSDSerializationFailure(self): - """ - Test llsd searialization with non supportd object type. - TypeError should be raised. - - Maps test scenarios : module:llsd:test#91 - """ - # make an object not supported by llsd - python_native_obj = Foo() - - # assert than an exception is raised - self.assertRaises(TypeError, self.llsd.as_notation, python_native_obj) - self.assertRaises(llsd.LLSDParseError, self.llsd.parse, b'2') - - def testParseNotationInvalidNotation1(self): - """ - Test with an invalid array notation. - Maps to module:llsd:test#76, 86 - """ - self.assertRaises(llsd.LLSDParseError, self.llsd.parse, b"[ 'foo' : 'bar')") - - def testParseNotationInvalidNotation2(self): - """ - Test with an invalid map notation. - Maps to module:llsd:test#87 - """ - self.assertRaises(llsd.LLSDParseError, self.llsd.parse, b"{'foo':'bar','doo':{'goo' 'poo'}") # missing separator - self.assertRaises(llsd.LLSDParseError, self.llsd.parse, b"{'foo':'bar','doo':{'goo' : 'poo'}") # missing closing '}' - - def testParseNotationInvalidNotation3(self): - """ - Test with an invalid map notation. - Maps to module:llsd:test#88 - """ - self.assertRaises(llsd.LLSDParseError, self.llsd.parse, b"day day up, day day up") - - def testParseNotationInvalidNotation4(self): - """ - Test with an invalid date notation. - """ - self.assertRaises(llsd.LLSDParseError, self.llsd.parse, b'd"2006#02-01T1429:53.460000Z"') - - def testParseNotationInvalidNotation5(self): - """ - Test with an invalid int notation. - """ - self.assertRaises(llsd.LLSDParseError, self.llsd.parse, b'i*123xx') - - def testParseNotationInvalidNotation6(self): - """ - Test with an invalid real notation. - """ - self.assertRaises(llsd.LLSDParseError, self.llsd.parse, b'r**1.23.3434') - - def testParseNotationInvalidNotation7(self): - """ - Test with an invalid binary notation. - """ - self.assertRaises(llsd.LLSDParseError, self.llsd.parse, b"b634'bGFsYQ='") - - def testParseNotationInvalidNotation8(self): - """ - Test with an invalid map notation. - """ - self.assertRaises(llsd.LLSDParseError, self.llsd.parse, b"{'foo':'bar',doo':{'goo' 'poo'}}") - - def testParseNotationInvalidNotation9(self): - """ - Test with an invalid map notation. - """ - self.assertRaises(llsd.LLSDParseError, self.llsd.parse, b"[i123,i123)") - - def testParseNotationInvalidNotation10(self): - """ - Test with an invalid raw string notation. - """ - self.assertRaises(llsd.LLSDParseError, self.llsd.parse, b"s[2]'xx'") - - def testParseNotationInvalidNotation11(self): - """ - Test with an invalid raw string notation. - """ - self.assertRaises(llsd.LLSDParseError, self.llsd.parse, b"s(2]'xx'") - - def testParseNotationInvalidNotation12(self): - """ - Test with an invalid raw string notation. - """ - self.assertRaises(llsd.LLSDParseError, self.llsd.parse, b"s(2)'xxxxx'") - - def testParseNotationInvalidNotation13(self): - """ - Test with an invalid raw string notation. - """ - self.assertRaises(llsd.LLSDParseError, self.llsd.parse, b"s(2)*xx'") - - def testParseNotationIncorrectMIME(self): - """ - Test with correct notation format but incorrect MIME type. -> llsd:test79 - """ - try: - self.llsd.parse(b"[ {'foo':'bar'}, {'foo':'bar'} ]", llsd.XML_MIME_TYPE) - self.fail("LLSDParseError should be raised.") - except llsd.LLSDParseError: - pass - - def testParseNotationUnterminatedString(self): - """ - Test with an unterminated delimited string - """ - self.assertRaises(llsd.LLSDParseError, self.llsd.parse, b"'foo") - - def testParseNotationHexEscapeNoChars(self): - self.assertRaises(llsd.LLSDParseError, self.llsd.parse, b"'\\x") - - def testParseNotationHalfTruncatedHex(self): - self.assertRaises(llsd.LLSDParseError, self.llsd.parse, b"'\\xf") - - def testParseNotationInvalidHex(self): - self.assertRaises(llsd.LLSDParseError, self.llsd.parse, b"'\\xzz'") - - def testDeepMap(self): - """ - Test formatting of a deeply nested map - """ - - test_map = {"foo":"bar", "depth":0} - max_depth = MAX_FORMAT_DEPTH - 1 - for depth in range(max_depth): - test_map = {"foo":"bar", "depth":depth, "next":test_map} - - # this should not throw an exception. - test_notation_out = self.llsd.as_notation(test_map) - - test_notation_parsed = self.llsd.parse(io.BytesIO(test_notation_out)) - self.assertEqual(test_map, test_notation_parsed) - - test_map = {"foo":"bar", "depth":depth, "next":test_map} - # this should throw an exception. - self.assertRaises(llsd.LLSDSerializationError, self.llsd.as_notation, test_map) - - -class LLSDBinaryUnitTest(unittest.TestCase): - """ - This class aggregates all the tests for parse_binary and LLSD.as_binary - which is the same as module function format_binary. The tests use roundtrip - test to check the serialization of llsd object and the parsing of binary - representation of llsd object. - - Note that llsd binary test scenarios maps to module:llsd:test#66 which reuses - all the test scenarios of llsd xml. - """ - def setUp(self): - """ - Set up the test class, create a LLSD object and assign to self.llsd. - """ - self.llsd = llsd.LLSD() - - def roundTrip(self, something): - """ - Utility method which serialize the passed in object using - binary format, parse the serialized binary format into object, and - return the object. - """ - binary = self.llsd.as_binary(something) - frombytes = self.llsd.parse(binary) - fromstream = self.llsd.parse(io.BytesIO(binary)) - self.assertEqual(fromstream, frombytes) - return frombytes - - def testMap(self): - """ - Test the binary serialization and parse of llsd type : Map. - """ - map_xml = b"""\ - - - -foo -bar - -""" - - map_within_map_xml = b"\ -\ -\ -\ -foo\ -bar\ -doo\ -\ -goo\ -poo\ -\ -\ -" - - blank_map_xml = b"\ -\ -\ -" - - python_map = {"foo" : "bar"} - python_map_within_map = {"foo":"bar", "doo":{"goo":"poo"}} - - self.assertEqual(python_map, self.roundTrip(self.llsd.parse(map_xml))) - self.assertEqual( - python_map_within_map, - self.roundTrip(self.llsd.parse(map_within_map_xml))) - self.assertEqual({}, self.roundTrip(self.llsd.parse(blank_map_xml))) - - def testArray(self): - """ - Test the binary serialization and parse of llsd type : Array. - """ - array_xml = b"\ -\ -\ -\ -foo\ -bar\ -\ -" - array_within_array_xml = b"\ -\ -\ -\ -foo\ -bar\ -\ -foo\ -bar\ -\ -\ -" - blank_array_xml = b"\ -\ -\ -" - - python_array = ["foo", "bar"] - python_array_within_array = ["foo", "bar", ["foo", "bar"]] - - self.assertEqual( - python_array, - self.roundTrip(self.llsd.parse(array_xml))) - self.assertEqual( - python_array_within_array, - self.roundTrip(self.llsd.parse(array_within_array_xml))) - self.assertEqual( - [], - self.roundTrip(self.llsd.parse(blank_array_xml))) - - def testString(self): - """ - Test the binary serialization and parse of llsd type : string. - """ - normal_xml = b""" - - -foo -""" - - blank_xml = b"\ -\ -\ -\ -" - - self.assertEqual('foo', self.roundTrip(self.llsd.parse(normal_xml))) - self.assertEqual("", self.roundTrip(self.llsd.parse(blank_xml))) - - def testInteger(self): - """ - Test the binary serialization and parse of llsd type : integer - """ - pos_int_xml = b"\ -\ -\ -289343\ -" - - neg_int_xml = b"\ -\ -\ --289343\ -" - - blank_int_xml = b"\ -\ -\ -\ -" - - python_pos_int = 289343 - python_neg_int = -289343 - - self.assertEqual( - python_pos_int, - self.roundTrip(self.llsd.parse(pos_int_xml))) - self.assertEqual( - python_neg_int, - self.roundTrip(self.llsd.parse(neg_int_xml))) - self.assertEqual( - 0, - self.roundTrip(self.llsd.parse(blank_int_xml))) - - def testReal(self): - """ - Test the binary serialization and parse of llsd type : real. - """ - pos_real_xml = b"\ -\ -\ -2983287453.3\ -" - - neg_real_xml = b"\ -\ -\ --2983287453.3\ -" - - blank_real_xml = b"\ -\ -\ -\ -" - - python_pos_real = 2983287453.3 - python_neg_real = -2983287453.3 - - self.assertEqual( - python_pos_real, - self.roundTrip(self.llsd.parse(pos_real_xml))) - self.assertEqual( - python_neg_real, - self.roundTrip(self.llsd.parse(neg_real_xml))) - self.assertEqual( - 0, - self.roundTrip(self.llsd.parse(blank_real_xml))) - - def testBoolean(self): - """ - Test the binary serialization and parse of llsd type : boolean. - """ - true_xml = b"\ -\ -\ -true\ -" - - false_xml = b"\ -\ -\ -false\ -" - - blank_xml = b"\ -\ -\ -\ -" - - self.assertEqual(True, self.roundTrip(self.llsd.parse(true_xml))) - self.assertEqual(False, self.roundTrip(self.llsd.parse(false_xml))) - self.assertEqual(False, self.roundTrip(self.llsd.parse(blank_xml))) - - def testDate(self): - """ - Test the binary serialization and parse of llsd type : date. - """ - valid_date_binary = b"d\x00\x00\x40\x78\x31\xf8\xd0\x41" - valid_date_xml = b"\ -\ -\ -2006-02-01T14:29:53Z\ -" - - blank_date_xml = b"\ -\ -\ -\ -" - python_valid_date = datetime(2006, 2, 1, 14, 29, 53) - python_blank_date = datetime(1970, 1, 1) - - self.assertEqual( - python_valid_date, - self.roundTrip(self.llsd.parse(valid_date_xml))) - self.assertEqual( - python_valid_date, - self.roundTrip(llsd.parse_binary(valid_date_binary))) - self.assertEqual( - python_blank_date, - self.roundTrip(self.llsd.parse(blank_date_xml))) - - def testBinary(self): - """ - Test the binary serialization and parse of llsd type : binary. - """ - base64_binary_xml = b"\ -\ -\ -dGhlIHF1aWNrIGJyb3duIGZveA==\ -" - - foo = self.llsd.parse(base64_binary_xml) - self.assertEqual( - llsd.binary(b"the quick brown fox"), - self.roundTrip(foo)) - - def testUUID(self): - """ - Test the binary serialization and parse of llsd type : UUID. - """ - valid_uuid_xml = b"\ -\ -\ -d7f4aeca-88f1-42a1-b385-b9db18abb255\ -" - blank_uuid_xml = b"\ -\ -\ -\ -" - self.assertEqual( - 'd7f4aeca-88f1-42a1-b385-b9db18abb255', - self.roundTrip(str(self.llsd.parse(valid_uuid_xml)))) - self.assertEqual( - '00000000-0000-0000-0000-000000000000', - self.roundTrip(str(self.llsd.parse(blank_uuid_xml)))) - - binary_uuid = b"""\nu\xe1g\xa9D\xd9\x06\x89\x04-\x04\x92\xab\x8e\xaf5\xbf""" - - self.assertEqual(uuid.UUID('e167a944-d906-8904-2d04-92ab8eaf35bf'), - llsd.parse(binary_uuid)) - - def testURI(self): - """ - Test the binary serialization and parse of llsd type : URI. - """ - valid_uri_xml = b"\ -\ -\ -http://sim956.agni.lindenlab.com:12035/runtime/agents\ -" - - blank_uri_xml = b"\ -\ -\ -\ -" - - self.assertEqual( - 'http://sim956.agni.lindenlab.com:12035/runtime/agents', - self.roundTrip(self.llsd.parse(valid_uri_xml))) - self.assertEqual( - '', - self.roundTrip(self.llsd.parse(blank_uri_xml))) - - def testUndefined(self): - """ - Test the binary serialization and parse of llsd type : undef. - """ - undef_xml = b"" - self.assertEqual( - None, - self.roundTrip(self.llsd.parse(undef_xml))) - - - def testBinaryOfAllTypes(self): - """ - Test the binary serialization and parse of a composited llsd object - which is composited of simple llsd types. - """ - multi_xml = b"\ -\ -\ -\ -\ -\ -content-typeapplication/binary\ -\ -MTIzNDU2Cg==\ -\ -\ -\ -content-typeapplication/exe\ -\ -d2hpbGUoMSkgeyBwcmludCAneWVzJ307Cg==\ -\ -\ -" - - multi_python = [ - [{'content-type':'application/binary'},b'123456\n'], - [{'content-type':'application/exe'},b"while(1) { print 'yes'};\n"]] - - self.assertEqual( - multi_python, - self.roundTrip(self.llsd.parse(multi_xml))) - - def testInvalidBinaryFormat(self): - """ - Test the parse with an invalid binary format. LLSDParseError should - be raised. - - Maps to test scenarios : module:llsd:test#78 - """ - invalid_binary = b"""\n[\\xx0{}]]""" - - self.assertRaises(llsd.LLSDParseError, llsd.parse, invalid_binary) - - def testParseBinaryIncorrectMIME(self): - """ - Test parse with binary format data but has an incorrect MIME type. - - LLSDParseError should be raised. - - Maps to test scenarios : module:llsd:test#81 - """ - binary_data = b"""\n[\x00\x00\x00\x02i\x00\x00\x00{i\x00\x00\x00{]""" - - try: - llsd.parse(binary_data, llsd.XML_MIME_TYPE) - self.fail("LLSDParseError should be raised.") - except llsd.LLSDParseError: - pass - - def testParseBinaryInvlaidBinaryFormat(self): - """ - Test the parse_binary with an invalid binary format. LLSDParseError - should be raised. - - Maps to test scenario : module:llsd:test#82 - """ - invalid_binary = b"""\n[\\xx0{}]]""" - - self.assertRaises(llsd.LLSDParseError, llsd.parse_binary, invalid_binary) - - def testAsBinaryWithNonSupportedType(self): - """ - Test the as_binary with a non-supported python type. - - Maps to test scenario module:llsd:test#89 - """ - # make an object not supported by llsd - python_native_obj = Foo() - - # assert than an exception is raised - self.assertRaises(TypeError, self.llsd.as_binary, python_native_obj) - self.assertRaises(llsd.LLSDParseError, self.llsd.parse, b'2') - - def testInvlaidBinaryParse1(self): - """ - Test with invalid binary format of map. - """ - invalid_binary = b"""\n{\x00\x00\x00\x01k\x00\x00\x00\x06'kaka'i\x00\x00\x00{{""" - - self.assertRaises(llsd.LLSDParseError, self.llsd.parse, invalid_binary) - - def testInvlaidBinaryParse2(self): - """ - Test with invalid binary format of array. - """ - invalid_binary = b"""\n[\x00\x00\x00\x02i\x00\x00\x00\x01i\x00\x00\x00\x02*""" - - self.assertRaises(llsd.LLSDParseError, self.llsd.parse, invalid_binary) - - def testParseDelimitedString(self): - """ - Test parse_binary with delimited string. - """ - delimited_string = b"""\n'\\t\\a\\b\\f\\n\\r\\t\\v\\x0f\\p'""" - - self.assertEqual('\t\x07\x08\x0c\n\r\t\x0b\x0fp', llsd.parse(delimited_string)) - - def testDeepMap(self): - """ - Test formatting of a deeply nested map - """ - - test_map = {"foo":"bar", "depth":0} - max_depth = MAX_FORMAT_DEPTH -1 - for depth in range(max_depth): - test_map = {"foo":"bar", "depth":depth, "next":test_map} - - # this should not throw an exception. - test_binary_out = self.llsd.as_binary(test_map) - - test_binary_parsed = self.llsd.parse(io.BytesIO(test_binary_out)) - self.assertEqual(test_map, test_binary_parsed) - - test_map = {"foo":"bar", "depth":depth, "next":test_map} - # this should throw an exception. - self.assertRaises(llsd.LLSDSerializationError, self.llsd.as_binary, test_map) - - - -class LLSDPythonXMLUnitTest(unittest.TestCase): - """ - This class aggregates all the tests for parse_xml(something), LLSD.as_xml(something) - and format_xml (i.e. same as LLSD.as_xml(something). - Note that test scenarios for the same input type are all put into single test method. And utility - method assert_xml_roundtrip is used to test parse_xml and as_xml at the same time. - - NOTE: Tests in this class use the pure python implementation for - serialization of llsd object to llsd xml format. - """ - def setUp(self): - """ - Create a LLSD object - """ - self.llsd = llsd.LLSD() - - def assertXMLRoundtrip(self, py, xml, ignore_rounding=False): - """ - Utility method to test parse_xml and as_xml at the same time - """ - - # use parse to check - parsed_py = self.llsd.parse(xml) - parsed_stream = self.llsd.parse(io.BytesIO(xml)) - self.assertEqual(parsed_stream, parsed_py) - formatted_xml = self.llsd.as_xml(py) - self.assertEqual(parsed_py, py) - self.assertEqual(py, self.llsd.parse(formatted_xml)) -## if not ignore_rounding: -## self.assertEqual(self.strip(formatted_xml), -## self.strip(xml)) -## self.assertEqual(self.strip(xml), -## self.strip(self.llsd.as_xml(parsed_py))) - - # use parse_xml to check again - parsed_py = llsd.parse_xml(xml) - formatted_xml = self.llsd.as_xml(py) - self.assertEqual(parsed_py, py) - self.assertEqual(py, llsd.parse_xml(formatted_xml)) -## if not ignore_rounding: -## self.assertEqual(self.strip(formatted_xml), -## self.strip(xml)) -## self.assertEqual(self.strip(xml), -## self.strip(self.llsd.as_xml(parsed_py))) - - def testBytesConversion(self): - """ - Test the __bytes__() conversion on the LLSD class - """ - if PY2: - return # not applicable on python 2 - some_xml =b"\ -\ -\ -1234\ -" - - c = llsd.LLSD(llsd.parse_xml(some_xml)) - out_xml = bytes(c) - - self.assertEqual(some_xml, out_xml) - - def testStrConversion(self): - """ - Test the __str__() conversion on the LLSD class - """ - some_xml =b"\ -\ -\ -1234\ -" - - c = llsd.LLSD(llsd.parse_xml(some_xml)) - out_xml = str(c).encode() - - self.assertEqual(some_xml, out_xml) - - def testInteger(self): - """ - Test the parse and serializatioin of input type : integer - Maps to the test scenarios : module:llsd:test#39 - 41 - """ - pos_int_xml = b"\ -\ -\ -289343\ -" - - neg_int_xml = b"\ -\ -\ --289343\ -" - - blank_int_xml = b"\ -\ -\ -\ -" - - python_pos_int = 289343 - python_neg_int = -289343 - python_blank_int = 0 - - self.assertXMLRoundtrip(python_pos_int, - pos_int_xml) - self.assertXMLRoundtrip(python_neg_int, - neg_int_xml) - self.assertEqual(python_blank_int, self.llsd.parse(blank_int_xml)) - - def testUndefined(self): - """ - Test the parse and serialization of input type: undef - - Maps to test scenarios module:llsd:test#42 - """ - undef_xml = b"" - self.assertXMLRoundtrip(None, undef_xml) - - def testBoolean(self): - """ - Test the parse and serialization of input tye: boolean. -> llsd:test 43 - 45 - """ - true_xml = b"\ -\ -\ -true\ -" - - false_xml = b"\ -\ -\ -false\ -" - - blank_xml = b"\ -\ -\ -\ -" - - self.assertXMLRoundtrip(True, true_xml) - self.assertXMLRoundtrip(False, false_xml) - self.assertEqual(False, self.llsd.parse(blank_xml)) - - def testReal(self): - """ - Test the parse and serialization of input type : real. - Maps to test scenarios module:llsd:test# 46 - 48 - """ - pos_real_xml = b"\ -\ -\ -2983287453.3000002\ -" - - neg_real_xml = b"\ -\ -\ --2983287453.3000002\ -" - - blank_real_xml = b"\ -\ -\ -\ -" - - python_pos_real = 2983287453.3 - python_neg_real = -2983287453.3 - python_blank_real = 0.0 - - self.assertXMLRoundtrip(python_pos_real, - pos_real_xml, True) - self.assertXMLRoundtrip(python_neg_real, - neg_real_xml, True) - self.assertEqual(python_blank_real, self.llsd.parse(blank_real_xml)) - - def testUUID(self): - """ - Test the parse and serialization of input type: UUID. - Maps to test scenarios module:llsd:test#49 - """ - uuid_tests = { - uuid.UUID(hex='d7f4aeca-88f1-42a1-b385-b9db18abb255'):b"\ -\ -\ -d7f4aeca-88f1-42a1-b385-b9db18abb255\ -", - uuid.UUID(int=0):b"\ -\ -\ -\ -"} - - for py, xml in uuid_tests.items(): - self.assertXMLRoundtrip(py, xml) - - - def testString(self): - """ - Test the parse and serialization of input type : String. - Maps to test scenarios module:llsd:test# 50 - 51 - """ - sample_data = {'foo':b"\ -\ -\ -foo\ -", - '':b"\ -\ -\ -\ -", - '&ent;':b"\ -\ -\ -<xml>&ent;</xml>\ -" - } - for py, xml in sample_data.items(): - self.assertXMLRoundtrip(py, xml) - - def testURI(self): - """ - Test the parse and serialization of input type: URI. - Maps to test scenarios module:llsd:test# 52 - 53 - """ - uri_tests = { - llsd.uri('http://sim956.agni.lindenlab.com:12035/runtime/agents'):b"\ -\ -\ -http://sim956.agni.lindenlab.com:12035/runtime/agents\ -"} - - blank_uri_xml = b"\ -\ -\ -\ -" - - for py, xml in uri_tests.items(): - self.assertXMLRoundtrip(py, xml) - self.assertEqual('', self.llsd.parse(blank_uri_xml)) - - def testDate(self): - """ - Test the parse and serialization of input type : Date. - Maps to test scenarios module:llsd:test#54 - 57 - """ - valid_date_xml = b"\ -\ -\ -2006-02-01T14:29:53.460000Z\ -" - - valid_date_xml_no_fractional = b"\ -\ -\ -2006-02-01T14:29:53Z\ -" - valid_date_xml_filled = b"\ -\ -\ -2006-02-01T14:29:05Z\ -" - - blank_date_xml = b"\ -\ -\ -\ -" - - before_19th_century_date = b"\ -\ -\ -1853-02-01T00:00:00Z\ -" - - python_valid_date = datetime(2006, 2, 1, 14, 29, 53, 460000) - python_valid_date_no_fractional = datetime(2006, 2, 1, 14, 29, 53) - python_valid_date_filled = datetime(2006, 2, 1, 14, 29, 5) - python_blank_date = datetime(1970, 1, 1) - python_19th_century_date = datetime(1853, 2, 1) - self.assertXMLRoundtrip(python_valid_date, - valid_date_xml) - self.assertXMLRoundtrip(python_valid_date_no_fractional, - valid_date_xml_no_fractional) - self.assertXMLRoundtrip(python_valid_date_filled, - valid_date_xml_filled) - self.assertXMLRoundtrip(python_19th_century_date, - before_19th_century_date) - self.assertEqual(python_blank_date, self.llsd.parse(blank_date_xml)) - - def testArray(self): - """ - Test the parse and serialization of input type : Array. - Maps to test scenarios module:llsd:test# 58 - 60 - """ - # simple array - array_xml = b"\ -\ -\ -\ -foo\ -bar\ -\ -" - # composite array - array_within_array_xml = b"\ -\ -\ -\ -foo\ -bar\ -\ -foo\ -bar\ -\ -\ -" - # blank array - blank_array_xml = b"\ -\ -\ -\ -" - - python_array = ["foo", "bar"] - python_array_within_array = ["foo", "bar", ["foo", "bar"]] - - self.assertXMLRoundtrip(python_array, array_xml) - self.assertXMLRoundtrip(python_array_within_array, - array_within_array_xml) - self.assertXMLRoundtrip([], blank_array_xml) - - def testMap(self): - """ - Test the parse and serialization of input type : map. - Maps to test scenarios module:llsd:test# 61 - 63 - """ - # simple map - map_xml = b"""\ - - - -foo -bar - -""" - # composite map - map_within_map_xml = b"\ -\ -\ -\ -foo\ -bar\ -doo\ -\ -goo\ -poo\ -\ -\ -" - # blank map - blank_map_xml = b"\ -\ -\ -\ -" - - python_map = {"foo":"bar"} - python_map_within_map = {"foo":"bar", "doo":{"goo":"poo"}} - - self.assertXMLRoundtrip(python_map, map_xml) - self.assertXMLRoundtrip(python_map_within_map, - map_within_map_xml) - self.assertXMLRoundtrip({}, blank_map_xml) - - def testBinary(self): - """ - Test the parse and serialization of input type : binary. - Maps to test scenarios module:llsd:test#64 - """ - base64_binary_xml = b"\ -\ -\ -dGhlIHF1aWNrIGJyb3duIGZveA==\ -" - - python_binary = llsd.binary(b"the quick brown fox") - self.assertXMLRoundtrip(python_binary, - base64_binary_xml) - - blank_binary_xml = b"""""" - - python_binary = llsd.binary(b''); - - self.assertXMLRoundtrip(python_binary, blank_binary_xml) - - @pytest.mark.skipif(PY2, reason="Object order between python 2 and 3 differs") - def testViewerAutobuildRoundTrip(self): - """Test that llsd does not muck up the viewer autobuild""" - with open(path.join(FIXTURES_DIR, "viewer-autobuild.xml"), "rb") as f: - autobuild_bytes = f.read() - autobuild_parsed = llsd.parse_xml(autobuild_bytes) - assert autobuild_bytes.decode("utf8") == llsd.format_pretty_xml(autobuild_parsed).decode("utf8") - - - def testXMLOfAllTypes(self): - """ - Test parse_xml with complex xml data which contains all types xml element. - Maps to test scenarios module:llsd:test#65 - """ - xml_of_all_types = b""" - - - string1 - 3.1415 - 18686 - - www.topcoder.com/tc - 2006-02-01T14:29:53.43Z - - region_id - 67153d5b-3659-afb4-8510-adda2c034649 - scale - one minute - simulator statistics - - time dilation - 0.9878624 - sim fps - 44.38898 - pysics fps - 44.38906 - agent updates per second - 1.34 - lsl instructions per second - 0 - total task count - 4 - active task count - 0 - active script count - 4 - main agent count - 0 - child agent count - 0 - inbound packets per second - 1.228283 - outbound packets per second - 1.277508 - pending downloads - 0 - pending uploads - 0.0001096525 - frame ms - 0.7757886 - net ms - 0.3152919 - sim other ms - 0.1826937 - sim physics ms - 0.04323055 - agent ms - 0.01599029 - image ms - 0.01865955 - script ms - 0.1338836 - - - - """ - - python_object = ['string1', 3.1415, 18686, None, - 'www.topcoder.com/tc', datetime(2006, 2, 1, 14, 29, 53, 430000), - {'scale': 'one minute', 'region_id': - uuid.UUID('67153d5b-3659-afb4-8510-adda2c034649'), - 'simulator statistics': {'total task count': 4.0, 'active task count': 0.0, - 'time dilation': 0.9878624, 'lsl instructions per second': 0.0, 'frame ms': - 0.7757886, 'agent ms': 0.01599029, 'sim other ms': 0.1826937, - 'pysics fps': 44.38906, 'outbound packets per second': 1.277508, - 'pending downloads': 0.0, 'pending uploads': 0.0001096525, 'net ms': 0.3152919, - 'agent updates per second': 1.34, 'inbound packets per second': - 1.228283, 'script ms': 0.1338836, 'main agent count': 0.0, - 'active script count': 4.0, 'image ms': 0.01865955, 'sim physics ms': - 0.04323055, 'child agent count': 0.0, 'sim fps': 44.38898}}] - - parsed_python = llsd.parse(xml_of_all_types) - - self.assertEqual(python_object, parsed_python) - - def testFormatPrettyXML(self): - """ - Test the format_pretty_xml function, characters like \n,\t should be generated within - the output to beautify the output xml. - - This maps to test scenarios module:llsd:test#75 - """ - python_object = {'id': ['string1', 123, {'name': 123}]} - - output_xml = llsd.format_pretty_xml(python_object) - - with open("foo.llsd.xml", "wb") as f: - f.write(output_xml) - - self.assertEqual(output_xml.decode("utf8"), """ - - - id - - string1 - 123 - - name - 123 - - - - -""") - - # check whether the output_xml contains whitespaces and new line character - whitespaces_count = output_xml.count(b' ') - newline_count = output_xml.count(b'\n') - - self.assertTrue(whitespaces_count > 50) - self.assertTrue(newline_count > 10) - - # remove all the whitespaces and new line chars from output_xml - result = self.strip(output_xml) - - # the result should equal to the reuslt of format_xml - # the xml version tag should be removed before comparing - format_xml_result = self.llsd.as_xml(python_object) - self.assertEqual(result[result.find(b"?>") + 2: len(result)], - format_xml_result[format_xml_result.find(b"?>") + 2: len(format_xml_result)]) - - def testDeepMap(self): - """ - Test formatting of a deeply nested map - """ - - test_map = {"foo":"bar", "depth":0} - max_depth = MAX_FORMAT_DEPTH - 1 - for depth in range(max_depth): - test_map = {"foo":"bar", "depth":depth, "next":test_map} - - # this should not throw an exception. - test_xml_out = self.llsd.as_xml(test_map) - - test_xml_parsed = self.llsd.parse(io.BytesIO(test_xml_out)) - self.assertEqual(test_map, test_xml_parsed) - - test_map = {"foo":"bar", "depth":depth, "next":test_map} - # this should throw an exception. - self.assertRaises(llsd.LLSDSerializationError, self.llsd.as_xml, test_map) - - def testLLSDSerializationFailure(self): - """ - Test serialization function as_xml with an object of non-supported type. - TypeError should be raised. - - This maps test scenarios module:llsd:test#90 - """ - # make an object not supported by llsd - python_native_obj = Foo() - - # assert than an exception is raised - self.assertRaises(TypeError, self.llsd.as_xml, python_native_obj) - - def testParseXMLIncorrectMIME(self): - """ - Test parse function with llsd in xml format but with incorrect mime type. - - Maps to test scenario module:llsd:test#80 - """ - llsd_xml = b"""12.3232""" - - try: - self.llsd.parse(llsd_xml, llsd.NOTATION_MIME_TYPE) - self.fail("LLSDParseError should be raised.") - except llsd.LLSDParseError: - pass - - def testParseXMLIncorrectMIME2(self): - """ - Test parse function with llsd in xml format but with incorrect mime type. - - Maps to test scenario module:llsd:test#80 - """ - llsd_xml = b"""12.3232""" - - try: - self.llsd.parse(llsd_xml, llsd.BINARY_MIME_TYPE) - self.fail("LLSDParseError should be raised.") - except llsd.LLSDParseError: - pass - - def testParseMalformedXML(self): - """ - Test parse with malformed llsd xml. LLSDParseError should be raised. - - Maps to test scenarios module:llsd:test#77 - """ - malformed_xml = b"""string>123/llsd>""" - self.assertRaises(llsd.LLSDParseError, llsd.parse, malformed_xml) - - def testParseXMLUnsupportedTag(self): - """ - Test parse with llsd xml which has non-supported tag. LLSDParseError - should be raised. - - Maps to test scenario module:llsd:test#83 - """ - unsupported_tag_xml = b"""123 - 1/llsd>""" - self.assertRaises(llsd.LLSDParseError, llsd.parse, unsupported_tag_xml) - - def testParseXMLWithoutRootTag(self): - """ - Test parse with xml which does not have root tag . - LLSDParseError should be raised. - - Maps to test scenario module:llsd:test#84 - """ - no_root_tag_xml = b"""test1.3434""" - - self.assertRaises(llsd.LLSDParseError, llsd.parse, no_root_tag_xml) - - def testParseXMLUnclosedTag(self): - """ - Test parse with xml which has unclosed tag. - LLSDParseError should be raised. - - Maps to test scenario module:llsd:test#85 - """ - unclosed_tag_xml = b"""123 - 12345/llsd>""" - self.assertRaises(llsd.LLSDParseError, llsd.parse, unclosed_tag_xml) - - def strip(self, the_string): - """ - Utility method to remove all the whitespace characters from - the given string. - """ - return re.sub(br'\s', b'', the_string) - - def test_segfault(self): - for i, badstring in enumerate([ - b'', - b'', - b'', - b'', - b'', - b'', - b'']): - self.assertRaises(llsd.LLSDParseError, llsd.parse, badstring) - -class LLSDStressTest(unittest.TestCase): - """ - This class aggregates all the stress tests for llsd. - """ - - # python object used for testing - python_object = [{'destination': 'http://secondlife.com'}, {'version': - 1}, {'modification_date': datetime(2006, 2, 1, 14, 29, 53, - 460000)}, {'first_name': 'Phoenix', 'last_name': 'Linden', 'granters': - [uuid.UUID('a2e76fcd-9360-4f6d-a924-000000000003')], 'look_at': [-0.043753, - -0.999042, 0.0], 'attachment_data': [{'attachment_point': - 2, 'item_id': uuid.UUID('d6852c11-a74e-309a-0462-50533f1ef9b3'), - 'asset_id': uuid.UUID('c69b29b1-8944-58ae-a7c5-2ca7b23e22fb')}, - {'attachment_point': 10, 'item_id': - uuid.UUID('ff852c22-a74e-309a-0462-50533f1ef900'), 'asset_id': - uuid.UUID('5868dd20-c25a-47bd-8b4c-dedc99ef9479')}], 'session_id': - uuid.UUID('2c585cec-038c-40b0-b42e-a25ebab4d132'), 'agent_id': - uuid.UUID('3c115e51-04f4-523c-9fa6-98aff1034730'), 'circuit_code': 1075, - 'position': [70.9247, 254.378, - 38.7304]}] - - # how many times to run - number = 5000 - - def testParseAndFormatXMLStress(self): - """ - Stress test for parse_xml and as_xml. - - Maps to test scenraio module:llsd:test#95 - """ - t = time.time() - for i in range(0, self.number): - x = llsd.format_xml(self.python_object) - delta = time.time() - t - print("format_xml", str(self.number), " times takes total :", delta, "secs") - print("average time:", delta / self.number, "secs") - - t = time.time() - for i in range(0, self.number): - r = llsd.parse(x) - delta = time.time() - t - print("parse_xml", str(self.number), " times takes total :", delta, "secs") - print("average time:", delta / self.number, "secs") - - - def testParseAndFormatNotationStress(self): - """ - Stress test for parse_notation and as_notation. - - Maps to test scenario module:llsd:test#96 - """ - t = time.time() - for i in range(0, self.number): - x = llsd.format_notation(self.python_object) - delta = time.time() - t - print("format_notation", str(self.number), " times takes total :", delta, "secs") - print("average time:", delta / self.number, "secs") - - t = time.time() - for i in range(0, self.number): - r = llsd.parse(x) - delta = time.time() - t - print("parse_notation", str(self.number), " times takes total :", delta, "secs") - print("average time:", delta / self.number, "secs") - - def testParseAndFormatBinaryStress(self): - """ - Stress test for parse_binary and as_binary. - - Maps to test scenarios module:llsd:test#97,98 - """ - t = time.time() - for i in range(0, self.number): - x = llsd.format_binary(self.python_object) - delta = time.time() - t - print("format_binary", str(self.number), " times takes total :", delta, "secs") - print("average time:", delta / self.number, "secs") - - t = time.time() - for i in range(0, self.number): - r = llsd.parse(x) - delta = time.time() - t - print("parse_binary", str(self.number), " times takes total :", delta, "secs") - print("average time:", delta / self.number, "secs") - - -FUZZ_ITERATIONS = 5000 -class LLSDFuzzTest(unittest.TestCase): - """ - This class aggregates all the fuzz tests for llsd. - """ - python_object = LLSDStressTest.python_object - def assertEqualsPretty(self, a, b): - try: - self.assertEqual(a,b) - except AssertionError: - self.fail("\n%s\n !=\n%s" % (pprint.pformat(a), pprint.pformat(b))) - - def fuzz_parsing_base(self, fuzz_method_name, legit_exceptions): - fuzzer = LLSDFuzzer(seed=1234) - fuzz_method = getattr(fuzzer, fuzz_method_name) - for f in islice(fuzz_method(self.python_object), FUZZ_ITERATIONS): - try: - parsed = llsd.parse(f) - except legit_exceptions: - pass # expected, since many of the inputs will be invalid - except Exception as e: - print("Raised exception", e.__class__) - print("Fuzzed value was", repr(f)) - raise - - def fuzz_roundtrip_base(self, formatter_method, normalize=None): - fuzzer = LLSDFuzzer(seed=1234) - for f in islice(fuzzer.structure_fuzz(self.python_object), FUZZ_ITERATIONS): - try: - try: - text = formatter_method(f) - except llsd.LLSDSerializationError: - # sometimes the fuzzer will generate invalid llsd - continue - parsed = llsd.parse(text) - try: - self.assertEqualsPretty(parsed, f) - except AssertionError: - if normalize: - self.assertEqualsPretty(normalize(parsed), normalize(f)) - else: - raise - except llsd.LLSDParseError: - print("Failed to parse", repr(text)) - raise - - - def test_notation_parsing(self): - self.fuzz_parsing_base('notation_fuzz', - (llsd.LLSDParseError, IndexError, ValueError)) - - def test_notation_roundtrip(self): - def normalize(s): - """ Certain transformations of input data are permitted by - the spec; this function normalizes a python data structure - so it receives these transformations as well. - * date objects -> datetime objects (parser only produces datetimes) - * nan converted to None (just because nan's are incomparable) - """ - if is_string(s): - return s - if isnan(s): - return None - if isinstance(s, date): - return datetime(s.year, s.month, s.day) - if isinstance(s, (list, tuple)): - s = [normalize(x) for x in s] - if isinstance(s, dict): - s = dict([(normalize(k), normalize(v)) - for k,v in s.items()]) - return s - - self.fuzz_roundtrip_base(llsd.format_notation, normalize) - - def test_binary_parsing(self): - self.fuzz_parsing_base('binary_fuzz', - (llsd.LLSDParseError, IndexError, ValueError)) - - def test_binary_roundtrip(self): - def normalize(s): - """ Certain transformations of input data are permitted by - the spec; this function normalizes a python data structure - so it receives these transformations as well. - * date objects -> datetime objects (parser only produces datetimes) - * fractional seconds dropped from datetime objects - * integral values larger than a signed 32-bit int become wrapped - * integral values larger than an unsigned 32-bit int become 0 - * nan converted to None (just because nan's are incomparable) - """ - if isnan(s): - return None - if is_integer(s): - if (s > (2<<30) - 1 or - s < -(2<<30)): - return struct.unpack('!i', struct.pack('!i', s))[0] - if isinstance(s, date): - return datetime(s.year, s.month, s.day) - if isinstance(s, datetime): - return datetime(s.year, s.month, s.day, s.hour, s.minute, s.second) - if isinstance(s, (list, tuple)): - s = [normalize(x) for x in s] - if isinstance(s, dict): - s = dict([(normalize(k), normalize(v)) - for k,v in s.items()]) - return s - self.fuzz_roundtrip_base(llsd.format_binary, normalize) - - def test_xml_parsing(self): - self.fuzz_parsing_base('xml_fuzz', - (llsd.LLSDParseError, IndexError, ValueError)) - - newline_re = re.compile(r'[\r\n]+') - - @pytest.mark.skipif(PY2, reason="Fails because fuzz generates invalid unicode sequences on Python 2") - def test_xml_roundtrip(self): - def normalize(s): - """ Certain transformations of input data are permitted by - the spec; this function normalizes a python data structure - so it receives these transformations as well. - * codepoints disallowed in xml dropped from strings and unicode objects - * any sequence of \n and \r compressed into a single \n - * date objects -> datetime objects (parser only produces datetimes) - * nan converted to None (just because nan's are incomparable) - """ - if is_string(s): - s = remove_invalid_xml_bytes(s) - s = self.newline_re.sub('\n', s) - if is_unicode(s): - s = s.replace(u'\uffff', u'') - s = s.replace(u'\ufffe', u'') - return s - if isnan(s): - return None - if isinstance(s, date): - return datetime(s.year, s.month, s.day) - if isinstance(s, (list, tuple)): - s = [normalize(x) for x in s] - if isinstance(s, dict): - s = dict([(normalize(k), normalize(v)) - for k,v in s.items()]) - return s - self.fuzz_roundtrip_base(llsd.format_xml, normalize) - -class Regression(unittest.TestCase): - ''' - Regression tests. - ''' - - def test_no_newline_in_base64_notation(self): - n = llsd.format_notation(llsd.binary(b'\0'*100)) - self.assertEqual(n.replace(b'\n', b''), n) - - def test_no_newline_in_base64_xml(self): - n = llsd.format_xml(llsd.binary(b'\0'*100)) - self.assertEqual(n.replace(b'\n', b''), n) - - def test_SL_13073(self): - # "new note" in Russian with Cyrillic characters. - good_xml = u'Новая Π·Π°ΠΌΠ΅Ρ‚ΠΊΠ°'.encode('utf8') - new_note_unicode = u"Новая Π·Π°ΠΌΠ΅Ρ‚ΠΊΠ°" - new_note_str = "Новая Π·Π°ΠΌΠ΅Ρ‚ΠΊΠ°" - - # Py2 unicode - # Py3 str (unicode) - self.assertEqual(llsd.format_xml(new_note_unicode), good_xml) - - # Py2 LLSD(unicode) - # Py3 LLSD(str (unicode)) - self.assertEqual(llsd.format_xml(llsd.LLSD(new_note_unicode)), good_xml) - - # Py2 str (b"") - # Py3 str (unicode) - self.assertEqual(llsd.format_xml(new_note_str), good_xml) - - # Py2 LLSD(str (b"")) - # Py3 LLSD(str (unicode)) - self.assertEqual(llsd.format_xml(llsd.LLSD(new_note_str)), good_xml) - - if PY2: - bytes_xml = good_xml - else: - bytes_xml = b'0J3QvtCy0LDRjyDQt9Cw0LzQtdGC0LrQsA==' - # Py2 str (b"") - # Py3 bytes (turned into binary type by llsd) - self.assertEqual(llsd.format_xml(new_note_unicode.encode("utf-8")), bytes_xml) - - # Py2 LLSD(str (b"")) - # Py3 LLSD(bytes) (turned into binary type by llsd) - self.assertEqual(llsd.format_xml(llsd.LLSD(new_note_unicode.encode("utf-8"))), bytes_xml) - -class MapConstraints(unittest.TestCase): - ''' - Implied type conversion tests - ''' - - def test_int_map_key(self): - ''' - LLSD Map keys are supposed to be strings; convert a map with an int key - ''' - llsdmap=llsd.LLSD({5 : 'int'}) - self.assertEqual(llsd.format_xml(llsdmap), b'5int') - self.assertEqual(llsd.format_notation(llsdmap), b"{'5':'int'}") - - def test_date_map_key(self): - ''' - LLSD Map keys are supposed to be strings; convert a map with a date key - ''' - llsdmap=llsd.LLSD({datetime(2006, 2, 1, 14, 29, 53, 460000) : 'date'}) - self.assertEqual(llsd.format_xml(llsdmap), b'2006-02-01 14:29:53.460000date') - self.assertEqual(llsd.format_notation(llsdmap), b"{'2006-02-01 14:29:53.460000':'date'}") - - def test_uuid_map_key(self): - ''' - LLSD Map keys are supposed to be strings; convert a map with a uuid key - ''' - llsdmap=llsd.LLSD({uuid.UUID(int=0) : 'uuid'}) - self.assertEqual(llsd.format_xml(llsdmap), b'00000000-0000-0000-0000-000000000000uuid') - self.assertEqual(llsd.format_notation(llsdmap), b"{'00000000-0000-0000-0000-000000000000':'uuid'}") - - diff --git a/tox.ini b/tox.ini deleted file mode 100644 index 2af420c..0000000 --- a/tox.ini +++ /dev/null @@ -1,8 +0,0 @@ -[tox] -envlist = py27, py37, py38, py310 - -[testenv] -setenv = - COVERAGE_FILE = .coverage.{envname} -deps = .[dev] -commands = pytest -vv --cov=llsd --cov-report=xml:.coverage.{envname}.xml tests/