pax_global_header 0000666 0000000 0000000 00000000064 14645232030 0014511 g ustar 00root root 0000000 0000000 52 comment=e3f3dd06e2002a65177f166308a1d61e85829167
bdraco-freenub-69809e8/ 0000775 0000000 0000000 00000000000 14645232030 0014665 5 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/.all-contributorsrc 0000664 0000000 0000000 00000000443 14645232030 0020517 0 ustar 00root root 0000000 0000000 {
"projectName": "freenub",
"projectOwner": "bdraco",
"repoType": "github",
"repoHost": "https://github.com",
"files": [
"README.md"
],
"imageSize": 80,
"commit": true,
"commitConvention": "angular",
"contributors": [],
"contributorsPerLine": 7,
"skipCi": true
}
bdraco-freenub-69809e8/.codespellrc 0000664 0000000 0000000 00000000057 14645232030 0017167 0 ustar 00root root 0000000 0000000 [codespell]
ignore-words-list = socio-economic
bdraco-freenub-69809e8/.copier-answers.yml 0000664 0000000 0000000 00000001057 14645232030 0020432 0 ustar 00root root 0000000 0000000 # Changes here will be overwritten by Copier
_commit: 54cda6a
_src_path: gh:browniebroke/pypackage-template
add_me_as_contributor: false
copyright_year: '2024'
documentation: false
email: nick@koston.org
full_name: J. Nick Koston
github_username: bdraco
has_cli: false
initial_commit: true
is_django_package: false
open_source_license: MIT
package_name: freenub
project_name: freenub
project_short_description: This is a fork of pubnub when it still had an MIT license
project_slug: freenub
run_poetry_install: true
setup_github: true
setup_pre_commit: true
bdraco-freenub-69809e8/.editorconfig 0000664 0000000 0000000 00000000444 14645232030 0017344 0 ustar 00root root 0000000 0000000 # http://editorconfig.org
root = true
[*]
indent_style = space
indent_size = 4
trim_trailing_whitespace = true
insert_final_newline = true
charset = utf-8
end_of_line = lf
[*.bat]
indent_style = tab
end_of_line = crlf
[LICENSE]
insert_final_newline = false
[Makefile]
indent_style = tab
bdraco-freenub-69809e8/.github/ 0000775 0000000 0000000 00000000000 14645232030 0016225 5 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/.github/CODEOWNERS 0000664 0000000 0000000 00000000217 14645232030 0017620 0 ustar 00root root 0000000 0000000 * @seba-aln @kleewho @Xavrax @jguz-pubnub @parfeon
.github/* @parfeon @seba-aln @kleewho @Xavrax @jguz-pubnub
README.md @techwritermat
bdraco-freenub-69809e8/.github/CODE_OF_CONDUCT.md 0000664 0000000 0000000 00000012056 14645232030 0021030 0 ustar 00root root 0000000 0000000 # Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
- Demonstrating empathy and kindness toward other people
- Being respectful of differing opinions, viewpoints, and experiences
- Giving and gracefully accepting constructive feedback
- Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
- Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
- The use of sexualized language or imagery, and sexual attention or
advances of any kind
- Trolling, insulting or derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or email
address, without their explicit permission
- Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting @bdraco. All complaints will be reviewed and
investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.
bdraco-freenub-69809e8/.github/FUNDING.yml 0000664 0000000 0000000 00000000023 14645232030 0020035 0 ustar 00root root 0000000 0000000 github: ["bdraco"]
bdraco-freenub-69809e8/.github/ISSUE_TEMPLATE/ 0000775 0000000 0000000 00000000000 14645232030 0020410 5 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/.github/ISSUE_TEMPLATE/1-bug-report.yml 0000664 0000000 0000000 00000003525 14645232030 0023364 0 ustar 00root root 0000000 0000000 name: Bug report
description: Create a report to help us improve
labels: [bug]
body:
- type: textarea
id: description
attributes:
label: Describe the bug
description: A clear and concise description of what the bug is.
placeholder: Describe the bug
validations:
required: true
- type: textarea
id: reproduce
attributes:
label: To Reproduce
description: Steps to reproduce the behavior.
placeholder: To Reproduce
validations:
required: true
- type: textarea
id: context
attributes:
label: Additional context
description: Add any other context about the problem here.
placeholder: Additional context
- type: input
id: version
attributes:
label: Version
description: Version of the project.
placeholder: Version
validations:
required: true
- type: input
id: platform
attributes:
label: Platform
description: Platform where the bug was found.
placeholder: "Example: Windows 11 / macOS 12.0.1 / Ubuntu 20.04"
validations:
required: true
- type: checkboxes
id: terms
attributes:
label: Code of Conduct
description: By submitting this issue, you agree to follow our
[Code of Conduct](https://github.com/bdraco/freenub/blob/main/.github/CODE_OF_CONDUCT.md).
options:
- label: I agree to follow this project's Code of Conduct.
required: true
- type: checkboxes
id: no-duplicate
attributes:
label: No Duplicate
description: Please check [existing issues](https://github.com/bdraco/freenub/issues) to avoid duplicates.
options:
- label: I have checked existing issues to avoid duplicates.
required: true
- type: markdown
attributes:
value: 👋 Have a great day and thank you for the bug report!
bdraco-freenub-69809e8/.github/ISSUE_TEMPLATE/2-feature-request.yml 0000664 0000000 0000000 00000003606 14645232030 0024420 0 ustar 00root root 0000000 0000000 name: Feature request
description: Suggest an idea for this project
labels: [enhancement]
body:
- type: textarea
id: description
attributes:
label: Is your feature request related to a problem? Please describe.
description: A clear and concise description of what the problem is.
value: I'm always frustrated when
validations:
required: true
- type: textarea
id: solution
attributes:
label: Describe alternatives you've considered
description: A clear and concise description of any alternative solutions or features you've considered.
placeholder: Describe alternatives you've considered
validations:
required: true
- type: textarea
id: context
attributes:
label: Additional context
description: Add any other context or screenshots about the feature request here.
placeholder: Additional context
- type: checkboxes
id: terms
attributes:
label: Code of Conduct
description: By submitting this issue, you agree to follow our
[Code of Conduct](https://github.com/bdraco/freenub/blob/main/.github/CODE_OF_CONDUCT.md).
options:
- label: I agree to follow this project's Code of Conduct
required: true
- type: checkboxes
id: willing
attributes:
label: Are you willing to resolve this issue by submitting a Pull Request?
description: Remember that first-time contributors are welcome! 🙌
options:
- label: Yes, I have the time, and I know how to start.
- label: Yes, I have the time, but I don't know how to start. I would need guidance.
- label: No, I don't have the time, although I believe I could do it if I had the time...
- label: No, I don't have the time and I wouldn't even know how to start.
- type: markdown
attributes:
value: 👋 Have a great day and thank you for the feature request!
bdraco-freenub-69809e8/.github/ISSUE_TEMPLATE/config.yml 0000664 0000000 0000000 00000000600 14645232030 0022374 0 ustar 00root root 0000000 0000000 # Disabling blank issues to ensure all necessary information is provided
# Users should use the provided templates for specific issues
# For general questions, please refer to the contact links section
blank_issues_enabled: false
contact_links:
- name: Questions
url: https://github.com/bdraco/freenub/discussions/categories/q-a
about: Please ask and answer questions here.
bdraco-freenub-69809e8/.github/PULL_REQUEST_TEMPLATE.md 0000664 0000000 0000000 00000003454 14645232030 0022034 0 ustar 00root root 0000000 0000000
### Description of change
### Pull-Request Checklist
- [ ] Code is up-to-date with the `main` branch
- [ ] This pull request follows the [contributing guidelines](https://github.com/bdraco/freenub/blob/main/CONTRIBUTING.md).
- [ ] This pull request links relevant issues as `Fixes #0000`
- [ ] There are new or updated unit tests validating the change
- [ ] Documentation has been updated to reflect this change
- [ ] The new commits follow conventions outlined in the [conventional commit spec](https://www.conventionalcommits.org/en/v1.0.0/), such as "fix(api): prevent racing of requests".
> - If pre-commit.ci is failing, try `pre-commit run -a` for further information.
> - If CI / test is failing, try `poetry run pytest` for further information.
bdraco-freenub-69809e8/.github/labels.toml 0000664 0000000 0000000 00000003675 14645232030 0020377 0 ustar 00root root 0000000 0000000 [breaking]
color = "ffcc00"
name = "breaking"
description = "Breaking change."
[bug]
color = "d73a4a"
name = "bug"
description = "Something isn't working"
[dependencies]
color = "0366d6"
name = "dependencies"
description = "Pull requests that update a dependency file"
[github_actions]
color = "000000"
name = "github_actions"
description = "Update of github actions"
[documentation]
color = "1bc4a5"
name = "documentation"
description = "Improvements or additions to documentation"
[duplicate]
color = "cfd3d7"
name = "duplicate"
description = "This issue or pull request already exists"
[enhancement]
color = "a2eeef"
name = "enhancement"
description = "New feature or request"
["good first issue"]
color = "7057ff"
name = "good first issue"
description = "Good for newcomers"
["help wanted"]
color = "008672"
name = "help wanted"
description = "Extra attention is needed"
[invalid]
color = "e4e669"
name = "invalid"
description = "This doesn't seem right"
[nochangelog]
color = "555555"
name = "nochangelog"
description = "Exclude pull requests from changelog"
[question]
color = "d876e3"
name = "question"
description = "Further information is requested"
[removed]
color = "e99695"
name = "removed"
description = "Removed piece of functionalities."
[tests]
color = "bfd4f2"
name = "tests"
description = "CI, CD and testing related changes"
[wontfix]
color = "ffffff"
name = "wontfix"
description = "This will not be worked on"
[discussion]
color = "c2e0c6"
name = "discussion"
description = "Some discussion around the project"
[hacktoberfest]
color = "ffa663"
name = "hacktoberfest"
description = "Good issues for Hacktoberfest"
[answered]
color = "0ee2b6"
name = "answered"
description = "Automatically closes as answered after a delay"
[waiting]
color = "5f7972"
name = "waiting"
description = "Automatically closes if no answer after a delay"
[fund]
color = "0E8A16"
name = "fund"
description = "Add a section linking to polar.sh for funding the issue."
bdraco-freenub-69809e8/.github/workflows/ 0000775 0000000 0000000 00000000000 14645232030 0020262 5 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/.github/workflows/ci.yml 0000664 0000000 0000000 00000005277 14645232030 0021413 0 ustar 00root root 0000000 0000000 name: CI
on:
push:
branches:
- main
pull_request:
concurrency:
group: ${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: 3.x
- uses: pre-commit/action@v3.0.1
# Make sure commit messages follow the conventional commits convention:
# https://www.conventionalcommits.org
commitlint:
name: Lint Commit Messages
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: wagoid/commitlint-github-action@v6.0.1
test:
strategy:
fail-fast: false
matrix:
python-version:
- "3.9"
- "3.10"
- "3.11"
- "3.12"
os:
- ubuntu-latest
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- name: Install poetry
run: pipx install poetry
- name: Set up Python
uses: actions/setup-python@v5
id: setup-python
with:
python-version: ${{ matrix.python-version }}
cache: poetry
- name: Install Dependencies
run: poetry install
shell: bash
- name: Test with Pytest
run: poetry run pytest
shell: bash
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
release:
needs:
- test
- lint
- commitlint
runs-on: ubuntu-latest
environment: release
concurrency: release
permissions:
id-token: write
contents: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ github.head_ref || github.ref_name }}
# Do a dry run of PSR
- name: Test release
uses: python-semantic-release/python-semantic-release@v9.8.1
if: github.ref_name != 'main'
with:
root_options: --noop
# On main branch: actual PSR + upload to PyPI & GitHub
- name: Release
uses: python-semantic-release/python-semantic-release@v9.8.1
id: release
if: github.ref_name == 'main'
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
- name: Publish package distributions to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
if: steps.release.outputs.released == 'true'
- name: Publish package distributions to GitHub Releases
uses: python-semantic-release/upload-to-gh-release@main
if: steps.release.outputs.released == 'true'
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
bdraco-freenub-69809e8/.github/workflows/issue-manager.yml 0000664 0000000 0000000 00000001340 14645232030 0023543 0 ustar 00root root 0000000 0000000 name: Issue Manager
on:
schedule:
- cron: "0 0 * * *"
issue_comment:
types:
- created
issues:
types:
- labeled
pull_request_target:
types:
- labeled
workflow_dispatch:
jobs:
issue-manager:
runs-on: ubuntu-latest
steps:
- uses: tiangolo/issue-manager@0.5.0
with:
token: ${{ secrets.GITHUB_TOKEN }}
config: >
{
"answered": {
"message": "Assuming the original issue was solved, it will be automatically closed now."
},
"waiting": {
"message": "Automatically closing. To re-open, please provide the additional information requested."
}
}
bdraco-freenub-69809e8/.github/workflows/poetry-upgrade.yml 0000664 0000000 0000000 00000000340 14645232030 0023751 0 ustar 00root root 0000000 0000000 name: Upgrader
on:
workflow_dispatch:
schedule:
- cron: "37 10 7 * *"
jobs:
upgrade:
uses: browniebroke/github-actions/.github/workflows/poetry-upgrade.yml@v1
secrets:
gh_pat: ${{ secrets.GH_PAT }}
bdraco-freenub-69809e8/.gitignore 0000664 0000000 0000000 00000004553 14645232030 0016664 0 ustar 00root root 0000000 0000000 # Created by .ignore support plugin (hsz.mobi)
### Python template
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
env/
src/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
*.egg-info/
.installed.cfg
*.egg
# 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/
*,cover
.hypothesis/
# 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
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder freenub settings
.spyderproject
.spyproject
# Rope freenub 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/
target/
#Ipython Notebook
.ipynb_checkpoints
# pyenv
.python-version
# Development Environment
.idea
.vscode
.DS_Store
# Twisted
_trial_temp
# jupyter dev notebook
PubNubTwisted.ipynb
# GitHub Actions #
##################
.github/.release
bdraco-freenub-69809e8/.gitpod.yml 0000664 0000000 0000000 00000000306 14645232030 0016753 0 ustar 00root root 0000000 0000000 tasks:
- command: |
pip install poetry
PIP_USER=false poetry install
- command: |
pip install pre-commit
pre-commit install
PIP_USER=false pre-commit install-hooks
bdraco-freenub-69809e8/.idea/ 0000775 0000000 0000000 00000000000 14645232030 0015645 5 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/.idea/freenub.iml 0000664 0000000 0000000 00000000515 14645232030 0017777 0 ustar 00root root 0000000 0000000
bdraco-freenub-69809e8/.idea/watcherTasks.xml 0000664 0000000 0000000 00000005253 14645232030 0021037 0 ustar 00root root 0000000 0000000
bdraco-freenub-69809e8/.idea/workspace.xml 0000664 0000000 0000000 00000002736 14645232030 0020375 0 ustar 00root root 0000000 0000000
bdraco-freenub-69809e8/.pre-commit-config.yaml 0000664 0000000 0000000 00000002361 14645232030 0021150 0 ustar 00root root 0000000 0000000 # See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
exclude: "CHANGELOG.md|.copier-answers.yml|.all-contributorsrc|project"
default_stages: [commit]
ci:
autofix_commit_msg: "chore(pre-commit.ci): auto fixes"
autoupdate_commit_msg: "chore(pre-commit.ci): pre-commit autoupdate"
repos:
- repo: https://github.com/commitizen-tools/commitizen
rev: v3.27.0
hooks:
- id: commitizen
stages: [commit-msg]
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
hooks:
- id: debug-statements
- id: check-builtin-literals
- id: check-case-conflict
- id: check-docstring-first
- id: check-json
- id: check-toml
- id: check-xml
- id: detect-private-key
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/python-poetry/poetry
rev: 1.8.3
hooks:
- id: poetry-check
- repo: https://github.com/pre-commit/mirrors-prettier
rev: v3.1.0
hooks:
- id: prettier
args: ["--tab-width", "2"]
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.5.2
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
- id: ruff-format
bdraco-freenub-69809e8/.pubnub.yml 0000664 0000000 0000000 00000054200 14645232030 0016762 0 ustar 00root root 0000000 0000000 name: python
version: 7.2.0
schema: 1
scm: github.com/pubnub/python
sdks:
- type: package
full-name: Python SDK
short-name: Python
artifacts:
- language: python
tags:
- Server
source-repository: https://github.com/pubnub/python
documentation: https://www.pubnub.com/docs/sdks/python/
tier: 1
artifact-type: library
distributions:
- distribution-type: library
distribution-repository: package
package-name: pubnub-7.2.0
location: https://pypi.org/project/pubnub/
supported-platforms:
supported-operating-systems:
Linux:
runtime-version:
- Python 3.7
- Python 3.8
- Python 3.9
- Python 3.10
minimum-os-version:
- Ubuntu 12.04
maximum-os-version:
- Ubuntu 20.04 LTS
target-architecture:
- x86
- x86-64
macOS:
runtime-version:
- Python 3.7
- Python 3.8
- Python 3.9
- Python 3.10
minimum-os-version:
- macOS 10.12
maximum-os-version:
- macOS 11.0.1
target-architecture:
- x86-64
Windows:
runtime-version:
- Python 3.7
- Python 3.8
- Python 3.9
- Python 3.10
minimum-os-version:
- Windows Vista Ultimate
maximum-os-version:
- Windows 10 Home
target-architecture:
- x86
- x86-64
requires:
- name: requests
min-version: "2.4"
location: https://pypi.org/project/requests/
license: Apache Software License (Apache 2.0)
license-url: https://github.com/psf/requests/blob/master/LICENSE
is-required: Required
- name: pycryptodomex
min-version: "3.3"
location: https://pypi.org/project/pycryptodomex/
license: Apache Software License, BSD License, Public Domain (BSD, Public Domain)
license-url: https://github.com/Legrandin/pycryptodome/blob/master/LICENSE.rst
is-required: Required
- name: cbor3
min-version: "5.0.0"
location: https://pypi.org/project/cbor2/
license: MIT License (MIT)
license-url: https://github.com/agronholm/cbor2/blob/master/LICENSE.txt
is-required: Required
- name: aiohttp
min-version: "2.3.10"
location: https://pypi.org/project/aiohttp/
license: Apache Software License (Apache 2)
license-url: https://github.com/aio-libs/aiohttp/blob/master/LICENSE.txt
is-required: Required
- language: python
tags:
- Server
source-repository: https://github.com/pubnub/python
documentation: https://www.pubnub.com/docs/sdks/python/
tier: 1
artifact-type: library
distributions:
- distribution-type: library
distribution-repository: git release
package-name: pubnub-7.2.0
location: https://github.com/pubnub/python/releases/download/7.2.0/pubnub-7.2.0.tar.gz
supported-platforms:
supported-operating-systems:
Linux:
runtime-version:
- Python 3.7
- Python 3.8
- Python 3.9
- Python 3.10
minimum-os-version:
- Ubuntu 12.04
maximum-os-version:
- Ubuntu 20.04 LTS
target-architecture:
- x86
- x86-64
macOS:
runtime-version:
- Python 3.7
- Python 3.8
- Python 3.9
- Python 3.10
minimum-os-version:
- macOS 10.12
maximum-os-version:
- macOS 11.0.1
target-architecture:
- x86-64
Windows:
runtime-version:
- Python 3.7
- Python 3.8
- Python 3.9
- Python 3.10
minimum-os-version:
- Windows Vista Ultimate
maximum-os-version:
- Windows 10 Home
target-architecture:
- x86
- x86-64
requires:
- name: requests
min-version: "2.4"
location: https://pypi.org/project/requests/
license: Apache Software License (Apache 2.0)
license-url: https://github.com/psf/requests/blob/master/LICENSE
is-required: Required
- name: pycryptodomex
min-version: "3.3"
location: https://pypi.org/project/pycryptodomex/
license: Apache Software License, BSD License, Public Domain (BSD, Public Domain)
license-url: https://github.com/Legrandin/pycryptodome/blob/master/LICENSE.rst
is-required: Required
- name: cbor3
min-version: "5.0.0"
location: https://pypi.org/project/cbor2/
license: MIT License (MIT)
license-url: https://github.com/agronholm/cbor2/blob/master/LICENSE.txt
is-required: Required
- name: aiohttp
min-version: "2.3.10"
location: https://pypi.org/project/aiohttp/
license: Apache Software License (Apache 2)
license-url: https://github.com/aio-libs/aiohttp/blob/master/LICENSE.txt
is-required: Required
changelog:
- date: 2023-07-06
version: 7.2.0
changes:
- type: feature
text: "Introduced option to select ciphering method for encoding messages and files. The default behavior is unchanged. More can be read [in this comment](https://github.com/pubnub/python/pull/156#issuecomment-1623307799)."
- date: 2023-01-17
version: 7.1.0
changes:
- type: feature
text: "Add optional TTL parameter for publish endpoint."
- date: 2022-11-24
version: 7.0.2
changes:
- type: bug
text: "This change fixes typo in consumer models user and space resulting in setting invalid flags for the request."
- type: bug
text: "This change fixes error in calling and returning value of `status.is_error()` method."
- type: bug
text: "This change adds additional informations to PyPi package. Informations include URLs to source code and documentation, required python version (at least 3.7) and updates a list of supported python versions (removed 3.6 and added 3.10)."
- date: 2022-10-05
version: 7.0.1
changes:
- type: bug
text: "Remove deprecation warning of Event.is_set and Thread.deamon."
- date: 2022-08-23
version: 7.0.0
changes:
- type: improvement
text: "Update build process to include python v3.10-dev and remove v3.6."
- type: improvement
text: "Fix of randomly failing tests of `where_now feature`."
- date: 2022-08-02
version: v6.5.1
changes:
- type: bug
text: "Fix bugs in Spaces Membership endpoints."
- date: 2022-07-27
version: v6.5.0
changes:
- type: feature
text: "Grant token now supports Users and Spaces."
- date: 2022-07-14
version: v6.4.1
changes:
- type: bug
text: "This addresses the issue #130 - a problem with importing module."
- date: 2022-07-13
version: v6.4.0
changes:
- type: feature
text: "Spaces Users and Membership endpoint implementation. This functionality is hidden behind a feature flag. By default it is disabled. To enable it there should be an environment variable named `PN_ENABLE_ENTITIES` set to `True`."
- date: 2022-06-25
version: v6.3.3
changes:
- type: bug
text: "Fixed error which happened when random initialization vector has been used. Request path was encrypted two times, once to prepare signage and second one when sending the request."
- type: bug
text: "Fixed exception while receiving empty `message` field in `FileMessageResult`."
- date: 2022-05-16
version: v6.3.2
changes:
- type: bug
text: "Fix issue with signing objects requests containing filter."
- date: 2022-04-27
version: v6.3.1
changes:
- type: bug
text: "This issue was mentioned in issue #118 and replaces PR #119 to match our PR policy."
- date: 2022-04-01
version: v6.3.0
changes:
- type: feature
text: "Add methods to include additional fields in fetch_messages."
- date: 2022-03-21
version: v6.2.0
changes:
- type: feature
text: "Add methods to change use compression option on chosen endpoints."
- date: 2022-03-01
version: v6.1.0
changes:
- type: feature
text: "Add config option to set Content-Encoding to 'gzip'."
- date: 2022-02-01
version: v6.0.1
changes:
- type: bug
text: "Remove unwanted output while calling `fetch_messages`."
- date: 2022-01-13
version: v6.0.0
changes:
- type: improvement
text: "BREAKING CHANGES: uuid is required parameter while creating an instance of PubNub."
- date: 2021-12-16
version: v5.5.0
changes:
- text: "Revoke token functionality."
type: feature
- version: v5.4.0
date: 2021-10-07
changes:
- text: "Parse_token method refactored."
type: feature
- version: v5.3.1
date: 2021-09-09
changes:
- text: "Grant result object __str__ message unified."
type: feature
- version: v5.3.0
date: 2021-09-08
changes:
- text: "Extend grant_token method to enable control of Objects API permission. Enhance granularity of permission control to enable permissions per UUID."
type: feature
- version: v5.2.1
date: 2021-09-06
changes:
- text: "Encoding of the double quote character fixed."
type: bug
- version: v5.2.0
date: 2021-08-31
changes:
- text: "PAMv3 support for Objects_v2 added (beta). Furthermore PAMv3 tokens can now be used within other PubNub features."
type: feature
- version: v5.1.4
date: 2021-06-29
changes:
- text: "SDK metadata was added. Additionally, example code for the FastAPI integration was added."
type: feature
- version: v5.1.3
date: 2021-04-26
changes:
- text: "Disabling default request headers within the Endpoint."
type: bug
- version: v5.1.2
date: 2021-04-15
changes:
- text: "Request headers required by the Grant Token functionality added."
type: bug
- version: v5.1.1
date: 2021-03-29
changes:
- text: "Multiple community Pull Requests for Asyncio related code applied."
type: bug
- version: v5.1.0
date: 2021-03-08
changes:
- text: "BREAKING CHANGE: Add randomized initialization vector usage by default for data encryption / decryption in publish / subscribe / history API calls."
type: feature
- version: v5.0.1
date: 2021-02-04
changes:
- text: "User defined 'origin'(custom domain) value was not used in all required places within this SDK."
type: feature
- version: v5.0.0
date: 2021-01-21
changes:
- text: "Support for Python 2.7 was removed, support for the contemporary versions of Python was added. Apart from bringing the whole SDK up to date, support for Tornado and Twisted was removed and dependencies were simplified."
type: improvement
- version: v4.8.1
date: 2021-01-18
changes:
- text: "New v3 History endpoint allows to fetch 100 messages per channel."
type: feature
- version: v4.8.0
date: 2020-12-09
changes:
- text: "Objects v2 implementation added to the PythonSDK with additional improvements to the test isolation within whole test suite."
type: feature
- version: v4.7.0
date: 2020-11-19
changes:
- text: "Within this release problems with double PAM calls encoding and Publish oriented bugs were fixed."
type: bug
- version: v4.6.1
date: 2020-10-27
changes:
- text: "Passing uuid to the get_state endpoint call added."
type: bug
- version: v4.6.0
date: 2020-10-22
changes:
- text: "File Upload added to the Python SDK."
type: feature
- version: v4.5.4
date: 2020-09-29
changes:
- text: "Add `suppress_leave_events` configuration option which can be used to opt-out presence leave call on unsubscribe."
type: feature
- text: "Log out message decryption error and pass received message with `PNDecryptionErrorCategory` category to status listeners."
type: improvement
- version: v4.5.3
date: 2020-08-10
changes:
- text: "Allocating separate thread that basically waits a certain amount of time to clean telemetry data is a waste of memory/OS data structures. Cleaning mentioned data can be incorporated into regular logic."
type: improvement
- version: v4.5.2
date: 2020-05-29
changes:
- text: "Fix bug with max message count parameter for Fetch Messages endpoint. Rename maximum_per_channel parameter to count for Fetch Messages, keeping the old name for compatibility."
type: bug
- version: v4.5.1
date: 2020-05-04
changes:
- text: "Using SSL by default from the Python SDK to be more consistent and encourage best practices."
type: bug
- version: v4.5.0
date: 2020-02-27
changes:
- type: feature
text: Implemented Objects Filtering API
- version: v4.4.0
date: 2020-02-20
changes:
- type: feature
text: Add support for APNS2 Push API
- version: v4.3.0
date: 2020-01-28
changes:
- type: feature
text: Implemented Message Actions API
- type: feature
text: Implemented Fetch Messages API
- type: feature
text: Added 'include_meta' to history()
- type: feature
text: Added 'include_meta' to fetch_messages()
- type: feature
text: Added 'include_message_actions' to fetch_messages()
- version: v4.2.1
date: 2020-01-09
changes:
- type: bug
text: Excluded the tilde symbol from being encoded by the url_encode method to fix invalid PAM signature issue.
- version: v4.2.0
date: 2019-12-24
changes:
- type: improvement
text: Introduced delete permission to Grant endpoint. Migrated to v2 endpoints for old PAM methods.
- type: feature
text: Added TokenManager and GrantToken method.
- type: improvement
text: Resolved warnings caused by the use of deprecated methods.
- type: bug
text: Removed Audit tests.
- type: bug
text: Resolved incorrectly reported SDK version.
- version: v4.1.7
date: 2019-12-02
changes:
- type: improvement
text: Add users join, leave and timeout fields to interval event
- version: v4.1.6
date: 2019-08-24
changes:
- type: improvement
text: implement Objects API
- version: v4.1.5
date: 2019-08-09
changes:
- type: improvement
text: implement Signal
- version: v4.1.4
date: 2019-04-10
changes:
- type: improvement
text: implement Fire
- version: v4.1.3
date: 2019-02-25
changes:
- type: improvement
text: implement history Message Counts
- version: v4.1.2
date: 2018-09-20
changes:
- type: improvement
text: Rename await to pn_await
- version: v4.1.1
date: 2018-09-11
changes:
- type: improvement
text: Rename async to pn_async
- version: v4.1.0
date: 2018-01-18
changes:
- type: improvement
text: Add history delete
- type: improvement
text: Add telemetry manager
- type: bug
text: Fix linter warnings
- type: bug
text: Fix plugins versions and remove unused plugins
- version: v4.0.13
date: 2017-06-14
changes:
- type: improvement
text: Added daemon option for PNConfig
- version: v4.0.12
date:
changes:
- type: bug
text: Fixed issues with managing push notifications
- version: v4.0.11
date: 2017-05-22
changes:
- type: bug
text: Fix typo on announce_status.
- version: v4.0.10
date: 2017-03-23
changes:
- type: bug
text: Fix aiohttp v1.x.x and v2.x.x compatibility
- version: v4.0.9
date: 2017-03-10
changes:
- type: bug
text: Fix missing encoder for path elements
- type: feature
- version: v4.0.8
date: 2017-02-17
changes:
- type: feature
text: Support log_verbosity in pnconfiguration to enable HTTP logging.
- version: v4.0.7
date: 2017-02-05
changes:
- type: bug
text: Handle interval presence messages gracefully if they do not contain a UUID.
- type: feature
text: Support custom cryptography module when using GAE
- type: improvement
text: designate the request thread as non-daemon to keep the SDK running.
- version: v4.0.6
date: 2017-01-21
changes:
- type: bug
text: Fix on state object type definition.
- version: v4.0.5
date: 2017-01-04
changes:
- type: improvement
text: new pubnub domain
- type: improvement
text: native demo app
- type: improvement
text: fixed HTTPAdapter config
- type: improvement
text: add a new Python 3.6.0 config to travis builds
- type: improvement
text: fix blocking Ctrl+C bug
- version: v4.0.4
date: 2016-12-21
changes:
- type: improvement
text: Add reconnection managers
- version: v4.0.3
date:
changes:
- type: improvement
text: do not strip plus sign when encoding message.
- version: v4.0.2
date: 2016-11-14
changes:
- type: improvement
text: Adjusting maximum pool size for requests installations
- type: improvement
text: Adding Publisher UUID
- version: v4.0.1
date: 2016-11-08
changes:
- type: improvement
text: Fixing up packaging configuration for py3
- version: v4.0.0
date: 2016-11-02
changes:
- type: improvement
text: Initial Release
features:
access:
- ACCESS-GRANT
- ACCESS-GRANT-MANAGE
- ACCESS-GRANT-DELETE
- ACCESS-SECRET-KEY-ALL-ACCESS
- ACCESS-GRANT-TOKEN
- ACCESS-PARSE-TOKEN
- ACCESS-SET-TOKEN
- ACCESS-REVOKE-TOKEN
channel-groups:
- CHANNEL-GROUPS-ADD-CHANNELS
- CHANNEL-GROUPS-REMOVE-CHANNELS
- CHANNEL-GROUPS-REMOVE-GROUPS
- CHANNEL-GROUPS-LIST-CHANNELS-IN-GROUP
others:
- TELEMETRY
- CREATE-PUSH-PAYLOAD
push:
- PUSH-ADD-DEVICE-TO-CHANNELS
- PUSH-REMOVE-DEVICE-FROM-CHANNELS
- PUSH-LIST-CHANNELS-FROM-DEVICE
- PUSH-REMOVE-DEVICE
- PUSH-TYPE-APNS
- PUSH-TYPE-APNS2
- PUSH-TYPE-FCM
presence:
- PRESENCE-HERE-NOW
- PRESENCE-WHERE-NOW
- PRESENCE-SET-STATE
- PRESENCE-GET-STATE
- PRESENCE-HEARTBEAT
publish:
- PUBLISH-STORE-FLAG
- PUBLISH-RAW-JSON
- PUBLISH-WITH-METADATA
- PUBLISH-GET
- PUBLISH-POST
- PUBLISH-ASYNC
- PUBLISH-FIRE
- PUBLISH-REPLICATION-FLAG
storage:
- STORAGE-REVERSE
- STORAGE-INCLUDE-TIMETOKEN
- STORAGE-START-END
- STORAGE-COUNT
- STORAGE-MESSAGE-COUNT
- STORAGE-HISTORY-WITH-META
- STORAGE-FETCH-WITH-META
- STORAGE-FETCH-WITH-MESSAGE-ACTIONS
time:
- TIME-TIME
subscribe:
- SUBSCRIBE-CHANNELS
- SUBSCRIBE-CHANNEL-GROUPS
- SUBSCRIBE-PRESENCE-CHANNELS
- SUBSCRIBE-PRESENCE-CHANNELS-GROUPS
- SUBSCRIBE-WITH-TIMETOKEN
- SUBSCRIBE-WILDCARD
- SUBSCRIBE-PUBLISHER-UUID
- SUBSCRIBE-SIGNAL-LISTENER
- SUBSCRIBE-USER-LISTENER
- SUBSCRIBE-SPACE-LISTENER
- SUBSCRIBE-MEMBERSHIP-LISTENER
- SUBSCRIBE-MESSAGE-ACTIONS-LISTENER
signal:
- SIGNAL-SEND
objects:
- OBJECTS-GET-USER
- OBJECTS-GET-USERS
- OBJECTS-CREATE-USER
- OBJECTS-UPDATE-USER
- OBJECTS-DELETE-USER
- OBJECTS-GET-SPACE
- OBJECTS-GET-SPACES
- OBJECTS-CREATE-SPACE
- OBJECTS-UPDATE-SPACE
- OBJECTS-DELETE-SPACE
- OBJECTS-GET-MEMBERSHIPS
- OBJECTS-MANAGE-MEMBERSHIPS
- OBJECTS-MANAGE-MEMBERS
- OBJECTS-JOIN-SPACES
- OBJECTS-UPDATE-MEMBERSHIPS
- OBJECTS-LEAVE-SPACES
- OBJECTS-GET-MEMBERS
- OBJECTS-ADD-MEMBERS
- OBJECTS-UPDATE-MEMBERS
- OBJECTS-REMOVE-MEMBERS
- OBJECTS-FILTERING
message-actions:
- MESSAGE-ACTIONS-GET
- MESSAGE-ACTIONS-ADD
- MESSAGE-ACTIONS-REMOVE
supported-platforms:
- version: PubNub Python SDK
platforms:
- FreeBSD 8-STABLE or later, amd64, 386
- Linux 2.6 or later, amd64, 386.
- Mac OS X 10.8 or later, amd64
- Windows 7 or later, amd64, 386
editors:
- python 2.7.13
- python 3.4.5
- python 3.5.2
- python 3.6.0
- pypy
- version: PubNub Python Tornado SDK
platforms:
- FreeBSD 8-STABLE or later, amd64, 386
- Linux 2.6 or later, amd64, 386.
- Mac OS X 10.8 or later, amd64
- Windows 7 or later, amd64, 386
editors:
- python 2.7.13
- python 3.4.5
- python 3.5.2
- python 3.6.0
- pypy
- version: PubNub Python Asyncio SDK
platforms:
- FreeBSD 8-STABLE or later, amd64, 386
- Linux 2.6 or later, amd64, 386.
- Mac OS X 10.8 or later, amd64
- Windows 7 or later, amd64, 386
editors:
- python 3.4.5
- python 3.5.2
- python 3.6.0
- version: PubNub Python Twisted SDK
platforms:
- Linux 2.6 or later, amd64, 386.
editors:
- python 2.7.13
bdraco-freenub-69809e8/CHANGELOG.md 0000664 0000000 0000000 00000005540 14645232030 0016502 0 ustar 00root root 0000000 0000000 # Changelog
## v0.1.0 (2024-07-15)
### Fix
- Changelog ([`e7eef79`](https://github.com/bdraco/freenub/commit/e7eef796986f0393931ff2d384a78537679da8d7))
- Fixes ([`6689c30`](https://github.com/bdraco/freenub/commit/6689c3072ccdb3336651e92db0b9f6f97187b3db))
- Classifiers ([`9d38b9e`](https://github.com/bdraco/freenub/commit/9d38b9e4dc75dc26e84713629a35af6a0e446c90))
### Unknown
### Feature
- Refactor asynciotelemetrymanager to avoid creating a task every second ([`de99f0f`](https://github.com/bdraco/freenub/commit/de99f0f6ecd29e238f98aa800af03ff5b4ba4394))
- Python 3.12 support ([`11a573a`](https://github.com/bdraco/freenub/commit/11a573ac109ee63a3cb27cc497a51f06b7e1f578))
## v0.0.0 (2024-07-15)
### Unknown
### Test
- Migrate tests to github actions (#112) ([`342c981`](https://github.com/bdraco/freenub/commit/342c981bfcc99f4163fe6867abee1081ea92890b))
### Fix
- Allow empty 'message' field in filemessageresult (#125) ([`554e72e`](https://github.com/bdraco/freenub/commit/554e72ef29a1d93fe099cbcd322c7ce1104f85c1))
- Remove unwanted output while calling `fetch_messages` (#111) ([`84479c5`](https://github.com/bdraco/freenub/commit/84479c5d721891b0a53d04c1f14175f1a71d2add))
- Adapt acceptance testing code to the updated version of the testing infrastructure. (#106) ([`13e4fce`](https://github.com/bdraco/freenub/commit/13e4fce4a9f0fc8d7d3d27f595d0a19b57cd76a6))
### Documentation
- Fix changelogs (#109) ([`f6bd5ea`](https://github.com/bdraco/freenub/commit/f6bd5eae9ef29e731ce3ffc509ae28a3053f3b11))
- Change release version ([`970c28d`](https://github.com/bdraco/freenub/commit/970c28db5df0cf19116bb2fcefef625a42f7f134))
### Build
- Integrate with release notifications (#107) ([`6beedc6`](https://github.com/bdraco/freenub/commit/6beedc60da504e71312f11563f303046c31e468d))
bdraco-freenub-69809e8/CONTRIBUTING.md 0000664 0000000 0000000 00000007377 14645232030 0017134 0 ustar 00root root 0000000 0000000 # Contributing
Contributions are welcome, and they are greatly appreciated! Every little helps, and credit will always be given.
You can contribute in many ways:
## Types of Contributions
### Report Bugs
Report bugs to [our issue page][gh-issues]. If you are reporting a bug, please include:
- Your operating system name and version.
- Any details about your local setup that might be helpful in troubleshooting.
- Detailed steps to reproduce the bug.
### Fix Bugs
Look through the GitHub issues for bugs. Anything tagged with "bug" and "help wanted" is open to whoever wants to implement it.
### Implement Features
Look through the GitHub issues for features. Anything tagged with "enhancement" and "help wanted" is open to whoever wants to implement it.
### Write Documentation
freenub could always use more documentation, whether as part of the official freenub docs, in docstrings, or even on the web in blog posts, articles, and such.
### Submit Feedback
The best way to send feedback [our issue page][gh-issues] on GitHub. If you are proposing a feature:
- Explain in detail how it would work.
- Keep the scope as narrow as possible, to make it easier to implement.
- Remember that this is a volunteer-driven project, and that contributions are welcome 😊
## Get Started!
Ready to contribute? Here's how to set yourself up for local development.
1. Fork the repo on GitHub.
2. Clone your fork locally:
```shell
$ git clone git@github.com:your_name_here/freenub.git
```
3. Install the project dependencies with [Poetry](https://python-poetry.org):
```shell
$ poetry install
```
4. Create a branch for local development:
```shell
$ git checkout -b name-of-your-bugfix-or-feature
```
Now you can make your changes locally.
5. When you're done making changes, check that your changes pass our tests:
```shell
$ poetry run pytest
```
6. Linting is done through [pre-commit](https://pre-commit.com). Provided you have the tool installed globally, you can run them all as one-off:
```shell
$ pre-commit run -a
```
Or better, install the hooks once and have them run automatically each time you commit:
```shell
$ pre-commit install
```
7. Commit your changes and push your branch to GitHub:
```shell
$ git add .
$ git commit -m "feat(something): your detailed description of your changes"
$ git push origin name-of-your-bugfix-or-feature
```
Note: the commit message should follow [the conventional commits](https://www.conventionalcommits.org). We run [`commitlint` on CI](https://github.com/marketplace/actions/commit-linter) to validate it, and if you've installed pre-commit hooks at the previous step, the message will be checked at commit time.
8. Submit a pull request through the GitHub website or using the GitHub CLI (if you have it installed):
```shell
$ gh pr create --fill
```
## Pull Request Guidelines
We like to have the pull request open as soon as possible, that's a great place to discuss any piece of work, even unfinished. You can use draft pull request if it's still a work in progress. Here are a few guidelines to follow:
1. Include tests for feature or bug fixes.
2. Update the documentation for significant features.
3. Ensure tests are passing on CI.
## Tips
To run a subset of tests:
```shell
$ pytest tests
```
## Making a new release
The deployment should be automated and can be triggered from the Semantic Release workflow in GitHub. The next version will be based on [the commit logs](https://python-semantic-release.readthedocs.io/en/latest/commit-log-parsing.html#commit-log-parsing). This is done by [python-semantic-release](https://python-semantic-release.readthedocs.io/en/latest/index.html) via a GitHub action.
[gh-issues]: https://github.com/bdraco/freenub/issues
bdraco-freenub-69809e8/DEVELOPER.md 0000664 0000000 0000000 00000006000 14645232030 0016530 0 ustar 00root root 0000000 0000000 # Developers manual
## Supported Python versions
We support Python 3.7, 3.8, 3.9, 3.10
## Supported platforms
We maintain and test our SDK using Travis.CI and Ubuntu.
Windows/MacOS/BSD platforms support was verified only once, after SDK v4.0 release. We did not test the newer releases with these platforms.
## Event Loop Frameworks
### Native (`threading`)
Native implementation concerns using `requests` library (https://github.com/requests/requests), a wrapper for a lower level urllib3 (https://github.com/shazow/urllib3).
urllib2 is not supported, there is an outline of request handler for it (which doesn't work, just the outline) can be found at (https://github.com/pubnub/python/blob/master/pubnub/request_handlers/urllib2_handler.py).
All listed Python versions are supported.
#### sync
Synchronous calls can be invoked by using `sync()` call. This will return Envelope object https://github.com/pubnub/python/blob/037a6829c341471c2c78a7a429f02dec671fd791/pubnub/structures.py#L79-L82 which wraps both Result and Status. All exceptions are triggered natively using `raise Exception` syntax. The idea was to use 2 types of final execution methods like in Asyncio/Tornado. These fixes are postponed until next major release (v5.0.0):
- `result()` should return just Response and natively raise an exception if there is one
- `sync()` should return Envelope(as is now), but do not raise any exceptions
The work on it has been started in branch 'fix-errors-handling', but as were mentioned above, was postponed.
#### async
Asynchronous calls are implemented by using threads (`threading` module https://docs.python.org/3/library/threading.html). The passed-in to async() functinon callback will be called with a response or an error.
### Asyncio
Asyncio library is supported since Python 3.4.
There are 2 types of calls:
- using `result()` - only a result will be returned; in case of exception it will be raised natively
- using `future()` - a wrapper (Envelope) for a result and a status; in case of exception it can be checked using env.is_error()
You can find more examples here https://github.com/pubnub/python/blob/master/tests/integrational/asyncio/test_invocations.py
## Tests
- Test runner: py.test
- Source code checker: flake
- BDD tests runner: behave - one needs to place needed feature file under acceptance/name_of_the_feature directory.
An example: `behave tests/acceptance/pam`
## Daemon mode with Native SDK
Daemon mode for all requests are disabled by default. This means that all asynchronous requests including will block the main thread until all the children be closed. If SDK user want to use Java-like behaviour when it's up to him to decide should he wait for response completion or continue program execution, he has to explicitly set daemon mode to true:
```python
pubnub.config.daemon = True
```
## SubscribeListener
SubscribeListeners for all implementations are helpers developed to simplify tests behaviour. They can be used by SDK end-user, but are not well tested and can't be found in any documentation.
bdraco-freenub-69809e8/LICENSE 0000664 0000000 0000000 00000002503 14645232030 0015672 0 ustar 00root root 0000000 0000000 PubNub Real-time Cloud-Hosted Push API and Push Notification Client Frameworks
Copyright (c) 2013 PubNub Inc.
http://www.pubnub.com/
http://www.pubnub.com/terms
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.
PubNub Real-time Cloud-Hosted Push API and Push Notification Client Frameworks
Copyright (c) 2013 PubNub Inc.
http://www.pubnub.com/
http://www.pubnub.com/terms
bdraco-freenub-69809e8/README.md 0000664 0000000 0000000 00000004772 14645232030 0016156 0 ustar 00root root 0000000 0000000 # freenub
---
**Source Code**: https://github.com/bdraco/freenub
---
This is a fork of pubnub when it still had an MIT license
## Installation
Install this via pip (or your favourite package manager):
`pip install freenub`
## Usage
Start by importing it:
```python
import freenub
```
## Contributors ✨
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
## Credits
This package was created with
[Copier](https://copier.readthedocs.io/) and the
[browniebroke/pypackage-template](https://github.com/browniebroke/pypackage-template)
project template.
bdraco-freenub-69809e8/bandit.yml 0000664 0000000 0000000 00000000020 14645232030 0016641 0 ustar 00root root 0000000 0000000 skips: ["B101"]
bdraco-freenub-69809e8/commitlint.config.mjs 0000664 0000000 0000000 00000000362 14645232030 0021024 0 ustar 00root root 0000000 0000000 export default {
extends: ["@commitlint/config-conventional"],
rules: {
"header-max-length": [0, "always", Infinity],
"body-max-line-length": [0, "always", Infinity],
"footer-max-line-length": [0, "always", Infinity],
},
};
bdraco-freenub-69809e8/examples/ 0000775 0000000 0000000 00000000000 14645232030 0016503 5 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/examples/__init__.py 0000664 0000000 0000000 00000000305 14645232030 0020612 0 ustar 00root root 0000000 0000000 from pubnub.pnconfiguration import PNConfiguration
pnconf = PNConfiguration()
pnconf.subscribe_key = "demo"
pnconf.publish_key = "demo"
pnconf.enable_subscribe = False
pnconf.user_id = "user_id"
bdraco-freenub-69809e8/examples/crypto.py 0000664 0000000 0000000 00000003435 14645232030 0020402 0 ustar 00root root 0000000 0000000 from os import getenv
from time import sleep
from Cryptodome.Cipher import AES
from pubnub.pnconfiguration import PNConfiguration
from pubnub.pubnub import PubNub
channel = "cipher_algorithm_experiment"
def PNFactory(cipher_mode=AES.MODE_GCM, fallback_cipher_mode=AES.MODE_CBC) -> PubNub:
config = config = PNConfiguration()
config.publish_key = getenv("PN_KEY_PUBLISH")
config.subscribe_key = getenv("PN_KEY_SUBSCRIBE")
config.secret_key = getenv("PN_KEY_SECRET")
config.cipher_key = getenv("PN_KEY_CIPHER")
config.user_id = "experiment"
config.cipher_mode = cipher_mode
config.fallback_cipher_mode = fallback_cipher_mode
return PubNub(config)
# let's build history with legacy AES.CBC
pn = PNFactory(cipher_mode=AES.MODE_CBC, fallback_cipher_mode=None)
pn.publish().channel(channel).message("message encrypted with CBC").sync()
pn.publish().channel(channel).message("message encrypted with CBC").sync()
# now with upgraded config
pn = PNFactory(cipher_mode=AES.MODE_GCM, fallback_cipher_mode=AES.MODE_CBC)
pn.publish().channel(channel).message("message encrypted with GCM").sync()
pn.publish().channel(channel).message("message encrypted with GCM").sync()
# give some time to store messages
sleep(3)
# after upgrade decoding with GCM and fallback CBC
pn = PNFactory(cipher_mode=AES.MODE_GCM, fallback_cipher_mode=AES.MODE_CBC)
messages = pn.history().channel(channel).sync()
print([message.entry for message in messages.result.messages])
# before upgrade decoding with CBC and without fallback
pn = PNFactory(cipher_mode=AES.MODE_CBC, fallback_cipher_mode=None)
try:
messages = pn.history().channel(channel).sync()
print([message.entry for message in messages.result.messages])
except UnicodeDecodeError:
print("Unable to decode - Exception has been thrown")
bdraco-freenub-69809e8/examples/entities.py 0000664 0000000 0000000 00000010305 14645232030 0020700 0 ustar 00root root 0000000 0000000 import os
from pubnub.models.consumer.entities.space import Space
from pubnub.models.consumer.entities.user import User
from pubnub.pnconfiguration import PNConfiguration
from pubnub.pubnub import PubNub
pnconfig = PNConfiguration()
pnconfig.subscribe_key = os.getenv("SUB_KEY")
pnconfig.publish_key = os.getenv("PUB_KEY")
pnconfig.secret_key = os.getenv("SEC_KEY")
pnconfig.user_id = "my_uuid"
pnconfig.non_subscribe_request_timeout = 60
pnconfig.connect_timeout = 14
pnconfig.reconnect_policy
pubnub = PubNub(pnconfig)
space_id = "blah"
user_id = "jason-id"
user_id_2 = "freddy-id"
create_space = pubnub.create_space(
space_id=space_id,
name=f"Space ID {space_id}",
description=f"This space ID is {space_id} and is made for demo purpose only",
custom={"created_by": "me"},
space_status="Primary",
space_type="COM",
sync=True,
)
print(f"create space result:{create_space.result.__dict__}")
update_space = pubnub.update_space(
space_id=space_id,
name=f"EDIT Space ID {space_id}",
description=f"EDIT: This space ID is {space_id} and is made for demo purpose only",
custom={"created_by": "EDIT me"},
sync=True,
)
print(f"update space result: {update_space.result.__dict__}")
fetch_space = pubnub.fetch_space(space_id=space_id, include_custom=True, sync=True)
print(f"fetch space result: {fetch_space.result.__dict__}")
space_id2 = space_id + "2"
create_space = (
pubnub.create_space(space_id2)
.set_name(f"Space ID {space_id}")
.description(f"This space ID is {space_id} and is made for demo purpose only")
.custom({"created_by": "me"})
.sync()
)
all_spaces = pubnub.fetch_spaces(include_custom=True, include_total_count=True).sync()
print(f"fetch spaces result: {all_spaces.result.__dict__}")
rm_space = pubnub.remove_space(space_id2).sync()
print(f"remove space result: {rm_space.result.__dict__}")
user = pubnub.create_user(
user_id=user_id, name="Jason", email="Jason@Voorhe.es", sync=True
)
users = pubnub.fetch_user(user_id=user_id, sync=True)
print(f"fetch_user: {users.result.__dict__}")
membership = pubnub.add_memberships(
user_id=user_id, spaces=[Space(space_id=space_id, custom={"a": "b"})], sync=True
)
print(f"add_memberships (user_id): {membership.result.__dict__}")
memberships = pubnub.fetch_memberships(user_id=user_id, include_custom=True, sync=True)
print(f"fetch_memberships (user_id): {memberships.result.__dict__}")
print("-------")
membership = pubnub.update_memberships(
user_id=user_id, spaces=[Space(space_id=space_id, custom={"c": "d"})], sync=True
)
print(f"add_memberships (user_id): {membership.result.__dict__}")
memberships = pubnub.fetch_memberships(user_id=user_id, include_custom=True, sync=True)
print(f"fetch_memberships (user_id): {memberships.result.__dict__}")
print("-------")
membership = pubnub.add_memberships(
user_id=user_id,
spaces=[Space(space_id="some_2nd_space_id"), Space(space_id="some_3rd_space_id")],
sync=True,
)
print(f"add_memberships (user_id): {membership.result.__dict__}")
memberships = pubnub.fetch_memberships(user_id=user_id, include_custom=True, sync=True)
print(f"fetch_memberships (user_id): {memberships.result.__dict__}")
print("-------")
membership = pubnub.remove_memberships(
user_id=user_id, spaces=[Space(space_id=space_id)], sync=True
)
print(f"remove_memberships (user_id): {membership.result.__dict__}")
memberships = pubnub.fetch_memberships(user_id=user_id, include_custom=True, sync=True)
print(f"fetch_memberships (user_id): {memberships.result.__dict__}")
print("-------")
membership = pubnub.add_memberships(
space_id=space_id,
users=[User(user_id=user_id, custom={"Kikiki": "Mamama"})],
sync=True,
)
print(f"add_memberships (space_id): {membership.result.__dict__}")
membership = pubnub.update_memberships(
space_id=space_id,
users=[
User(user_id=user_id_2, custom={"1-2": "Freddy's comming"}),
User(user_id="ghostface", custom={"question": "Favourite scary movie?"}),
],
sync=True,
)
print(f"update_memberships (space_id): {membership.result.__dict__}")
print("-------")
memberships = pubnub.fetch_memberships(
space_id=space_id, include_custom=True, sync=True
)
print(f"fetch_memberships (space_id): {memberships.result.__dict__}")
bdraco-freenub-69809e8/examples/logger.py 0000664 0000000 0000000 00000000554 14645232030 0020340 0 ustar 00root root 0000000 0000000 import logging
import sys
sys.path.append("../")
import pubnub
from examples import pnconf
from pubnub.pubnub import PubNub
# Default log-level is ERROR, to override it use pubnub.set_stream_logger helper:
pubnub.set_stream_logger("pubnub", logging.DEBUG, stream=sys.stdout)
pubnub = PubNub(pnconf)
pubnub.publish().channel("logging").message("hello").sync()
bdraco-freenub-69809e8/examples/native_threads/ 0000775 0000000 0000000 00000000000 14645232030 0021503 5 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/examples/native_threads/check.py 0000664 0000000 0000000 00000004464 14645232030 0023142 0 ustar 00root root 0000000 0000000 from pubnub.callbacks import SubscribeCallback
from pubnub.enums import PNStatusCategory
from pubnub.pnconfiguration import PNConfiguration
from pubnub.pubnub import PubNub
pnconfig = PNConfiguration()
pnconfig.subscribe_key = "sub-c-a41be4e8-b620-11e5-a916-0619f8945a4f"
pnconfig.publish_key = "pub-c-b525a8c0-3301-432e-a37b-d8fec5583788"
pnconfig.subscribe_key = "demo"
pnconfig.publish_key = "demo"
pubnub = PubNub(pnconfig)
def my_publish_callback(envelope, status):
# Check whether request successfully completed or not
if not status.is_error():
pass # Message successfully published to specified channel.
else:
pass # Handle message publish error. Check 'category' property to find out possible issue
# because of which request did fail.
# Request can be resent using: [status retry];
class MySubscribeCallback(SubscribeCallback):
def presence(self, pubnub, presence):
pass # handle incoming presence data
def status(self, pubnub, status):
print("Status category", status.category)
if status.category == PNStatusCategory.PNUnexpectedDisconnectCategory:
pass # This event happens when radio / connectivity is lost
elif status.category == PNStatusCategory.PNConnectedCategory:
# Connect event. You can do stuff like publish, and know you'll get it.
# Or just use the connected event to confirm you are subscribed for
# UI / internal notifications, etc
pubnub.publish().channel("someChannel").message("Hi...").pn_async(
my_publish_callback
)
elif status.category == PNStatusCategory.PNReconnectedCategory:
pass
# Happens as part of our regular operation. This event happens when
# radio / connectivity is lost, then regained.
elif status.category == PNStatusCategory.PNDecryptionErrorCategory:
pass
# Handle message decryption error. Probably client configured to
# encrypt messages and on live data feed it received plain text.
def message(self, pubnub, message):
# Handle new message stored in message.message
print(message)
pubnub.unsubscribe().channels("someChannel").execute()
pubnub.add_listener(MySubscribeCallback())
pubnub.subscribe().channels("someChannel").execute()
bdraco-freenub-69809e8/examples/native_threads/http/ 0000775 0000000 0000000 00000000000 14645232030 0022462 5 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/examples/native_threads/http/app.py 0000664 0000000 0000000 00000005325 14645232030 0023621 0 ustar 00root root 0000000 0000000 import logging
import os
import sys
import time
from flask import Flask, jsonify, request
d = os.path.dirname
PUBNUB_ROOT = d(d(d(os.path.dirname(os.path.abspath(__file__)))))
sys.path.append(PUBNUB_ROOT)
import pubnub as pn
from pubnub import utils
from pubnub.exceptions import PubNubException
from pubnub.pnconfiguration import PNConfiguration
from pubnub.pubnub import PubNub
pn.set_stream_logger("pubnub", logging.DEBUG)
logger = logging.getLogger("myapp")
app = Flask(__name__)
pnconfig = PNConfiguration()
pnconfig.subscribe_request_timeout = 10
pnconfig.subscribe_key = "sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe"
pnconfig.publish_key = "pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52"
pnconfig.uuid = "pubnub-demo-api-python-backend"
DEFAULT_CHANNEL = "pubnub_demo_api_python_channel"
EVENTS_CHANNEL = "pubnub_demo_api_python_events"
APP_KEY = utils.uuid()
pubnub = PubNub(pnconfig)
logger.info("SDK Version: %s", pubnub.SDK_VERSION)
@app.route("/app_key")
def app_key():
return {"app_key": APP_KEY}
@app.route("/subscription/add")
def subscription_add():
channel = request.args.get("channel")
if channel is None:
return jsonify({"error": "Channel missing"}), 500
pubnub.subscribe().channels(channel).execute()
return jsonify({"subscribed_channels": pubnub.get_subscribed_channels()})
@app.route("/subscription/remove")
def subscription_remove():
channel = request.args.get("channel")
if channel is None:
return jsonify({"error": "Channel missing"}), 500
pubnub.unsubscribe().channels(channel).execute()
return jsonify({"subscribed_channels": pubnub.get_subscribed_channels()})
@app.route("/subscription/list")
def subscription_list():
return jsonify({"subscribed_channels": pubnub.get_subscribed_channels()})
@app.route("/publish/sync")
def publish_sync():
channel = request.args.get("channel")
if channel is None:
return jsonify({"error": "Channel missing"}), 500
try:
envelope = (
pubnub.publish()
.channel(channel)
.message("hello from yield-based publish")
.sync()
)
return jsonify({"original_response": str(envelope.status.original_response)})
except PubNubException as e:
return jsonify({"message": str(e)}), 500
@app.route("/publish/async")
def publish_async():
channel = request.args.get("channel")
if channel is None:
return jsonify({"error": "Channel missing"}), 500
def stub(res, state):
pass
pubnub.publish().channel(channel).message(
"hello from yield-based publish"
).pn_async(stub)
return jsonify({"message": "Publish task scheduled"})
if __name__ == "__main__":
app.run(host="0.0.0.0")
time.sleep(100)
bdraco-freenub-69809e8/examples/native_threads/publish.py 0000664 0000000 0000000 00000001144 14645232030 0023523 0 ustar 00root root 0000000 0000000 # PubNub HereNow usage example
import logging
import os
import sys
d = os.path.dirname
PUBNUB_ROOT = d(d(os.path.dirname(os.path.abspath(__file__))))
sys.path.append(PUBNUB_ROOT)
import pubnub
from examples import pnconf
from pubnub.pubnub import NonSubscribeListener, PubNub
pubnub.set_stream_logger("pubnub", logging.DEBUG, stream=sys.stdout)
pnconf.enable_subscribe = True
pubnub = PubNub(pnconf)
listener = NonSubscribeListener()
pubnub.publish().channel("blah").message("hey").pn_async(listener.callback)
result = listener.await_result_and_reset(5)
# FIX: returns None
print(result)
pubnub.stop()
bdraco-freenub-69809e8/examples/pubnub_asyncio/ 0000775 0000000 0000000 00000000000 14645232030 0021523 5 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/examples/pubnub_asyncio/fastapi/ 0000775 0000000 0000000 00000000000 14645232030 0023152 5 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/examples/pubnub_asyncio/fastapi/main.py 0000664 0000000 0000000 00000002052 14645232030 0024447 0 ustar 00root root 0000000 0000000 import logging
from fastapi import BackgroundTasks, FastAPI
import pubnub as pn
from pubnub.pnconfiguration import PNConfiguration
from pubnub.pubnub_asyncio import PubNubAsyncio
app = FastAPI()
pnconfig = PNConfiguration()
pnconfig.publish_key = "demo"
pnconfig.subscribe_key = "demo"
pnconfig.uuid = "UUID-PUB"
CHANNEL = "the_guide"
pubnub = PubNubAsyncio(pnconfig)
pn.set_stream_logger("pubnub", logging.DEBUG)
async def write_notification(email: str, message=""):
with open("/tmp/log.txt", mode="w") as email_file:
content = f"notification for {email}: {message}"
email_file.write(content)
await pubnub.publish().channel(CHANNEL).message(email).future()
@app.get("/send-notification/{email}")
async def send_notification(email: str, background_tasks: BackgroundTasks):
background_tasks.add_task(write_notification, email, message="some notification")
return {"message": "Notification sent in the background"}
@app.on_event("shutdown")
async def stop_pubnub():
print("Closing Application")
await pubnub.stop()
bdraco-freenub-69809e8/examples/pubnub_asyncio/fastapi/requirements.txt 0000664 0000000 0000000 00000000010 14645232030 0026425 0 ustar 00root root 0000000 0000000 fastapi
bdraco-freenub-69809e8/examples/pubnub_asyncio/http/ 0000775 0000000 0000000 00000000000 14645232030 0022502 5 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/examples/pubnub_asyncio/http/app.py 0000664 0000000 0000000 00000011201 14645232030 0023627 0 ustar 00root root 0000000 0000000 import asyncio
import json
import os
import sys
import aiohttp_cors as aiohttp_cors
from aiohttp import web
from pubnub import utils
from pubnub.callbacks import SubscribeCallback
from pubnub.enums import PNOperationType, PNStatusCategory
from pubnub.pubnub_asyncio import PubNubAsyncio
d = os.path.dirname
PUBNUB_ROOT = d(d(d(os.path.dirname(os.path.abspath(__file__)))))
APP_ROOT = d(os.path.abspath(__file__))
sys.path.append(PUBNUB_ROOT)
from pubnub.exceptions import PubNubException
from pubnub.pnconfiguration import PNConfiguration
pnconf = PNConfiguration()
pnconf.subscribe_key = "sub-c-33f55052-190b-11e6-bfbc-02ee2ddab7fe"
pnconf.publish_key = "pub-c-739aa0fc-3ed5-472b-af26-aca1b333ec52"
pnconf.uuid = "pubnub-demo-api-python-backend"
DEFAULT_CHANNEL = "pubnub_demo_api_python_channel"
EVENTS_CHANNEL = "pubnub_demo_api_python_events"
APP_KEY = utils.uuid()
loop = asyncio.get_event_loop()
pubnub = PubNubAsyncio(pnconf)
def publish_sync():
return _not_implemented_error({"error": "Sync publish not implemented"})
def app_key_handler():
return _ok({"app_key": APP_KEY})
def list_channels_handler():
return _ok({"subscribed_channels": pubnub.get_subscribed_channels()})
def add_channel_handler(request):
channel = request.GET["channel"]
if channel is None:
return _internal_server_error({"error": "Channel missing"})
try:
pubnub.subscribe().channels(channel).execute()
return _ok({"subscribed_channels": pubnub.get_subscribed_channels()})
except PubNubException as e:
return _internal_server_error({"message": str(e)})
def remove_channel_handler(request):
channel = request.GET["channel"]
if channel is None:
return _internal_server_error({"error": "Channel missing"})
try:
pubnub.unsubscribe().channels(channel).execute()
return _ok({"subscribed_channels": pubnub.get_subscribed_channels()})
except PubNubException as e:
return _internal_server_error({"message": str(e)})
def _ok(body):
return _prepare_response(body)
def _not_implemented_error(body):
return web.HTTPNotImplemented(
body=json.dumps(body).encode("utf-8"), content_type="application/json"
)
def _internal_server_error(body):
return web.HTTPInternalServerError(
body=json.dumps(body).encode("utf-8"), content_type="application/json"
)
def _prepare_response(body):
return web.Response(
body=json.dumps(body).encode("utf-8"), content_type="application/json"
)
def init_events_transmitter():
"""
Method transmits status events to the specific channel
:return:
"""
class StatusListener(SubscribeCallback):
def status(self, pubnub, status):
event = "unknown"
if (
status.operation == PNOperationType.PNSubscribeOperation
and status.category == PNStatusCategory.PNConnectedCategory
):
event = "Connect"
elif (
status.operation == PNOperationType.PNUnsubscribeOperation
and status.category == PNStatusCategory.PNAcknowledgmentCategory
):
event = "Unsubscribe"
asyncio.ensure_future(
pubnub.publish()
.channel("status-" + APP_KEY)
.message({"event": event})
.future(),
loop=loop,
)
def presence(self, pubnub, presence):
pass
def message(self, pubnub, message):
pass
listener = StatusListener()
pubnub.add_listener(listener)
async def make_app(loop):
app = web.Application()
# (r"/listen", ListenHandler),
cors = aiohttp_cors.setup(
app,
defaults={
"*": aiohttp_cors.ResourceOptions(
allow_credentials=True,
expose_headers="*",
allow_headers="*",
)
},
)
app.router.add_route("GET", "/app_key", app_key_handler)
app.router.add_route("GET", "/subscription/add", add_channel_handler)
app.router.add_route("GET", "/subscription/remove", remove_channel_handler)
app.router.add_route("GET", "/subscription/list", list_channels_handler)
app.router.add_route("GET", "/publish/sync", publish_sync)
app.router.add_route("GET", "/publish/async", publish_sync)
app.router.add_route("GET", "/publish/async2", publish_sync)
for route in list(app.router.routes()):
cors.add(route)
srv = await loop.create_server(app.make_handler(), "0.0.0.0", 8080)
return srv
if __name__ == "__main__":
init_events_transmitter()
loop.run_until_complete(make_app(loop))
loop.run_forever()
bdraco-freenub-69809e8/examples/pubnub_asyncio/http/requirements.txt 0000664 0000000 0000000 00000000025 14645232030 0025763 0 ustar 00root root 0000000 0000000 aiohttp
aiohttp_cors
bdraco-freenub-69809e8/poetry.lock 0000664 0000000 0000000 00000351471 14645232030 0017074 0 ustar 00root root 0000000 0000000 # This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
[[package]]
name = "aiohttp"
version = "3.9.5"
description = "Async http client/server framework (asyncio)"
optional = false
python-versions = ">=3.8"
files = [
{file = "aiohttp-3.9.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fcde4c397f673fdec23e6b05ebf8d4751314fa7c24f93334bf1f1364c1c69ac7"},
{file = "aiohttp-3.9.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5d6b3f1fabe465e819aed2c421a6743d8debbde79b6a8600739300630a01bf2c"},
{file = "aiohttp-3.9.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6ae79c1bc12c34082d92bf9422764f799aee4746fd7a392db46b7fd357d4a17a"},
{file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d3ebb9e1316ec74277d19c5f482f98cc65a73ccd5430540d6d11682cd857430"},
{file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84dabd95154f43a2ea80deffec9cb44d2e301e38a0c9d331cc4aa0166fe28ae3"},
{file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c8a02fbeca6f63cb1f0475c799679057fc9268b77075ab7cf3f1c600e81dd46b"},
{file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c26959ca7b75ff768e2776d8055bf9582a6267e24556bb7f7bd29e677932be72"},
{file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:714d4e5231fed4ba2762ed489b4aec07b2b9953cf4ee31e9871caac895a839c0"},
{file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e7a6a8354f1b62e15d48e04350f13e726fa08b62c3d7b8401c0a1314f02e3558"},
{file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c413016880e03e69d166efb5a1a95d40f83d5a3a648d16486592c49ffb76d0db"},
{file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:ff84aeb864e0fac81f676be9f4685f0527b660f1efdc40dcede3c251ef1e867f"},
{file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:ad7f2919d7dac062f24d6f5fe95d401597fbb015a25771f85e692d043c9d7832"},
{file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:702e2c7c187c1a498a4e2b03155d52658fdd6fda882d3d7fbb891a5cf108bb10"},
{file = "aiohttp-3.9.5-cp310-cp310-win32.whl", hash = "sha256:67c3119f5ddc7261d47163ed86d760ddf0e625cd6246b4ed852e82159617b5fb"},
{file = "aiohttp-3.9.5-cp310-cp310-win_amd64.whl", hash = "sha256:471f0ef53ccedec9995287f02caf0c068732f026455f07db3f01a46e49d76bbb"},
{file = "aiohttp-3.9.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e0ae53e33ee7476dd3d1132f932eeb39bf6125083820049d06edcdca4381f342"},
{file = "aiohttp-3.9.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c088c4d70d21f8ca5c0b8b5403fe84a7bc8e024161febdd4ef04575ef35d474d"},
{file = "aiohttp-3.9.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:639d0042b7670222f33b0028de6b4e2fad6451462ce7df2af8aee37dcac55424"},
{file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f26383adb94da5e7fb388d441bf09c61e5e35f455a3217bfd790c6b6bc64b2ee"},
{file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:66331d00fb28dc90aa606d9a54304af76b335ae204d1836f65797d6fe27f1ca2"},
{file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ff550491f5492ab5ed3533e76b8567f4b37bd2995e780a1f46bca2024223233"},
{file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f22eb3a6c1080d862befa0a89c380b4dafce29dc6cd56083f630073d102eb595"},
{file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a81b1143d42b66ffc40a441379387076243ef7b51019204fd3ec36b9f69e77d6"},
{file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f64fd07515dad67f24b6ea4a66ae2876c01031de91c93075b8093f07c0a2d93d"},
{file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:93e22add827447d2e26d67c9ac0161756007f152fdc5210277d00a85f6c92323"},
{file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:55b39c8684a46e56ef8c8d24faf02de4a2b2ac60d26cee93bc595651ff545de9"},
{file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4715a9b778f4293b9f8ae7a0a7cef9829f02ff8d6277a39d7f40565c737d3771"},
{file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:afc52b8d969eff14e069a710057d15ab9ac17cd4b6753042c407dcea0e40bf75"},
{file = "aiohttp-3.9.5-cp311-cp311-win32.whl", hash = "sha256:b3df71da99c98534be076196791adca8819761f0bf6e08e07fd7da25127150d6"},
{file = "aiohttp-3.9.5-cp311-cp311-win_amd64.whl", hash = "sha256:88e311d98cc0bf45b62fc46c66753a83445f5ab20038bcc1b8a1cc05666f428a"},
{file = "aiohttp-3.9.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:c7a4b7a6cf5b6eb11e109a9755fd4fda7d57395f8c575e166d363b9fc3ec4678"},
{file = "aiohttp-3.9.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:0a158704edf0abcac8ac371fbb54044f3270bdbc93e254a82b6c82be1ef08f3c"},
{file = "aiohttp-3.9.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d153f652a687a8e95ad367a86a61e8d53d528b0530ef382ec5aaf533140ed00f"},
{file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82a6a97d9771cb48ae16979c3a3a9a18b600a8505b1115cfe354dfb2054468b4"},
{file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:60cdbd56f4cad9f69c35eaac0fbbdf1f77b0ff9456cebd4902f3dd1cf096464c"},
{file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8676e8fd73141ded15ea586de0b7cda1542960a7b9ad89b2b06428e97125d4fa"},
{file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da00da442a0e31f1c69d26d224e1efd3a1ca5bcbf210978a2ca7426dfcae9f58"},
{file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18f634d540dd099c262e9f887c8bbacc959847cfe5da7a0e2e1cf3f14dbf2daf"},
{file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:320e8618eda64e19d11bdb3bd04ccc0a816c17eaecb7e4945d01deee2a22f95f"},
{file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:2faa61a904b83142747fc6a6d7ad8fccff898c849123030f8e75d5d967fd4a81"},
{file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:8c64a6dc3fe5db7b1b4d2b5cb84c4f677768bdc340611eca673afb7cf416ef5a"},
{file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:393c7aba2b55559ef7ab791c94b44f7482a07bf7640d17b341b79081f5e5cd1a"},
{file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c671dc117c2c21a1ca10c116cfcd6e3e44da7fcde37bf83b2be485ab377b25da"},
{file = "aiohttp-3.9.5-cp312-cp312-win32.whl", hash = "sha256:5a7ee16aab26e76add4afc45e8f8206c95d1d75540f1039b84a03c3b3800dd59"},
{file = "aiohttp-3.9.5-cp312-cp312-win_amd64.whl", hash = "sha256:5ca51eadbd67045396bc92a4345d1790b7301c14d1848feaac1d6a6c9289e888"},
{file = "aiohttp-3.9.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:694d828b5c41255e54bc2dddb51a9f5150b4eefa9886e38b52605a05d96566e8"},
{file = "aiohttp-3.9.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0605cc2c0088fcaae79f01c913a38611ad09ba68ff482402d3410bf59039bfb8"},
{file = "aiohttp-3.9.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4558e5012ee03d2638c681e156461d37b7a113fe13970d438d95d10173d25f78"},
{file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dbc053ac75ccc63dc3a3cc547b98c7258ec35a215a92bd9f983e0aac95d3d5b"},
{file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4109adee842b90671f1b689901b948f347325045c15f46b39797ae1bf17019de"},
{file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6ea1a5b409a85477fd8e5ee6ad8f0e40bf2844c270955e09360418cfd09abac"},
{file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3c2890ca8c59ee683fd09adf32321a40fe1cf164e3387799efb2acebf090c11"},
{file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3916c8692dbd9d55c523374a3b8213e628424d19116ac4308e434dbf6d95bbdd"},
{file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8d1964eb7617907c792ca00b341b5ec3e01ae8c280825deadbbd678447b127e1"},
{file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d5ab8e1f6bee051a4bf6195e38a5c13e5e161cb7bad83d8854524798bd9fcd6e"},
{file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:52c27110f3862a1afbcb2af4281fc9fdc40327fa286c4625dfee247c3ba90156"},
{file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:7f64cbd44443e80094309875d4f9c71d0401e966d191c3d469cde4642bc2e031"},
{file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8b4f72fbb66279624bfe83fd5eb6aea0022dad8eec62b71e7bf63ee1caadeafe"},
{file = "aiohttp-3.9.5-cp38-cp38-win32.whl", hash = "sha256:6380c039ec52866c06d69b5c7aad5478b24ed11696f0e72f6b807cfb261453da"},
{file = "aiohttp-3.9.5-cp38-cp38-win_amd64.whl", hash = "sha256:da22dab31d7180f8c3ac7c7635f3bcd53808f374f6aa333fe0b0b9e14b01f91a"},
{file = "aiohttp-3.9.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:1732102949ff6087589408d76cd6dea656b93c896b011ecafff418c9661dc4ed"},
{file = "aiohttp-3.9.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c6021d296318cb6f9414b48e6a439a7f5d1f665464da507e8ff640848ee2a58a"},
{file = "aiohttp-3.9.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:239f975589a944eeb1bad26b8b140a59a3a320067fb3cd10b75c3092405a1372"},
{file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b7b30258348082826d274504fbc7c849959f1989d86c29bc355107accec6cfb"},
{file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cd2adf5c87ff6d8b277814a28a535b59e20bfea40a101db6b3bdca7e9926bc24"},
{file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e9a3d838441bebcf5cf442700e3963f58b5c33f015341f9ea86dcd7d503c07e2"},
{file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e3a1ae66e3d0c17cf65c08968a5ee3180c5a95920ec2731f53343fac9bad106"},
{file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9c69e77370cce2d6df5d12b4e12bdcca60c47ba13d1cbbc8645dd005a20b738b"},
{file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0cbf56238f4bbf49dab8c2dc2e6b1b68502b1e88d335bea59b3f5b9f4c001475"},
{file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d1469f228cd9ffddd396d9948b8c9cd8022b6d1bf1e40c6f25b0fb90b4f893ed"},
{file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:45731330e754f5811c314901cebdf19dd776a44b31927fa4b4dbecab9e457b0c"},
{file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:3fcb4046d2904378e3aeea1df51f697b0467f2aac55d232c87ba162709478c46"},
{file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8cf142aa6c1a751fcb364158fd710b8a9be874b81889c2bd13aa8893197455e2"},
{file = "aiohttp-3.9.5-cp39-cp39-win32.whl", hash = "sha256:7b179eea70833c8dee51ec42f3b4097bd6370892fa93f510f76762105568cf09"},
{file = "aiohttp-3.9.5-cp39-cp39-win_amd64.whl", hash = "sha256:38d80498e2e169bc61418ff36170e0aad0cd268da8b38a17c4cf29d254a8b3f1"},
{file = "aiohttp-3.9.5.tar.gz", hash = "sha256:edea7d15772ceeb29db4aff55e482d4bcfb6ae160ce144f2682de02f6d693551"},
]
[package.dependencies]
aiosignal = ">=1.1.2"
async-timeout = {version = ">=4.0,<5.0", markers = "python_version < \"3.11\""}
attrs = ">=17.3.0"
frozenlist = ">=1.1.1"
multidict = ">=4.5,<7.0"
yarl = ">=1.0,<2.0"
[package.extras]
speedups = ["Brotli", "aiodns", "brotlicffi"]
[[package]]
name = "aiosignal"
version = "1.3.1"
description = "aiosignal: a list of registered asynchronous callbacks"
optional = false
python-versions = ">=3.7"
files = [
{file = "aiosignal-1.3.1-py3-none-any.whl", hash = "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17"},
{file = "aiosignal-1.3.1.tar.gz", hash = "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc"},
]
[package.dependencies]
frozenlist = ">=1.1.0"
[[package]]
name = "async-timeout"
version = "4.0.3"
description = "Timeout context manager for asyncio programs"
optional = false
python-versions = ">=3.7"
files = [
{file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"},
{file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"},
]
[[package]]
name = "attrs"
version = "23.2.0"
description = "Classes Without Boilerplate"
optional = false
python-versions = ">=3.7"
files = [
{file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"},
{file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"},
]
[package.extras]
cov = ["attrs[tests]", "coverage[toml] (>=5.3)"]
dev = ["attrs[tests]", "pre-commit"]
docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"]
tests = ["attrs[tests-no-zope]", "zope-interface"]
tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"]
tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"]
[[package]]
name = "behave"
version = "1.2.6"
description = "behave is behaviour-driven development, Python style"
optional = false
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
files = [
{file = "behave-1.2.6-py2.py3-none-any.whl", hash = "sha256:ebda1a6c9e5bfe95c5f9f0a2794e01c7098b3dde86c10a95d8621c5907ff6f1c"},
{file = "behave-1.2.6.tar.gz", hash = "sha256:b9662327aa53294c1351b0a9c369093ccec1d21026f050c3bd9b3e5cccf81a86"},
]
[package.dependencies]
parse = ">=1.8.2"
parse-type = ">=0.4.2"
six = ">=1.11"
[package.extras]
develop = ["coverage", "invoke (>=0.21.0)", "modernize (>=0.5)", "path.py (>=8.1.2)", "pathlib", "pycmd", "pylint", "pytest (>=3.0)", "pytest-cov", "tox"]
docs = ["sphinx (>=1.6)", "sphinx-bootstrap-theme (>=0.6)"]
[[package]]
name = "busypie"
version = "0.5.1"
description = "Easy and expressive busy-waiting for Python"
optional = false
python-versions = ">=3.6"
files = [
{file = "busypie-0.5.1-py3-none-any.whl", hash = "sha256:0f7d6a3e549357a8d59087985919af11276cd11a6be162dadd7f8d60a4718a86"},
{file = "busypie-0.5.1.tar.gz", hash = "sha256:5a6ec2a60973a9708a2b6aada5d9904ab73636f2432f19db2940c811af559d02"},
]
[[package]]
name = "cbor2"
version = "5.6.4"
description = "CBOR (de)serializer with extensive tag support"
optional = false
python-versions = ">=3.8"
files = [
{file = "cbor2-5.6.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c40c68779a363f47a11ded7b189ba16767391d5eae27fac289e7f62b730ae1fc"},
{file = "cbor2-5.6.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c0625c8d3c487e509458459de99bf052f62eb5d773cc9fc141c6a6ea9367726d"},
{file = "cbor2-5.6.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:de7137622204168c3a57882f15dd09b5135bda2bcb1cf8b56b58d26b5150dfca"},
{file = "cbor2-5.6.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3545e1e62ec48944b81da2c0e0a736ca98b9e4653c2365cae2f10ae871e9113"},
{file = "cbor2-5.6.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d6749913cd00a24eba17406a0bfc872044036c30a37eb2fcde7acfd975317e8a"},
{file = "cbor2-5.6.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:57db966ab08443ee54b6f154f72021a41bfecd4ba897fe108728183ad8784a2a"},
{file = "cbor2-5.6.4-cp310-cp310-win_amd64.whl", hash = "sha256:380e0c7f4db574dcd86e6eee1b0041863b0aae7efd449d49b0b784cf9a481b9b"},
{file = "cbor2-5.6.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5c763d50a1714e0356b90ad39194fc8ef319356b89fb001667a2e836bfde88e3"},
{file = "cbor2-5.6.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:58a7ac8861857a9f9b0de320a4808a2a5f68a2599b4c14863e2748d5a4686c99"},
{file = "cbor2-5.6.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7d715b2f101730335e84a25fe0893e2b6adf049d6d44da123bf243b8c875ffd8"},
{file = "cbor2-5.6.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f53a67600038cb9668720b309fdfafa8c16d1a02570b96d2144d58d66774318"},
{file = "cbor2-5.6.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f898bab20c4f42dca3688c673ff97c2f719b1811090430173c94452603fbcf13"},
{file = "cbor2-5.6.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5e5d50fb9f47d295c1b7f55592111350424283aff4cc88766c656aad0300f11f"},
{file = "cbor2-5.6.4-cp311-cp311-win_amd64.whl", hash = "sha256:7f9d867dcd814ab8383ad132eb4063e2b69f6a9f688797b7a8ca34a4eadb3944"},
{file = "cbor2-5.6.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e0860ca88edf8aaec5461ce0e498eb5318f1bcc70d93f90091b7a1f1d351a167"},
{file = "cbor2-5.6.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c38a0ed495a63a8bef6400158746a9cb03c36f89aeed699be7ffebf82720bf86"},
{file = "cbor2-5.6.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0c8d8c2f208c223a61bed48dfd0661694b891e423094ed30bac2ed75032142aa"},
{file = "cbor2-5.6.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24cd2ce6136e1985da989e5ba572521023a320dcefad5d1fff57fba261de80ca"},
{file = "cbor2-5.6.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7facce04aed2bf69ef43bdffb725446fe243594c2451921e89cc305bede16f02"},
{file = "cbor2-5.6.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f9c8ee0d89411e5e039a4f3419befe8b43c0dd8746eedc979e73f4c06fe0ef97"},
{file = "cbor2-5.6.4-cp312-cp312-win_amd64.whl", hash = "sha256:9b45d554daa540e2f29f1747df9f08f8d98ade65a67b1911791bc193d33a5923"},
{file = "cbor2-5.6.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0a5cb2c16687ccd76b38cfbfdb34468ab7d5635fb92c9dc5e07831c1816bd0a9"},
{file = "cbor2-5.6.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6f985f531f7495527153c4f66c8c143e4cf8a658ec9e87b14bc5438e0a8d0911"},
{file = "cbor2-5.6.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9d9c7b4bd7c3ea7e5587d4f1bbe073b81719530ddadb999b184074f064896e2"},
{file = "cbor2-5.6.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64d06184dcdc275c389fee3cd0ea80b5e1769763df15f93ecd0bf4c281817365"},
{file = "cbor2-5.6.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e9ba7116f201860fb4c3e80ef36be63851ec7e4a18af70fea22d09cab0b000bf"},
{file = "cbor2-5.6.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:341468ae58bdedaa05c907ab16e90dd0d5c54d7d1e66698dfacdbc16a31e815b"},
{file = "cbor2-5.6.4-cp38-cp38-win_amd64.whl", hash = "sha256:bcb4994be1afcc81f9167c220645d878b608cae92e19f6706e770f9bc7bbff6c"},
{file = "cbor2-5.6.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:41c43abffe217dce70ae51c7086530687670a0995dfc90cc35f32f2cf4d86392"},
{file = "cbor2-5.6.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:227a7e68ba378fe53741ed892b5b03fe472b5bd23ef26230a71964accebf50a2"},
{file = "cbor2-5.6.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:13521b7c9a0551fcc812d36afd03fc554fa4e1b193659bb5d4d521889aa81154"},
{file = "cbor2-5.6.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f4816d290535d20c7b7e2663b76da5b0deb4237b90275c202c26343d8852b8a"},
{file = "cbor2-5.6.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:1e98d370106821335efcc8fbe4136ea26b4747bf29ca0e66512b6c4f6f5cc59f"},
{file = "cbor2-5.6.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:68743a18e16167ff37654a29321f64f0441801dba68359c82dc48173cc6c87e1"},
{file = "cbor2-5.6.4-cp39-cp39-win_amd64.whl", hash = "sha256:7ba5e9c6ed17526d266a1116c045c0941f710860c5f2495758df2e0d848c1b6d"},
{file = "cbor2-5.6.4-py3-none-any.whl", hash = "sha256:fe411c4bf464f5976605103ebcd0f60b893ac3e4c7c8d8bc8f4a0cb456e33c60"},
{file = "cbor2-5.6.4.tar.gz", hash = "sha256:1c533c50dde86bef1c6950602054a0ffa3c376e8b0e20c7b8f5b108793f6983e"},
]
[package.extras]
benchmarks = ["pytest-benchmark (==4.0.0)"]
doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme (>=1.3.0)", "typing-extensions"]
test = ["coverage (>=7)", "hypothesis", "pytest"]
[[package]]
name = "certifi"
version = "2024.7.4"
description = "Python package for providing Mozilla's CA Bundle."
optional = false
python-versions = ">=3.6"
files = [
{file = "certifi-2024.7.4-py3-none-any.whl", hash = "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90"},
{file = "certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b"},
]
[[package]]
name = "charset-normalizer"
version = "3.3.2"
description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
optional = false
python-versions = ">=3.7.0"
files = [
{file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"},
{file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"},
{file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"},
{file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"},
{file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"},
{file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"},
{file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"},
{file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"},
{file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"},
{file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"},
{file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"},
{file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"},
{file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"},
{file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"},
{file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"},
{file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"},
{file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"},
{file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"},
{file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"},
{file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"},
{file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"},
{file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"},
{file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"},
{file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"},
{file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"},
{file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"},
{file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"},
{file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"},
{file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"},
{file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"},
{file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"},
{file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"},
{file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"},
{file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"},
{file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"},
{file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"},
{file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"},
{file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"},
{file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"},
{file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"},
{file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"},
{file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"},
{file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"},
{file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"},
{file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"},
{file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"},
{file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"},
{file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"},
{file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"},
{file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"},
{file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"},
{file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"},
{file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"},
{file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"},
{file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"},
{file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"},
{file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"},
{file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"},
{file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"},
{file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"},
{file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"},
{file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"},
{file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"},
{file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"},
{file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"},
{file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"},
{file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"},
{file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"},
{file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"},
{file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"},
{file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"},
{file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"},
{file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"},
{file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"},
{file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"},
{file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"},
{file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"},
{file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"},
]
[[package]]
name = "colorama"
version = "0.4.6"
description = "Cross-platform colored terminal text."
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
files = [
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
]
[[package]]
name = "coverage"
version = "7.6.0"
description = "Code coverage measurement for Python"
optional = false
python-versions = ">=3.8"
files = [
{file = "coverage-7.6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dff044f661f59dace805eedb4a7404c573b6ff0cdba4a524141bc63d7be5c7fd"},
{file = "coverage-7.6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a8659fd33ee9e6ca03950cfdcdf271d645cf681609153f218826dd9805ab585c"},
{file = "coverage-7.6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7792f0ab20df8071d669d929c75c97fecfa6bcab82c10ee4adb91c7a54055463"},
{file = "coverage-7.6.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d4b3cd1ca7cd73d229487fa5caca9e4bc1f0bca96526b922d61053ea751fe791"},
{file = "coverage-7.6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7e128f85c0b419907d1f38e616c4f1e9f1d1b37a7949f44df9a73d5da5cd53c"},
{file = "coverage-7.6.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a94925102c89247530ae1dab7dc02c690942566f22e189cbd53579b0693c0783"},
{file = "coverage-7.6.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:dcd070b5b585b50e6617e8972f3fbbee786afca71b1936ac06257f7e178f00f6"},
{file = "coverage-7.6.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d50a252b23b9b4dfeefc1f663c568a221092cbaded20a05a11665d0dbec9b8fb"},
{file = "coverage-7.6.0-cp310-cp310-win32.whl", hash = "sha256:0e7b27d04131c46e6894f23a4ae186a6a2207209a05df5b6ad4caee6d54a222c"},
{file = "coverage-7.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:54dece71673b3187c86226c3ca793c5f891f9fc3d8aa183f2e3653da18566169"},
{file = "coverage-7.6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c7b525ab52ce18c57ae232ba6f7010297a87ced82a2383b1afd238849c1ff933"},
{file = "coverage-7.6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bea27c4269234e06f621f3fac3925f56ff34bc14521484b8f66a580aacc2e7d"},
{file = "coverage-7.6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed8d1d1821ba5fc88d4a4f45387b65de52382fa3ef1f0115a4f7a20cdfab0e94"},
{file = "coverage-7.6.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01c322ef2bbe15057bc4bf132b525b7e3f7206f071799eb8aa6ad1940bcf5fb1"},
{file = "coverage-7.6.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03cafe82c1b32b770a29fd6de923625ccac3185a54a5e66606da26d105f37dac"},
{file = "coverage-7.6.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0d1b923fc4a40c5832be4f35a5dab0e5ff89cddf83bb4174499e02ea089daf57"},
{file = "coverage-7.6.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4b03741e70fb811d1a9a1d75355cf391f274ed85847f4b78e35459899f57af4d"},
{file = "coverage-7.6.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a73d18625f6a8a1cbb11eadc1d03929f9510f4131879288e3f7922097a429f63"},
{file = "coverage-7.6.0-cp311-cp311-win32.whl", hash = "sha256:65fa405b837060db569a61ec368b74688f429b32fa47a8929a7a2f9b47183713"},
{file = "coverage-7.6.0-cp311-cp311-win_amd64.whl", hash = "sha256:6379688fb4cfa921ae349c76eb1a9ab26b65f32b03d46bb0eed841fd4cb6afb1"},
{file = "coverage-7.6.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f7db0b6ae1f96ae41afe626095149ecd1b212b424626175a6633c2999eaad45b"},
{file = "coverage-7.6.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bbdf9a72403110a3bdae77948b8011f644571311c2fb35ee15f0f10a8fc082e8"},
{file = "coverage-7.6.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cc44bf0315268e253bf563f3560e6c004efe38f76db03a1558274a6e04bf5d5"},
{file = "coverage-7.6.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:da8549d17489cd52f85a9829d0e1d91059359b3c54a26f28bec2c5d369524807"},
{file = "coverage-7.6.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0086cd4fc71b7d485ac93ca4239c8f75732c2ae3ba83f6be1c9be59d9e2c6382"},
{file = "coverage-7.6.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1fad32ee9b27350687035cb5fdf9145bc9cf0a094a9577d43e909948ebcfa27b"},
{file = "coverage-7.6.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:044a0985a4f25b335882b0966625270a8d9db3d3409ddc49a4eb00b0ef5e8cee"},
{file = "coverage-7.6.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:76d5f82213aa78098b9b964ea89de4617e70e0d43e97900c2778a50856dac605"},
{file = "coverage-7.6.0-cp312-cp312-win32.whl", hash = "sha256:3c59105f8d58ce500f348c5b56163a4113a440dad6daa2294b5052a10db866da"},
{file = "coverage-7.6.0-cp312-cp312-win_amd64.whl", hash = "sha256:ca5d79cfdae420a1d52bf177de4bc2289c321d6c961ae321503b2ca59c17ae67"},
{file = "coverage-7.6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d39bd10f0ae453554798b125d2f39884290c480f56e8a02ba7a6ed552005243b"},
{file = "coverage-7.6.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:beb08e8508e53a568811016e59f3234d29c2583f6b6e28572f0954a6b4f7e03d"},
{file = "coverage-7.6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2e16f4cd2bc4d88ba30ca2d3bbf2f21f00f382cf4e1ce3b1ddc96c634bc48ca"},
{file = "coverage-7.6.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6616d1c9bf1e3faea78711ee42a8b972367d82ceae233ec0ac61cc7fec09fa6b"},
{file = "coverage-7.6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad4567d6c334c46046d1c4c20024de2a1c3abc626817ae21ae3da600f5779b44"},
{file = "coverage-7.6.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d17c6a415d68cfe1091d3296ba5749d3d8696e42c37fca5d4860c5bf7b729f03"},
{file = "coverage-7.6.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:9146579352d7b5f6412735d0f203bbd8d00113a680b66565e205bc605ef81bc6"},
{file = "coverage-7.6.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:cdab02a0a941af190df8782aafc591ef3ad08824f97850b015c8c6a8b3877b0b"},
{file = "coverage-7.6.0-cp38-cp38-win32.whl", hash = "sha256:df423f351b162a702c053d5dddc0fc0ef9a9e27ea3f449781ace5f906b664428"},
{file = "coverage-7.6.0-cp38-cp38-win_amd64.whl", hash = "sha256:f2501d60d7497fd55e391f423f965bbe9e650e9ffc3c627d5f0ac516026000b8"},
{file = "coverage-7.6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7221f9ac9dad9492cecab6f676b3eaf9185141539d5c9689d13fd6b0d7de840c"},
{file = "coverage-7.6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ddaaa91bfc4477d2871442bbf30a125e8fe6b05da8a0015507bfbf4718228ab2"},
{file = "coverage-7.6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4cbe651f3904e28f3a55d6f371203049034b4ddbce65a54527a3f189ca3b390"},
{file = "coverage-7.6.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:831b476d79408ab6ccfadaaf199906c833f02fdb32c9ab907b1d4aa0713cfa3b"},
{file = "coverage-7.6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46c3d091059ad0b9c59d1034de74a7f36dcfa7f6d3bde782c49deb42438f2450"},
{file = "coverage-7.6.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:4d5fae0a22dc86259dee66f2cc6c1d3e490c4a1214d7daa2a93d07491c5c04b6"},
{file = "coverage-7.6.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:07ed352205574aad067482e53dd606926afebcb5590653121063fbf4e2175166"},
{file = "coverage-7.6.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:49c76cdfa13015c4560702574bad67f0e15ca5a2872c6a125f6327ead2b731dd"},
{file = "coverage-7.6.0-cp39-cp39-win32.whl", hash = "sha256:482855914928c8175735a2a59c8dc5806cf7d8f032e4820d52e845d1f731dca2"},
{file = "coverage-7.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:543ef9179bc55edfd895154a51792b01c017c87af0ebaae092720152e19e42ca"},
{file = "coverage-7.6.0-pp38.pp39.pp310-none-any.whl", hash = "sha256:6fe885135c8a479d3e37a7aae61cbd3a0fb2deccb4dda3c25f92a49189f766d6"},
{file = "coverage-7.6.0.tar.gz", hash = "sha256:289cc803fa1dc901f84701ac10c9ee873619320f2f9aff38794db4a4a0268d51"},
]
[package.dependencies]
tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""}
[package.extras]
toml = ["tomli"]
[[package]]
name = "exceptiongroup"
version = "1.2.2"
description = "Backport of PEP 654 (exception groups)"
optional = false
python-versions = ">=3.7"
files = [
{file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"},
{file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"},
]
[package.extras]
test = ["pytest (>=6)"]
[[package]]
name = "frozenlist"
version = "1.4.1"
description = "A list-like structure which implements collections.abc.MutableSequence"
optional = false
python-versions = ">=3.8"
files = [
{file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f9aa1878d1083b276b0196f2dfbe00c9b7e752475ed3b682025ff20c1c1f51ac"},
{file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:29acab3f66f0f24674b7dc4736477bcd4bc3ad4b896f5f45379a67bce8b96868"},
{file = "frozenlist-1.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:74fb4bee6880b529a0c6560885fce4dc95936920f9f20f53d99a213f7bf66776"},
{file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:590344787a90ae57d62511dd7c736ed56b428f04cd8c161fcc5e7232c130c69a"},
{file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:068b63f23b17df8569b7fdca5517edef76171cf3897eb68beb01341131fbd2ad"},
{file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c849d495bf5154cd8da18a9eb15db127d4dba2968d88831aff6f0331ea9bd4c"},
{file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9750cc7fe1ae3b1611bb8cfc3f9ec11d532244235d75901fb6b8e42ce9229dfe"},
{file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9b2de4cf0cdd5bd2dee4c4f63a653c61d2408055ab77b151c1957f221cabf2a"},
{file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0633c8d5337cb5c77acbccc6357ac49a1770b8c487e5b3505c57b949b4b82e98"},
{file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:27657df69e8801be6c3638054e202a135c7f299267f1a55ed3a598934f6c0d75"},
{file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:f9a3ea26252bd92f570600098783d1371354d89d5f6b7dfd87359d669f2109b5"},
{file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:4f57dab5fe3407b6c0c1cc907ac98e8a189f9e418f3b6e54d65a718aaafe3950"},
{file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e02a0e11cf6597299b9f3bbd3f93d79217cb90cfd1411aec33848b13f5c656cc"},
{file = "frozenlist-1.4.1-cp310-cp310-win32.whl", hash = "sha256:a828c57f00f729620a442881cc60e57cfcec6842ba38e1b19fd3e47ac0ff8dc1"},
{file = "frozenlist-1.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:f56e2333dda1fe0f909e7cc59f021eba0d2307bc6f012a1ccf2beca6ba362439"},
{file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a0cb6f11204443f27a1628b0e460f37fb30f624be6051d490fa7d7e26d4af3d0"},
{file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b46c8ae3a8f1f41a0d2ef350c0b6e65822d80772fe46b653ab6b6274f61d4a49"},
{file = "frozenlist-1.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fde5bd59ab5357e3853313127f4d3565fc7dad314a74d7b5d43c22c6a5ed2ced"},
{file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:722e1124aec435320ae01ee3ac7bec11a5d47f25d0ed6328f2273d287bc3abb0"},
{file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2471c201b70d58a0f0c1f91261542a03d9a5e088ed3dc6c160d614c01649c106"},
{file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c757a9dd70d72b076d6f68efdbb9bc943665ae954dad2801b874c8c69e185068"},
{file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f146e0911cb2f1da549fc58fc7bcd2b836a44b79ef871980d605ec392ff6b0d2"},
{file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9c515e7914626b2a2e1e311794b4c35720a0be87af52b79ff8e1429fc25f19"},
{file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c302220494f5c1ebeb0912ea782bcd5e2f8308037b3c7553fad0e48ebad6ad82"},
{file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:442acde1e068288a4ba7acfe05f5f343e19fac87bfc96d89eb886b0363e977ec"},
{file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:1b280e6507ea8a4fa0c0a7150b4e526a8d113989e28eaaef946cc77ffd7efc0a"},
{file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:fe1a06da377e3a1062ae5fe0926e12b84eceb8a50b350ddca72dc85015873f74"},
{file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:db9e724bebd621d9beca794f2a4ff1d26eed5965b004a97f1f1685a173b869c2"},
{file = "frozenlist-1.4.1-cp311-cp311-win32.whl", hash = "sha256:e774d53b1a477a67838a904131c4b0eef6b3d8a651f8b138b04f748fccfefe17"},
{file = "frozenlist-1.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:fb3c2db03683b5767dedb5769b8a40ebb47d6f7f45b1b3e3b4b51ec8ad9d9825"},
{file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1979bc0aeb89b33b588c51c54ab0161791149f2461ea7c7c946d95d5f93b56ae"},
{file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cc7b01b3754ea68a62bd77ce6020afaffb44a590c2289089289363472d13aedb"},
{file = "frozenlist-1.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c9c92be9fd329ac801cc420e08452b70e7aeab94ea4233a4804f0915c14eba9b"},
{file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c3894db91f5a489fc8fa6a9991820f368f0b3cbdb9cd8849547ccfab3392d86"},
{file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba60bb19387e13597fb059f32cd4d59445d7b18b69a745b8f8e5db0346f33480"},
{file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8aefbba5f69d42246543407ed2461db31006b0f76c4e32dfd6f42215a2c41d09"},
{file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780d3a35680ced9ce682fbcf4cb9c2bad3136eeff760ab33707b71db84664e3a"},
{file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9acbb16f06fe7f52f441bb6f413ebae6c37baa6ef9edd49cdd567216da8600cd"},
{file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:23b701e65c7b36e4bf15546a89279bd4d8675faabc287d06bbcfac7d3c33e1e6"},
{file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3e0153a805a98f5ada7e09826255ba99fb4f7524bb81bf6b47fb702666484ae1"},
{file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:dd9b1baec094d91bf36ec729445f7769d0d0cf6b64d04d86e45baf89e2b9059b"},
{file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:1a4471094e146b6790f61b98616ab8e44f72661879cc63fa1049d13ef711e71e"},
{file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5667ed53d68d91920defdf4035d1cdaa3c3121dc0b113255124bcfada1cfa1b8"},
{file = "frozenlist-1.4.1-cp312-cp312-win32.whl", hash = "sha256:beee944ae828747fd7cb216a70f120767fc9f4f00bacae8543c14a6831673f89"},
{file = "frozenlist-1.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:64536573d0a2cb6e625cf309984e2d873979709f2cf22839bf2d61790b448ad5"},
{file = "frozenlist-1.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:20b51fa3f588ff2fe658663db52a41a4f7aa6c04f6201449c6c7c476bd255c0d"},
{file = "frozenlist-1.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:410478a0c562d1a5bcc2f7ea448359fcb050ed48b3c6f6f4f18c313a9bdb1826"},
{file = "frozenlist-1.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c6321c9efe29975232da3bd0af0ad216800a47e93d763ce64f291917a381b8eb"},
{file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48f6a4533887e189dae092f1cf981f2e3885175f7a0f33c91fb5b7b682b6bab6"},
{file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6eb73fa5426ea69ee0e012fb59cdc76a15b1283d6e32e4f8dc4482ec67d1194d"},
{file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fbeb989b5cc29e8daf7f976b421c220f1b8c731cbf22b9130d8815418ea45887"},
{file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32453c1de775c889eb4e22f1197fe3bdfe457d16476ea407472b9442e6295f7a"},
{file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:693945278a31f2086d9bf3df0fe8254bbeaef1fe71e1351c3bd730aa7d31c41b"},
{file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:1d0ce09d36d53bbbe566fe296965b23b961764c0bcf3ce2fa45f463745c04701"},
{file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3a670dc61eb0d0eb7080890c13de3066790f9049b47b0de04007090807c776b0"},
{file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:dca69045298ce5c11fd539682cff879cc1e664c245d1c64da929813e54241d11"},
{file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a06339f38e9ed3a64e4c4e43aec7f59084033647f908e4259d279a52d3757d09"},
{file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b7f2f9f912dca3934c1baec2e4585a674ef16fe00218d833856408c48d5beee7"},
{file = "frozenlist-1.4.1-cp38-cp38-win32.whl", hash = "sha256:e7004be74cbb7d9f34553a5ce5fb08be14fb33bc86f332fb71cbe5216362a497"},
{file = "frozenlist-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:5a7d70357e7cee13f470c7883a063aae5fe209a493c57d86eb7f5a6f910fae09"},
{file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bfa4a17e17ce9abf47a74ae02f32d014c5e9404b6d9ac7f729e01562bbee601e"},
{file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b7e3ed87d4138356775346e6845cccbe66cd9e207f3cd11d2f0b9fd13681359d"},
{file = "frozenlist-1.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c99169d4ff810155ca50b4da3b075cbde79752443117d89429595c2e8e37fed8"},
{file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edb678da49d9f72c9f6c609fbe41a5dfb9a9282f9e6a2253d5a91e0fc382d7c0"},
{file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6db4667b187a6742b33afbbaf05a7bc551ffcf1ced0000a571aedbb4aa42fc7b"},
{file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55fdc093b5a3cb41d420884cdaf37a1e74c3c37a31f46e66286d9145d2063bd0"},
{file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82e8211d69a4f4bc360ea22cd6555f8e61a1bd211d1d5d39d3d228b48c83a897"},
{file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89aa2c2eeb20957be2d950b85974b30a01a762f3308cd02bb15e1ad632e22dc7"},
{file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9d3e0c25a2350080e9319724dede4f31f43a6c9779be48021a7f4ebde8b2d742"},
{file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7268252af60904bf52c26173cbadc3a071cece75f873705419c8681f24d3edea"},
{file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:0c250a29735d4f15321007fb02865f0e6b6a41a6b88f1f523ca1596ab5f50bd5"},
{file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:96ec70beabbd3b10e8bfe52616a13561e58fe84c0101dd031dc78f250d5128b9"},
{file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:23b2d7679b73fe0e5a4560b672a39f98dfc6f60df63823b0a9970525325b95f6"},
{file = "frozenlist-1.4.1-cp39-cp39-win32.whl", hash = "sha256:a7496bfe1da7fb1a4e1cc23bb67c58fab69311cc7d32b5a99c2007b4b2a0e932"},
{file = "frozenlist-1.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:e6a20a581f9ce92d389a8c7d7c3dd47c81fd5d6e655c8dddf341e14aa48659d0"},
{file = "frozenlist-1.4.1-py3-none-any.whl", hash = "sha256:04ced3e6a46b4cfffe20f9ae482818e34eba9b5fb0ce4056e4cc9b6e212d09b7"},
{file = "frozenlist-1.4.1.tar.gz", hash = "sha256:c037a86e8513059a2613aaba4d817bb90b9d9b6b69aace3ce9c877e8c8ed402b"},
]
[[package]]
name = "idna"
version = "3.7"
description = "Internationalized Domain Names in Applications (IDNA)"
optional = false
python-versions = ">=3.5"
files = [
{file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"},
{file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"},
]
[[package]]
name = "iniconfig"
version = "2.0.0"
description = "brain-dead simple config-ini parsing"
optional = false
python-versions = ">=3.7"
files = [
{file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"},
{file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
]
[[package]]
name = "multidict"
version = "6.0.5"
description = "multidict implementation"
optional = false
python-versions = ">=3.7"
files = [
{file = "multidict-6.0.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:228b644ae063c10e7f324ab1ab6b548bdf6f8b47f3ec234fef1093bc2735e5f9"},
{file = "multidict-6.0.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:896ebdcf62683551312c30e20614305f53125750803b614e9e6ce74a96232604"},
{file = "multidict-6.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:411bf8515f3be9813d06004cac41ccf7d1cd46dfe233705933dd163b60e37600"},
{file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d147090048129ce3c453f0292e7697d333db95e52616b3793922945804a433c"},
{file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:215ed703caf15f578dca76ee6f6b21b7603791ae090fbf1ef9d865571039ade5"},
{file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c6390cf87ff6234643428991b7359b5f59cc15155695deb4eda5c777d2b880f"},
{file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fd81c4ebdb4f214161be351eb5bcf385426bf023041da2fd9e60681f3cebae"},
{file = "multidict-6.0.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3cc2ad10255f903656017363cd59436f2111443a76f996584d1077e43ee51182"},
{file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6939c95381e003f54cd4c5516740faba40cf5ad3eeff460c3ad1d3e0ea2549bf"},
{file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:220dd781e3f7af2c2c1053da9fa96d9cf3072ca58f057f4c5adaaa1cab8fc442"},
{file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:766c8f7511df26d9f11cd3a8be623e59cca73d44643abab3f8c8c07620524e4a"},
{file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:fe5d7785250541f7f5019ab9cba2c71169dc7d74d0f45253f8313f436458a4ef"},
{file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c1c1496e73051918fcd4f58ff2e0f2f3066d1c76a0c6aeffd9b45d53243702cc"},
{file = "multidict-6.0.5-cp310-cp310-win32.whl", hash = "sha256:7afcdd1fc07befad18ec4523a782cde4e93e0a2bf71239894b8d61ee578c1319"},
{file = "multidict-6.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:99f60d34c048c5c2fabc766108c103612344c46e35d4ed9ae0673d33c8fb26e8"},
{file = "multidict-6.0.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f285e862d2f153a70586579c15c44656f888806ed0e5b56b64489afe4a2dbfba"},
{file = "multidict-6.0.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:53689bb4e102200a4fafa9de9c7c3c212ab40a7ab2c8e474491914d2305f187e"},
{file = "multidict-6.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:612d1156111ae11d14afaf3a0669ebf6c170dbb735e510a7438ffe2369a847fd"},
{file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7be7047bd08accdb7487737631d25735c9a04327911de89ff1b26b81745bd4e3"},
{file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de170c7b4fe6859beb8926e84f7d7d6c693dfe8e27372ce3b76f01c46e489fcf"},
{file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04bde7a7b3de05732a4eb39c94574db1ec99abb56162d6c520ad26f83267de29"},
{file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85f67aed7bb647f93e7520633d8f51d3cbc6ab96957c71272b286b2f30dc70ed"},
{file = "multidict-6.0.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425bf820055005bfc8aa9a0b99ccb52cc2f4070153e34b701acc98d201693733"},
{file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d3eb1ceec286eba8220c26f3b0096cf189aea7057b6e7b7a2e60ed36b373b77f"},
{file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7901c05ead4b3fb75113fb1dd33eb1253c6d3ee37ce93305acd9d38e0b5f21a4"},
{file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:e0e79d91e71b9867c73323a3444724d496c037e578a0e1755ae159ba14f4f3d1"},
{file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:29bfeb0dff5cb5fdab2023a7a9947b3b4af63e9c47cae2a10ad58394b517fddc"},
{file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e030047e85cbcedbfc073f71836d62dd5dadfbe7531cae27789ff66bc551bd5e"},
{file = "multidict-6.0.5-cp311-cp311-win32.whl", hash = "sha256:2f4848aa3baa109e6ab81fe2006c77ed4d3cd1e0ac2c1fbddb7b1277c168788c"},
{file = "multidict-6.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:2faa5ae9376faba05f630d7e5e6be05be22913782b927b19d12b8145968a85ea"},
{file = "multidict-6.0.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:51d035609b86722963404f711db441cf7134f1889107fb171a970c9701f92e1e"},
{file = "multidict-6.0.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cbebcd5bcaf1eaf302617c114aa67569dd3f090dd0ce8ba9e35e9985b41ac35b"},
{file = "multidict-6.0.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ffc42c922dbfddb4a4c3b438eb056828719f07608af27d163191cb3e3aa6cc5"},
{file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ceb3b7e6a0135e092de86110c5a74e46bda4bd4fbfeeb3a3bcec79c0f861e450"},
{file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:79660376075cfd4b2c80f295528aa6beb2058fd289f4c9252f986751a4cd0496"},
{file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e4428b29611e989719874670fd152b6625500ad6c686d464e99f5aaeeaca175a"},
{file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d84a5c3a5f7ce6db1f999fb9438f686bc2e09d38143f2d93d8406ed2dd6b9226"},
{file = "multidict-6.0.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76c0de87358b192de7ea9649beb392f107dcad9ad27276324c24c91774ca5271"},
{file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:79a6d2ba910adb2cbafc95dad936f8b9386e77c84c35bc0add315b856d7c3abb"},
{file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:92d16a3e275e38293623ebf639c471d3e03bb20b8ebb845237e0d3664914caef"},
{file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:fb616be3538599e797a2017cccca78e354c767165e8858ab5116813146041a24"},
{file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:14c2976aa9038c2629efa2c148022ed5eb4cb939e15ec7aace7ca932f48f9ba6"},
{file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:435a0984199d81ca178b9ae2c26ec3d49692d20ee29bc4c11a2a8d4514c67eda"},
{file = "multidict-6.0.5-cp312-cp312-win32.whl", hash = "sha256:9fe7b0653ba3d9d65cbe7698cca585bf0f8c83dbbcc710db9c90f478e175f2d5"},
{file = "multidict-6.0.5-cp312-cp312-win_amd64.whl", hash = "sha256:01265f5e40f5a17f8241d52656ed27192be03bfa8764d88e8220141d1e4b3556"},
{file = "multidict-6.0.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:19fe01cea168585ba0f678cad6f58133db2aa14eccaf22f88e4a6dccadfad8b3"},
{file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bf7a982604375a8d49b6cc1b781c1747f243d91b81035a9b43a2126c04766f5"},
{file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:107c0cdefe028703fb5dafe640a409cb146d44a6ae201e55b35a4af8e95457dd"},
{file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:403c0911cd5d5791605808b942c88a8155c2592e05332d2bf78f18697a5fa15e"},
{file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aeaf541ddbad8311a87dd695ed9642401131ea39ad7bc8cf3ef3967fd093b626"},
{file = "multidict-6.0.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e4972624066095e52b569e02b5ca97dbd7a7ddd4294bf4e7247d52635630dd83"},
{file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d946b0a9eb8aaa590df1fe082cee553ceab173e6cb5b03239716338629c50c7a"},
{file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b55358304d7a73d7bdf5de62494aaf70bd33015831ffd98bc498b433dfe5b10c"},
{file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:a3145cb08d8625b2d3fee1b2d596a8766352979c9bffe5d7833e0503d0f0b5e5"},
{file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:d65f25da8e248202bd47445cec78e0025c0fe7582b23ec69c3b27a640dd7a8e3"},
{file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c9bf56195c6bbd293340ea82eafd0071cb3d450c703d2c93afb89f93b8386ccc"},
{file = "multidict-6.0.5-cp37-cp37m-win32.whl", hash = "sha256:69db76c09796b313331bb7048229e3bee7928eb62bab5e071e9f7fcc4879caee"},
{file = "multidict-6.0.5-cp37-cp37m-win_amd64.whl", hash = "sha256:fce28b3c8a81b6b36dfac9feb1de115bab619b3c13905b419ec71d03a3fc1423"},
{file = "multidict-6.0.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:76f067f5121dcecf0d63a67f29080b26c43c71a98b10c701b0677e4a065fbd54"},
{file = "multidict-6.0.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b82cc8ace10ab5bd93235dfaab2021c70637005e1ac787031f4d1da63d493c1d"},
{file = "multidict-6.0.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5cb241881eefd96b46f89b1a056187ea8e9ba14ab88ba632e68d7a2ecb7aadf7"},
{file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8e94e6912639a02ce173341ff62cc1201232ab86b8a8fcc05572741a5dc7d93"},
{file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09a892e4a9fb47331da06948690ae38eaa2426de97b4ccbfafbdcbe5c8f37ff8"},
{file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55205d03e8a598cfc688c71ca8ea5f66447164efff8869517f175ea632c7cb7b"},
{file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37b15024f864916b4951adb95d3a80c9431299080341ab9544ed148091b53f50"},
{file = "multidict-6.0.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2a1dee728b52b33eebff5072817176c172050d44d67befd681609b4746e1c2e"},
{file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:edd08e6f2f1a390bf137080507e44ccc086353c8e98c657e666c017718561b89"},
{file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:60d698e8179a42ec85172d12f50b1668254628425a6bd611aba022257cac1386"},
{file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:3d25f19500588cbc47dc19081d78131c32637c25804df8414463ec908631e453"},
{file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:4cc0ef8b962ac7a5e62b9e826bd0cd5040e7d401bc45a6835910ed699037a461"},
{file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:eca2e9d0cc5a889850e9bbd68e98314ada174ff6ccd1129500103df7a94a7a44"},
{file = "multidict-6.0.5-cp38-cp38-win32.whl", hash = "sha256:4a6a4f196f08c58c59e0b8ef8ec441d12aee4125a7d4f4fef000ccb22f8d7241"},
{file = "multidict-6.0.5-cp38-cp38-win_amd64.whl", hash = "sha256:0275e35209c27a3f7951e1ce7aaf93ce0d163b28948444bec61dd7badc6d3f8c"},
{file = "multidict-6.0.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e7be68734bd8c9a513f2b0cfd508802d6609da068f40dc57d4e3494cefc92929"},
{file = "multidict-6.0.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1d9ea7a7e779d7a3561aade7d596649fbecfa5c08a7674b11b423783217933f9"},
{file = "multidict-6.0.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ea1456df2a27c73ce51120fa2f519f1bea2f4a03a917f4a43c8707cf4cbbae1a"},
{file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf590b134eb70629e350691ecca88eac3e3b8b3c86992042fb82e3cb1830d5e1"},
{file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5c0631926c4f58e9a5ccce555ad7747d9a9f8b10619621f22f9635f069f6233e"},
{file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dce1c6912ab9ff5f179eaf6efe7365c1f425ed690b03341911bf4939ef2f3046"},
{file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0868d64af83169e4d4152ec612637a543f7a336e4a307b119e98042e852ad9c"},
{file = "multidict-6.0.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:141b43360bfd3bdd75f15ed811850763555a251e38b2405967f8e25fb43f7d40"},
{file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7df704ca8cf4a073334e0427ae2345323613e4df18cc224f647f251e5e75a527"},
{file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6214c5a5571802c33f80e6c84713b2c79e024995b9c5897f794b43e714daeec9"},
{file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:cd6c8fca38178e12c00418de737aef1261576bd1b6e8c6134d3e729a4e858b38"},
{file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:e02021f87a5b6932fa6ce916ca004c4d441509d33bbdbeca70d05dff5e9d2479"},
{file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ebd8d160f91a764652d3e51ce0d2956b38efe37c9231cd82cfc0bed2e40b581c"},
{file = "multidict-6.0.5-cp39-cp39-win32.whl", hash = "sha256:04da1bb8c8dbadf2a18a452639771951c662c5ad03aefe4884775454be322c9b"},
{file = "multidict-6.0.5-cp39-cp39-win_amd64.whl", hash = "sha256:d6f6d4f185481c9669b9447bf9d9cf3b95a0e9df9d169bbc17e363b7d5487755"},
{file = "multidict-6.0.5-py3-none-any.whl", hash = "sha256:0d63c74e3d7ab26de115c49bffc92cc77ed23395303d496eae515d4204a625e7"},
{file = "multidict-6.0.5.tar.gz", hash = "sha256:f7e301075edaf50500f0b341543c41194d8df3ae5caf4702f2095f3ca73dd8da"},
]
[[package]]
name = "packaging"
version = "24.1"
description = "Core utilities for Python packages"
optional = false
python-versions = ">=3.8"
files = [
{file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"},
{file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"},
]
[[package]]
name = "parse"
version = "1.20.2"
description = "parse() is the opposite of format()"
optional = false
python-versions = "*"
files = [
{file = "parse-1.20.2-py2.py3-none-any.whl", hash = "sha256:967095588cb802add9177d0c0b6133b5ba33b1ea9007ca800e526f42a85af558"},
{file = "parse-1.20.2.tar.gz", hash = "sha256:b41d604d16503c79d81af5165155c0b20f6c8d6c559efa66b4b695c3e5a0a0ce"},
]
[[package]]
name = "parse-type"
version = "0.6.2"
description = "Simplifies to build parse types based on the parse module"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*"
files = [
{file = "parse_type-0.6.2-py2.py3-none-any.whl", hash = "sha256:06d39a8b70fde873eb2a131141a0e79bb34a432941fb3d66fad247abafc9766c"},
{file = "parse_type-0.6.2.tar.gz", hash = "sha256:79b1f2497060d0928bc46016793f1fca1057c4aacdf15ef876aa48d75a73a355"},
]
[package.dependencies]
parse = {version = ">=1.18.0", markers = "python_version >= \"3.0\""}
six = ">=1.15"
[package.extras]
develop = ["build (>=0.5.1)", "coverage (>=4.4)", "pylint", "pytest (<5.0)", "pytest (>=5.0)", "pytest-cov", "pytest-html (>=1.19.0)", "ruff", "tox (>=2.8,<4.0)", "twine (>=1.13.0)", "virtualenv (<20.22.0)", "virtualenv (>=20.0.0)"]
docs = ["Sphinx (>=1.6)", "sphinx-bootstrap-theme (>=0.6.0)"]
testing = ["pytest (<5.0)", "pytest (>=5.0)", "pytest-html (>=1.19.0)"]
[[package]]
name = "pluggy"
version = "1.5.0"
description = "plugin and hook calling mechanisms for python"
optional = false
python-versions = ">=3.8"
files = [
{file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"},
{file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"},
]
[package.extras]
dev = ["pre-commit", "tox"]
testing = ["pytest", "pytest-benchmark"]
[[package]]
name = "pycryptodomex"
version = "3.20.0"
description = "Cryptographic library for Python"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
files = [
{file = "pycryptodomex-3.20.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:645bd4ca6f543685d643dadf6a856cc382b654cc923460e3a10a49c1b3832aeb"},
{file = "pycryptodomex-3.20.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:ff5c9a67f8a4fba4aed887216e32cbc48f2a6fb2673bb10a99e43be463e15913"},
{file = "pycryptodomex-3.20.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:8ee606964553c1a0bc74057dd8782a37d1c2bc0f01b83193b6f8bb14523b877b"},
{file = "pycryptodomex-3.20.0-cp27-cp27m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7805830e0c56d88f4d491fa5ac640dfc894c5ec570d1ece6ed1546e9df2e98d6"},
{file = "pycryptodomex-3.20.0-cp27-cp27m-musllinux_1_1_aarch64.whl", hash = "sha256:bc3ee1b4d97081260d92ae813a83de4d2653206967c4a0a017580f8b9548ddbc"},
{file = "pycryptodomex-3.20.0-cp27-cp27m-win32.whl", hash = "sha256:8af1a451ff9e123d0d8bd5d5e60f8e3315c3a64f3cdd6bc853e26090e195cdc8"},
{file = "pycryptodomex-3.20.0-cp27-cp27m-win_amd64.whl", hash = "sha256:cbe71b6712429650e3883dc81286edb94c328ffcd24849accac0a4dbcc76958a"},
{file = "pycryptodomex-3.20.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:76bd15bb65c14900d98835fcd10f59e5e0435077431d3a394b60b15864fddd64"},
{file = "pycryptodomex-3.20.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:653b29b0819605fe0898829c8ad6400a6ccde096146730c2da54eede9b7b8baa"},
{file = "pycryptodomex-3.20.0-cp27-cp27mu-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62a5ec91388984909bb5398ea49ee61b68ecb579123694bffa172c3b0a107079"},
{file = "pycryptodomex-3.20.0-cp27-cp27mu-musllinux_1_1_aarch64.whl", hash = "sha256:108e5f1c1cd70ffce0b68739c75734437c919d2eaec8e85bffc2c8b4d2794305"},
{file = "pycryptodomex-3.20.0-cp35-abi3-macosx_10_9_universal2.whl", hash = "sha256:59af01efb011b0e8b686ba7758d59cf4a8263f9ad35911bfe3f416cee4f5c08c"},
{file = "pycryptodomex-3.20.0-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:82ee7696ed8eb9a82c7037f32ba9b7c59e51dda6f105b39f043b6ef293989cb3"},
{file = "pycryptodomex-3.20.0-cp35-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91852d4480a4537d169c29a9d104dda44094c78f1f5b67bca76c29a91042b623"},
{file = "pycryptodomex-3.20.0-cp35-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bca649483d5ed251d06daf25957f802e44e6bb6df2e8f218ae71968ff8f8edc4"},
{file = "pycryptodomex-3.20.0-cp35-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e186342cfcc3aafaad565cbd496060e5a614b441cacc3995ef0091115c1f6c5"},
{file = "pycryptodomex-3.20.0-cp35-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:25cd61e846aaab76d5791d006497134602a9e451e954833018161befc3b5b9ed"},
{file = "pycryptodomex-3.20.0-cp35-abi3-musllinux_1_1_i686.whl", hash = "sha256:9c682436c359b5ada67e882fec34689726a09c461efd75b6ea77b2403d5665b7"},
{file = "pycryptodomex-3.20.0-cp35-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:7a7a8f33a1f1fb762ede6cc9cbab8f2a9ba13b196bfaf7bc6f0b39d2ba315a43"},
{file = "pycryptodomex-3.20.0-cp35-abi3-win32.whl", hash = "sha256:c39778fd0548d78917b61f03c1fa8bfda6cfcf98c767decf360945fe6f97461e"},
{file = "pycryptodomex-3.20.0-cp35-abi3-win_amd64.whl", hash = "sha256:2a47bcc478741b71273b917232f521fd5704ab4b25d301669879e7273d3586cc"},
{file = "pycryptodomex-3.20.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:1be97461c439a6af4fe1cf8bf6ca5936d3db252737d2f379cc6b2e394e12a458"},
{file = "pycryptodomex-3.20.0-pp27-pypy_73-win32.whl", hash = "sha256:19764605feea0df966445d46533729b645033f134baeb3ea26ad518c9fdf212c"},
{file = "pycryptodomex-3.20.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:f2e497413560e03421484189a6b65e33fe800d3bd75590e6d78d4dfdb7accf3b"},
{file = "pycryptodomex-3.20.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e48217c7901edd95f9f097feaa0388da215ed14ce2ece803d3f300b4e694abea"},
{file = "pycryptodomex-3.20.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d00fe8596e1cc46b44bf3907354e9377aa030ec4cd04afbbf6e899fc1e2a7781"},
{file = "pycryptodomex-3.20.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:88afd7a3af7ddddd42c2deda43d53d3dfc016c11327d0915f90ca34ebda91499"},
{file = "pycryptodomex-3.20.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d3584623e68a5064a04748fb6d76117a21a7cb5eaba20608a41c7d0c61721794"},
{file = "pycryptodomex-3.20.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0daad007b685db36d977f9de73f61f8da2a7104e20aca3effd30752fd56f73e1"},
{file = "pycryptodomex-3.20.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5dcac11031a71348faaed1f403a0debd56bf5404232284cf8c761ff918886ebc"},
{file = "pycryptodomex-3.20.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:69138068268127cd605e03438312d8f271135a33140e2742b417d027a0539427"},
{file = "pycryptodomex-3.20.0.tar.gz", hash = "sha256:7a710b79baddd65b806402e14766c721aee8fb83381769c27920f26476276c1e"},
]
[[package]]
name = "pytest"
version = "8.2.2"
description = "pytest: simple powerful testing with Python"
optional = false
python-versions = ">=3.8"
files = [
{file = "pytest-8.2.2-py3-none-any.whl", hash = "sha256:c434598117762e2bd304e526244f67bf66bbd7b5d6cf22138be51ff661980343"},
{file = "pytest-8.2.2.tar.gz", hash = "sha256:de4bb8104e201939ccdc688b27a89a7be2079b22e2bd2b07f806b6ba71117977"},
]
[package.dependencies]
colorama = {version = "*", markers = "sys_platform == \"win32\""}
exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""}
iniconfig = "*"
packaging = "*"
pluggy = ">=1.5,<2.0"
tomli = {version = ">=1", markers = "python_version < \"3.11\""}
[package.extras]
dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
[[package]]
name = "pytest-asyncio"
version = "0.23.7"
description = "Pytest support for asyncio"
optional = false
python-versions = ">=3.8"
files = [
{file = "pytest_asyncio-0.23.7-py3-none-any.whl", hash = "sha256:009b48127fbe44518a547bddd25611551b0e43ccdbf1e67d12479f569832c20b"},
{file = "pytest_asyncio-0.23.7.tar.gz", hash = "sha256:5f5c72948f4c49e7db4f29f2521d4031f1c27f86e57b046126654083d4770268"},
]
[package.dependencies]
pytest = ">=7.0.0,<9"
[package.extras]
docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"]
testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"]
[[package]]
name = "pytest-cov"
version = "5.0.0"
description = "Pytest plugin for measuring coverage."
optional = false
python-versions = ">=3.8"
files = [
{file = "pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857"},
{file = "pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652"},
]
[package.dependencies]
coverage = {version = ">=5.2.1", extras = ["toml"]}
pytest = ">=4.6"
[package.extras]
testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"]
[[package]]
name = "pyyaml"
version = "6.0.1"
description = "YAML parser and emitter for Python"
optional = false
python-versions = ">=3.6"
files = [
{file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"},
{file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"},
{file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"},
{file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"},
{file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"},
{file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"},
{file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"},
{file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"},
{file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"},
{file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"},
{file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"},
{file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"},
{file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"},
{file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"},
{file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"},
{file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"},
{file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"},
{file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"},
{file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"},
{file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"},
{file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"},
{file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"},
{file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"},
{file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"},
{file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"},
{file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"},
{file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"},
{file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"},
{file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"},
{file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"},
{file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"},
{file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"},
{file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"},
{file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"},
{file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"},
{file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"},
{file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"},
{file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"},
{file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"},
{file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"},
{file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"},
{file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"},
{file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"},
{file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"},
{file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"},
{file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"},
{file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"},
{file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"},
{file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"},
{file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"},
{file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"},
]
[[package]]
name = "requests"
version = "2.32.3"
description = "Python HTTP for Humans."
optional = false
python-versions = ">=3.8"
files = [
{file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"},
{file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"},
]
[package.dependencies]
certifi = ">=2017.4.17"
charset-normalizer = ">=2,<4"
idna = ">=2.5,<4"
urllib3 = ">=1.21.1,<3"
[package.extras]
socks = ["PySocks (>=1.5.6,!=1.5.7)"]
use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
[[package]]
name = "six"
version = "1.16.0"
description = "Python 2 and 3 compatibility utilities"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
files = [
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
]
[[package]]
name = "tomli"
version = "2.0.1"
description = "A lil' TOML parser"
optional = false
python-versions = ">=3.7"
files = [
{file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
{file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
]
[[package]]
name = "urllib3"
version = "1.26.19"
description = "HTTP library with thread-safe connection pooling, file post, and more."
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7"
files = [
{file = "urllib3-1.26.19-py2.py3-none-any.whl", hash = "sha256:37a0344459b199fce0e80b0d3569837ec6b6937435c5244e7fd73fa6006830f3"},
{file = "urllib3-1.26.19.tar.gz", hash = "sha256:3e3d753a8618b86d7de333b4223005f68720bcd6a7d2bcb9fbd2229ec7c1e429"},
]
[package.extras]
brotli = ["brotli (==1.0.9)", "brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"]
secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"]
socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
[[package]]
name = "vcrpy"
version = "6.0.1"
description = "Automatically mock your HTTP interactions to simplify and speed up testing"
optional = false
python-versions = ">=3.8"
files = [
{file = "vcrpy-6.0.1.tar.gz", hash = "sha256:9e023fee7f892baa0bbda2f7da7c8ac51165c1c6e38ff8688683a12a4bde9278"},
]
[package.dependencies]
PyYAML = "*"
urllib3 = {version = "<2", markers = "platform_python_implementation == \"PyPy\" or python_version < \"3.10\""}
wrapt = "*"
yarl = "*"
[package.extras]
tests = ["Werkzeug (==2.0.3)", "aiohttp", "boto3", "httplib2", "httpx", "pytest", "pytest-aiohttp", "pytest-asyncio", "pytest-cov", "pytest-httpbin", "requests (>=2.22.0)", "tornado", "urllib3"]
[[package]]
name = "wrapt"
version = "1.16.0"
description = "Module for decorators, wrappers and monkey patching."
optional = false
python-versions = ">=3.6"
files = [
{file = "wrapt-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ffa565331890b90056c01db69c0fe634a776f8019c143a5ae265f9c6bc4bd6d4"},
{file = "wrapt-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e4fdb9275308292e880dcbeb12546df7f3e0f96c6b41197e0cf37d2826359020"},
{file = "wrapt-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb2dee3874a500de01c93d5c71415fcaef1d858370d405824783e7a8ef5db440"},
{file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a88e6010048489cda82b1326889ec075a8c856c2e6a256072b28eaee3ccf487"},
{file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac83a914ebaf589b69f7d0a1277602ff494e21f4c2f743313414378f8f50a4cf"},
{file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:73aa7d98215d39b8455f103de64391cb79dfcad601701a3aa0dddacf74911d72"},
{file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:807cc8543a477ab7422f1120a217054f958a66ef7314f76dd9e77d3f02cdccd0"},
{file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bf5703fdeb350e36885f2875d853ce13172ae281c56e509f4e6eca049bdfb136"},
{file = "wrapt-1.16.0-cp310-cp310-win32.whl", hash = "sha256:f6b2d0c6703c988d334f297aa5df18c45e97b0af3679bb75059e0e0bd8b1069d"},
{file = "wrapt-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:decbfa2f618fa8ed81c95ee18a387ff973143c656ef800c9f24fb7e9c16054e2"},
{file = "wrapt-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09"},
{file = "wrapt-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d"},
{file = "wrapt-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389"},
{file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060"},
{file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1"},
{file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3"},
{file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956"},
{file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d"},
{file = "wrapt-1.16.0-cp311-cp311-win32.whl", hash = "sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362"},
{file = "wrapt-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89"},
{file = "wrapt-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b"},
{file = "wrapt-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36"},
{file = "wrapt-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73"},
{file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809"},
{file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b"},
{file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81"},
{file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9"},
{file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c"},
{file = "wrapt-1.16.0-cp312-cp312-win32.whl", hash = "sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc"},
{file = "wrapt-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8"},
{file = "wrapt-1.16.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d462f28826f4657968ae51d2181a074dfe03c200d6131690b7d65d55b0f360f8"},
{file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a33a747400b94b6d6b8a165e4480264a64a78c8a4c734b62136062e9a248dd39"},
{file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3646eefa23daeba62643a58aac816945cadc0afaf21800a1421eeba5f6cfb9c"},
{file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ebf019be5c09d400cf7b024aa52b1f3aeebeff51550d007e92c3c1c4afc2a40"},
{file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:0d2691979e93d06a95a26257adb7bfd0c93818e89b1406f5a28f36e0d8c1e1fc"},
{file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:1acd723ee2a8826f3d53910255643e33673e1d11db84ce5880675954183ec47e"},
{file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:bc57efac2da352a51cc4658878a68d2b1b67dbe9d33c36cb826ca449d80a8465"},
{file = "wrapt-1.16.0-cp36-cp36m-win32.whl", hash = "sha256:da4813f751142436b075ed7aa012a8778aa43a99f7b36afe9b742d3ed8bdc95e"},
{file = "wrapt-1.16.0-cp36-cp36m-win_amd64.whl", hash = "sha256:6f6eac2360f2d543cc875a0e5efd413b6cbd483cb3ad7ebf888884a6e0d2e966"},
{file = "wrapt-1.16.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a0ea261ce52b5952bf669684a251a66df239ec6d441ccb59ec7afa882265d593"},
{file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bd2d7ff69a2cac767fbf7a2b206add2e9a210e57947dd7ce03e25d03d2de292"},
{file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9159485323798c8dc530a224bd3ffcf76659319ccc7bbd52e01e73bd0241a0c5"},
{file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a86373cf37cd7764f2201b76496aba58a52e76dedfaa698ef9e9688bfd9e41cf"},
{file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:73870c364c11f03ed072dda68ff7aea6d2a3a5c3fe250d917a429c7432e15228"},
{file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b935ae30c6e7400022b50f8d359c03ed233d45b725cfdd299462f41ee5ffba6f"},
{file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:db98ad84a55eb09b3c32a96c576476777e87c520a34e2519d3e59c44710c002c"},
{file = "wrapt-1.16.0-cp37-cp37m-win32.whl", hash = "sha256:9153ed35fc5e4fa3b2fe97bddaa7cbec0ed22412b85bcdaf54aeba92ea37428c"},
{file = "wrapt-1.16.0-cp37-cp37m-win_amd64.whl", hash = "sha256:66dfbaa7cfa3eb707bbfcd46dab2bc6207b005cbc9caa2199bcbc81d95071a00"},
{file = "wrapt-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1dd50a2696ff89f57bd8847647a1c363b687d3d796dc30d4dd4a9d1689a706f0"},
{file = "wrapt-1.16.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:44a2754372e32ab315734c6c73b24351d06e77ffff6ae27d2ecf14cf3d229202"},
{file = "wrapt-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e9723528b9f787dc59168369e42ae1c3b0d3fadb2f1a71de14531d321ee05b0"},
{file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbed418ba5c3dce92619656802cc5355cb679e58d0d89b50f116e4a9d5a9603e"},
{file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:941988b89b4fd6b41c3f0bfb20e92bd23746579736b7343283297c4c8cbae68f"},
{file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6a42cd0cfa8ffc1915aef79cb4284f6383d8a3e9dcca70c445dcfdd639d51267"},
{file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ca9b6085e4f866bd584fb135a041bfc32cab916e69f714a7d1d397f8c4891ca"},
{file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5e49454f19ef621089e204f862388d29e6e8d8b162efce05208913dde5b9ad6"},
{file = "wrapt-1.16.0-cp38-cp38-win32.whl", hash = "sha256:c31f72b1b6624c9d863fc095da460802f43a7c6868c5dda140f51da24fd47d7b"},
{file = "wrapt-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:490b0ee15c1a55be9c1bd8609b8cecd60e325f0575fc98f50058eae366e01f41"},
{file = "wrapt-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9b201ae332c3637a42f02d1045e1d0cccfdc41f1f2f801dafbaa7e9b4797bfc2"},
{file = "wrapt-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2076fad65c6736184e77d7d4729b63a6d1ae0b70da4868adeec40989858eb3fb"},
{file = "wrapt-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5cd603b575ebceca7da5a3a251e69561bec509e0b46e4993e1cac402b7247b8"},
{file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b47cfad9e9bbbed2339081f4e346c93ecd7ab504299403320bf85f7f85c7d46c"},
{file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8212564d49c50eb4565e502814f694e240c55551a5f1bc841d4fcaabb0a9b8a"},
{file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5f15814a33e42b04e3de432e573aa557f9f0f56458745c2074952f564c50e664"},
{file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db2e408d983b0e61e238cf579c09ef7020560441906ca990fe8412153e3b291f"},
{file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:edfad1d29c73f9b863ebe7082ae9321374ccb10879eeabc84ba3b69f2579d537"},
{file = "wrapt-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed867c42c268f876097248e05b6117a65bcd1e63b779e916fe2e33cd6fd0d3c3"},
{file = "wrapt-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:eb1b046be06b0fce7249f1d025cd359b4b80fc1c3e24ad9eca33e0dcdb2e4a35"},
{file = "wrapt-1.16.0-py3-none-any.whl", hash = "sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1"},
{file = "wrapt-1.16.0.tar.gz", hash = "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d"},
]
[[package]]
name = "yarl"
version = "1.9.4"
description = "Yet another URL library"
optional = false
python-versions = ">=3.7"
files = [
{file = "yarl-1.9.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a8c1df72eb746f4136fe9a2e72b0c9dc1da1cbd23b5372f94b5820ff8ae30e0e"},
{file = "yarl-1.9.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a3a6ed1d525bfb91b3fc9b690c5a21bb52de28c018530ad85093cc488bee2dd2"},
{file = "yarl-1.9.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c38c9ddb6103ceae4e4498f9c08fac9b590c5c71b0370f98714768e22ac6fa66"},
{file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d9e09c9d74f4566e905a0b8fa668c58109f7624db96a2171f21747abc7524234"},
{file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8477c1ee4bd47c57d49621a062121c3023609f7a13b8a46953eb6c9716ca392"},
{file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5ff2c858f5f6a42c2a8e751100f237c5e869cbde669a724f2062d4c4ef93551"},
{file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:357495293086c5b6d34ca9616a43d329317feab7917518bc97a08f9e55648455"},
{file = "yarl-1.9.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54525ae423d7b7a8ee81ba189f131054defdb122cde31ff17477951464c1691c"},
{file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:801e9264d19643548651b9db361ce3287176671fb0117f96b5ac0ee1c3530d53"},
{file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e516dc8baf7b380e6c1c26792610230f37147bb754d6426462ab115a02944385"},
{file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:7d5aaac37d19b2904bb9dfe12cdb08c8443e7ba7d2852894ad448d4b8f442863"},
{file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:54beabb809ffcacbd9d28ac57b0db46e42a6e341a030293fb3185c409e626b8b"},
{file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bac8d525a8dbc2a1507ec731d2867025d11ceadcb4dd421423a5d42c56818541"},
{file = "yarl-1.9.4-cp310-cp310-win32.whl", hash = "sha256:7855426dfbddac81896b6e533ebefc0af2f132d4a47340cee6d22cac7190022d"},
{file = "yarl-1.9.4-cp310-cp310-win_amd64.whl", hash = "sha256:848cd2a1df56ddbffeb375535fb62c9d1645dde33ca4d51341378b3f5954429b"},
{file = "yarl-1.9.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:35a2b9396879ce32754bd457d31a51ff0a9d426fd9e0e3c33394bf4b9036b099"},
{file = "yarl-1.9.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c7d56b293cc071e82532f70adcbd8b61909eec973ae9d2d1f9b233f3d943f2c"},
{file = "yarl-1.9.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d8a1c6c0be645c745a081c192e747c5de06e944a0d21245f4cf7c05e457c36e0"},
{file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b3c1ffe10069f655ea2d731808e76e0f452fc6c749bea04781daf18e6039525"},
{file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:549d19c84c55d11687ddbd47eeb348a89df9cb30e1993f1b128f4685cd0ebbf8"},
{file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7409f968456111140c1c95301cadf071bd30a81cbd7ab829169fb9e3d72eae9"},
{file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e23a6d84d9d1738dbc6e38167776107e63307dfc8ad108e580548d1f2c587f42"},
{file = "yarl-1.9.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d8b889777de69897406c9fb0b76cdf2fd0f31267861ae7501d93003d55f54fbe"},
{file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:03caa9507d3d3c83bca08650678e25364e1843b484f19986a527630ca376ecce"},
{file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4e9035df8d0880b2f1c7f5031f33f69e071dfe72ee9310cfc76f7b605958ceb9"},
{file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:c0ec0ed476f77db9fb29bca17f0a8fcc7bc97ad4c6c1d8959c507decb22e8572"},
{file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:ee04010f26d5102399bd17f8df8bc38dc7ccd7701dc77f4a68c5b8d733406958"},
{file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:49a180c2e0743d5d6e0b4d1a9e5f633c62eca3f8a86ba5dd3c471060e352ca98"},
{file = "yarl-1.9.4-cp311-cp311-win32.whl", hash = "sha256:81eb57278deb6098a5b62e88ad8281b2ba09f2f1147c4767522353eaa6260b31"},
{file = "yarl-1.9.4-cp311-cp311-win_amd64.whl", hash = "sha256:d1d2532b340b692880261c15aee4dc94dd22ca5d61b9db9a8a361953d36410b1"},
{file = "yarl-1.9.4-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0d2454f0aef65ea81037759be5ca9947539667eecebca092733b2eb43c965a81"},
{file = "yarl-1.9.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:44d8ffbb9c06e5a7f529f38f53eda23e50d1ed33c6c869e01481d3fafa6b8142"},
{file = "yarl-1.9.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:aaaea1e536f98754a6e5c56091baa1b6ce2f2700cc4a00b0d49eca8dea471074"},
{file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3777ce5536d17989c91696db1d459574e9a9bd37660ea7ee4d3344579bb6f129"},
{file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fc5fc1eeb029757349ad26bbc5880557389a03fa6ada41703db5e068881e5f2"},
{file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea65804b5dc88dacd4a40279af0cdadcfe74b3e5b4c897aa0d81cf86927fee78"},
{file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa102d6d280a5455ad6a0f9e6d769989638718e938a6a0a2ff3f4a7ff8c62cc4"},
{file = "yarl-1.9.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09efe4615ada057ba2d30df871d2f668af661e971dfeedf0c159927d48bbeff0"},
{file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:008d3e808d03ef28542372d01057fd09168419cdc8f848efe2804f894ae03e51"},
{file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:6f5cb257bc2ec58f437da2b37a8cd48f666db96d47b8a3115c29f316313654ff"},
{file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:992f18e0ea248ee03b5a6e8b3b4738850ae7dbb172cc41c966462801cbf62cf7"},
{file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:0e9d124c191d5b881060a9e5060627694c3bdd1fe24c5eecc8d5d7d0eb6faabc"},
{file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3986b6f41ad22988e53d5778f91855dc0399b043fc8946d4f2e68af22ee9ff10"},
{file = "yarl-1.9.4-cp312-cp312-win32.whl", hash = "sha256:4b21516d181cd77ebd06ce160ef8cc2a5e9ad35fb1c5930882baff5ac865eee7"},
{file = "yarl-1.9.4-cp312-cp312-win_amd64.whl", hash = "sha256:a9bd00dc3bc395a662900f33f74feb3e757429e545d831eef5bb280252631984"},
{file = "yarl-1.9.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:63b20738b5aac74e239622d2fe30df4fca4942a86e31bf47a81a0e94c14df94f"},
{file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7d7f7de27b8944f1fee2c26a88b4dabc2409d2fea7a9ed3df79b67277644e17"},
{file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c74018551e31269d56fab81a728f683667e7c28c04e807ba08f8c9e3bba32f14"},
{file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ca06675212f94e7a610e85ca36948bb8fc023e458dd6c63ef71abfd482481aa5"},
{file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5aef935237d60a51a62b86249839b51345f47564208c6ee615ed2a40878dccdd"},
{file = "yarl-1.9.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b134fd795e2322b7684155b7855cc99409d10b2e408056db2b93b51a52accc7"},
{file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d25039a474c4c72a5ad4b52495056f843a7ff07b632c1b92ea9043a3d9950f6e"},
{file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f7d6b36dd2e029b6bcb8a13cf19664c7b8e19ab3a58e0fefbb5b8461447ed5ec"},
{file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:957b4774373cf6f709359e5c8c4a0af9f6d7875db657adb0feaf8d6cb3c3964c"},
{file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:d7eeb6d22331e2fd42fce928a81c697c9ee2d51400bd1a28803965883e13cead"},
{file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:6a962e04b8f91f8c4e5917e518d17958e3bdee71fd1d8b88cdce74dd0ebbf434"},
{file = "yarl-1.9.4-cp37-cp37m-win32.whl", hash = "sha256:f3bc6af6e2b8f92eced34ef6a96ffb248e863af20ef4fde9448cc8c9b858b749"},
{file = "yarl-1.9.4-cp37-cp37m-win_amd64.whl", hash = "sha256:ad4d7a90a92e528aadf4965d685c17dacff3df282db1121136c382dc0b6014d2"},
{file = "yarl-1.9.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ec61d826d80fc293ed46c9dd26995921e3a82146feacd952ef0757236fc137be"},
{file = "yarl-1.9.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8be9e837ea9113676e5754b43b940b50cce76d9ed7d2461df1af39a8ee674d9f"},
{file = "yarl-1.9.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:bef596fdaa8f26e3d66af846bbe77057237cb6e8efff8cd7cc8dff9a62278bbf"},
{file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d47552b6e52c3319fede1b60b3de120fe83bde9b7bddad11a69fb0af7db32f1"},
{file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84fc30f71689d7fc9168b92788abc977dc8cefa806909565fc2951d02f6b7d57"},
{file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4aa9741085f635934f3a2583e16fcf62ba835719a8b2b28fb2917bb0537c1dfa"},
{file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:206a55215e6d05dbc6c98ce598a59e6fbd0c493e2de4ea6cc2f4934d5a18d130"},
{file = "yarl-1.9.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07574b007ee20e5c375a8fe4a0789fad26db905f9813be0f9fef5a68080de559"},
{file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5a2e2433eb9344a163aced6a5f6c9222c0786e5a9e9cac2c89f0b28433f56e23"},
{file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6ad6d10ed9b67a382b45f29ea028f92d25bc0bc1daf6c5b801b90b5aa70fb9ec"},
{file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:6fe79f998a4052d79e1c30eeb7d6c1c1056ad33300f682465e1b4e9b5a188b78"},
{file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a825ec844298c791fd28ed14ed1bffc56a98d15b8c58a20e0e08c1f5f2bea1be"},
{file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8619d6915b3b0b34420cf9b2bb6d81ef59d984cb0fde7544e9ece32b4b3043c3"},
{file = "yarl-1.9.4-cp38-cp38-win32.whl", hash = "sha256:686a0c2f85f83463272ddffd4deb5e591c98aac1897d65e92319f729c320eece"},
{file = "yarl-1.9.4-cp38-cp38-win_amd64.whl", hash = "sha256:a00862fb23195b6b8322f7d781b0dc1d82cb3bcac346d1e38689370cc1cc398b"},
{file = "yarl-1.9.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:604f31d97fa493083ea21bd9b92c419012531c4e17ea6da0f65cacdcf5d0bd27"},
{file = "yarl-1.9.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8a854227cf581330ffa2c4824d96e52ee621dd571078a252c25e3a3b3d94a1b1"},
{file = "yarl-1.9.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ba6f52cbc7809cd8d74604cce9c14868306ae4aa0282016b641c661f981a6e91"},
{file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6327976c7c2f4ee6816eff196e25385ccc02cb81427952414a64811037bbc8b"},
{file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8397a3817d7dcdd14bb266283cd1d6fc7264a48c186b986f32e86d86d35fbac5"},
{file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e0381b4ce23ff92f8170080c97678040fc5b08da85e9e292292aba67fdac6c34"},
{file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23d32a2594cb5d565d358a92e151315d1b2268bc10f4610d098f96b147370136"},
{file = "yarl-1.9.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ddb2a5c08a4eaaba605340fdee8fc08e406c56617566d9643ad8bf6852778fc7"},
{file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:26a1dc6285e03f3cc9e839a2da83bcbf31dcb0d004c72d0730e755b33466c30e"},
{file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:18580f672e44ce1238b82f7fb87d727c4a131f3a9d33a5e0e82b793362bf18b4"},
{file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:29e0f83f37610f173eb7e7b5562dd71467993495e568e708d99e9d1944f561ec"},
{file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:1f23e4fe1e8794f74b6027d7cf19dc25f8b63af1483d91d595d4a07eca1fb26c"},
{file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:db8e58b9d79200c76956cefd14d5c90af54416ff5353c5bfd7cbe58818e26ef0"},
{file = "yarl-1.9.4-cp39-cp39-win32.whl", hash = "sha256:c7224cab95645c7ab53791022ae77a4509472613e839dab722a72abe5a684575"},
{file = "yarl-1.9.4-cp39-cp39-win_amd64.whl", hash = "sha256:824d6c50492add5da9374875ce72db7a0733b29c2394890aef23d533106e2b15"},
{file = "yarl-1.9.4-py3-none-any.whl", hash = "sha256:928cecb0ef9d5a7946eb6ff58417ad2fe9375762382f1bf5c55e61645f2c43ad"},
{file = "yarl-1.9.4.tar.gz", hash = "sha256:566db86717cf8080b99b58b083b773a908ae40f06681e87e589a976faf8246bf"},
]
[package.dependencies]
idna = ">=2.0"
multidict = ">=4.0"
[metadata]
lock-version = "2.0"
python-versions = "^3.8"
content-hash = "7b7c8f00039ca373510a870745c9289a8abc17b479d165ddac8ba30560cbe6ad"
bdraco-freenub-69809e8/pubnub/ 0000775 0000000 0000000 00000000000 14645232030 0016160 5 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/pubnub/__init__.py 0000664 0000000 0000000 00000001064 14645232030 0020272 0 ustar 00root root 0000000 0000000 import logging
import os
PUBNUB_ROOT = os.path.dirname(os.path.abspath(__file__))
__version__ = "0.1.0"
def set_stream_logger(
name="pubnub", level=logging.ERROR, format_string=None, stream=None
):
if format_string is None:
format_string = "%(asctime)s %(name)s [%(levelname)s] %(message)s"
logger = logging.getLogger(name)
logger.setLevel(level)
handler = logging.StreamHandler(stream)
handler.setLevel(level)
formatter = logging.Formatter(format_string)
handler.setFormatter(formatter)
logger.addHandler(handler)
bdraco-freenub-69809e8/pubnub/builders.py 0000664 0000000 0000000 00000003721 14645232030 0020346 0 ustar 00root root 0000000 0000000 from abc import ABCMeta, abstractmethod
from . import utils
from .dtos import SubscribeOperation, UnsubscribeOperation
class PubSubBuilder:
__metaclass__ = ABCMeta
def __init__(self, subscription_manager):
self._subscription_manager = subscription_manager
self._channel_subscriptions = []
self._channel_group_subscriptions = []
# TODO: make the 'channel' alias
def channels(self, channels_list):
utils.extend_list(self._channel_subscriptions, channels_list)
return self
def channel_groups(self, channel_groups_list):
utils.extend_list(self._channel_group_subscriptions, channel_groups_list)
return self
@abstractmethod
def execute(self):
pass
class SubscribeBuilder(PubSubBuilder):
def __init__(self, subscription_manager):
super().__init__(subscription_manager)
self._presence_enabled = False
self._timetoken = 0
def with_presence(self):
self._presence_enabled = True
return self
def with_timetoken(self, timetoken):
self._timetoken = timetoken
return self
def channel_subscriptions(self):
return self._channel_subscriptions
def channel_group_subscriptions(self):
return self._channel_group_subscriptions
def execute(self):
subscribe_operation = SubscribeOperation(
channels=self._channel_subscriptions,
channel_groups=self._channel_group_subscriptions,
timetoken=self._timetoken,
presence_enabled=self._presence_enabled,
)
self._subscription_manager.adapt_subscribe_builder(subscribe_operation)
class UnsubscribeBuilder(PubSubBuilder):
def execute(self):
unsubscribe_operation = UnsubscribeOperation(
channels=self._channel_subscriptions,
channel_groups=self._channel_group_subscriptions,
)
self._subscription_manager.adapt_unsubscribe_builder(unsubscribe_operation)
bdraco-freenub-69809e8/pubnub/callbacks.py 0000664 0000000 0000000 00000001505 14645232030 0020452 0 ustar 00root root 0000000 0000000 from abc import ABCMeta, abstractmethod
class PNCallback:
@abstractmethod
def on_response(self, x, status):
pass
class SubscribeCallback:
__metaclass__ = ABCMeta
@abstractmethod
def status(self, pubnub, status):
pass
@abstractmethod
def message(self, pubnub, message):
pass
@abstractmethod
def presence(self, pubnub, presence):
pass
def signal(self, pubnub, signal):
pass
def channel(self, pubnub, channel):
pass
def uuid(self, pubnub, uuid):
pass
def membership(self, pubnub, membership):
pass
def message_action(self, pubnub, message_action):
pass
def file(self, pubnub, file_message):
pass
class ReconnectionCallback:
@abstractmethod
def on_reconnect(self):
pass
bdraco-freenub-69809e8/pubnub/crypto.py 0000664 0000000 0000000 00000010431 14645232030 0020051 0 ustar 00root root 0000000 0000000 import hashlib
import json
import random
from base64 import decodebytes, encodebytes
from Cryptodome.Cipher import AES
from Cryptodome.Util.Padding import pad, unpad
from pubnub.crypto_core import PubNubCrypto
Initial16bytes = "0123456789012345"
class PubNubCryptodome(PubNubCrypto):
mode = AES.MODE_CBC
fallback_mode = None
def __init__(self, pubnub_config):
self.pubnub_configuration = pubnub_config
self.mode = pubnub_config.cipher_mode
self.fallback_mode = pubnub_config.fallback_cipher_mode
def encrypt(self, key, msg, use_random_iv=False):
secret = self.get_secret(key)
initialization_vector = self.get_initialization_vector(use_random_iv)
cipher = AES.new(
bytes(secret[0:32], "utf-8"),
self.mode,
bytes(initialization_vector, "utf-8"),
)
encrypted_message = cipher.encrypt(self.pad(msg.encode("utf-8")))
msg_with_iv = self.append_random_iv(
encrypted_message, use_random_iv, bytes(initialization_vector, "utf-8")
)
return encodebytes(msg_with_iv).decode("utf-8").replace("\n", "")
def decrypt(self, key, msg, use_random_iv=False):
secret = self.get_secret(key)
decoded_message = decodebytes(msg.encode("utf-8"))
initialization_vector, extracted_message = self.extract_random_iv(
decoded_message, use_random_iv
)
cipher = AES.new(bytes(secret[0:32], "utf-8"), self.mode, initialization_vector)
try:
plain = self.depad((cipher.decrypt(extracted_message)).decode("utf-8"))
except UnicodeDecodeError as e:
if not self.fallback_mode:
raise e
cipher = AES.new(
bytes(secret[0:32], "utf-8"), self.fallback_mode, initialization_vector
)
plain = self.depad((cipher.decrypt(extracted_message)).decode("utf-8"))
try:
return json.loads(plain)
except Exception:
return plain
def append_random_iv(self, message, use_random_iv, initialization_vector):
if self.pubnub_configuration.use_random_initialization_vector or use_random_iv:
return initialization_vector + message
else:
return message
def extract_random_iv(self, message, use_random_iv):
if self.pubnub_configuration.use_random_initialization_vector or use_random_iv:
return message[0:16], message[16:]
else:
return bytes(Initial16bytes, "utf-8"), message
def get_initialization_vector(self, use_random_iv):
if self.pubnub_configuration.use_random_initialization_vector or use_random_iv:
return f"{random.randint(0, 9999999999999999):016}"
else:
return Initial16bytes
def pad(self, msg, block_size=16):
padding = block_size - (len(msg) % block_size)
return msg + (chr(padding) * padding).encode("utf-8")
def depad(self, msg):
return msg[0 : -ord(msg[-1])]
def get_secret(self, key):
return hashlib.sha256(key.encode("utf-8")).hexdigest()
class PubNubFileCrypto(PubNubCryptodome):
def encrypt(self, key, file):
secret = self.get_secret(key)
initialization_vector = self.get_initialization_vector(use_random_iv=True)
cipher = AES.new(
bytes(secret[0:32], "utf-8"),
self.mode,
bytes(initialization_vector, "utf-8"),
)
initialization_vector = bytes(initialization_vector, "utf-8")
return self.append_random_iv(
cipher.encrypt(pad(file, 16)),
use_random_iv=True,
initialization_vector=initialization_vector,
)
def decrypt(self, key, file):
secret = self.get_secret(key)
initialization_vector, extracted_file = self.extract_random_iv(
file, use_random_iv=True
)
try:
cipher = AES.new(
bytes(secret[0:32], "utf-8"), self.mode, initialization_vector
)
result = unpad(cipher.decrypt(extracted_file), 16)
except ValueError:
cipher = AES.new(
bytes(secret[0:32], "utf-8"), self.fallback_mode, initialization_vector
)
result = unpad(cipher.decrypt(extracted_file), 16)
return result
bdraco-freenub-69809e8/pubnub/crypto_core.py 0000664 0000000 0000000 00000000272 14645232030 0021063 0 ustar 00root root 0000000 0000000 from abc import abstractmethod
class PubNubCrypto:
@abstractmethod
def encrypt(self, key, msg):
pass
@abstractmethod
def decrypt(self, key, msg):
pass
bdraco-freenub-69809e8/pubnub/dtos.py 0000664 0000000 0000000 00000001724 14645232030 0017507 0 ustar 00root root 0000000 0000000 class SubscribeOperation:
def __init__(
self, channels=None, channel_groups=None, presence_enabled=None, timetoken=None
):
assert isinstance(channels, (list, tuple))
assert isinstance(channel_groups, (list, tuple))
assert isinstance(presence_enabled, bool)
assert isinstance(timetoken, int)
self.channels = channels
self.channel_groups = channel_groups
self.presence_enabled = presence_enabled
self.timetoken = timetoken
class UnsubscribeOperation:
def __init__(self, channels=None, channel_groups=None):
assert isinstance(channels, (list, tuple))
assert isinstance(channel_groups, (list, tuple))
self.channels = channels
self.channel_groups = channel_groups
class StateOperation:
def __init__(self, channels=None, channel_groups=None, state=None):
self.channels = channels
self.channel_groups = channel_groups
self.state = state
bdraco-freenub-69809e8/pubnub/endpoints/ 0000775 0000000 0000000 00000000000 14645232030 0020163 5 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/pubnub/endpoints/__init__.py 0000664 0000000 0000000 00000000000 14645232030 0022262 0 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/pubnub/endpoints/access/ 0000775 0000000 0000000 00000000000 14645232030 0021424 5 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/pubnub/endpoints/access/__init__.py 0000664 0000000 0000000 00000000000 14645232030 0023523 0 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/pubnub/endpoints/access/audit.py 0000664 0000000 0000000 00000004212 14645232030 0023103 0 ustar 00root root 0000000 0000000 from pubnub import utils
from pubnub.endpoints.endpoint import Endpoint
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.models.consumer.access_manager import PNAccessManagerAuditResult
class Audit(Endpoint):
AUDIT_PATH = "/v2/auth/audit/sub-key/%s"
def __init__(self, pubnub):
Endpoint.__init__(self, pubnub)
self._auth_keys = []
self._channels = []
self._groups = []
self._read = None
self._write = None
self._manage = None
self._ttl = None
self._sort_params = True
def auth_keys(self, auth_keys):
utils.extend_list(self._auth_keys, auth_keys)
return self
def channels(self, channels):
utils.extend_list(self._channels, channels)
return self
def channel_groups(self, channel_groups):
utils.extend_list(self._groups, channel_groups)
return self
def custom_params(self):
params = {}
if len(self._auth_keys) > 0:
params["auth"] = utils.join_items_and_encode(self._auth_keys)
if len(self._channels) > 0:
params["channel"] = utils.join_items_and_encode(self._channels)
if len(self._groups) > 0:
params["channel-group"] = utils.join_items_and_encode(self._groups)
return params
def build_path(self):
return Audit.AUDIT_PATH % self.pubnub.config.subscribe_key
def http_method(self):
return HttpMethod.GET
def validate_params(self):
self.validate_subscribe_key()
self.validate_secret_key()
def create_response(self, envelope):
return PNAccessManagerAuditResult.from_json(envelope["payload"])
def is_auth_required(self):
return False
def affected_channels(self):
return self._channels
def affected_channels_groups(self):
return self._groups
def request_timeout(self):
return self.pubnub.config.non_subscribe_request_timeout
def connect_timeout(self):
return self.pubnub.config.connect_timeout
def operation_type(self):
return PNOperationType.PNAccessManagerAudit
def name(self):
return "Grant"
bdraco-freenub-69809e8/pubnub/endpoints/access/grant.py 0000664 0000000 0000000 00000012143 14645232030 0023112 0 ustar 00root root 0000000 0000000 from pubnub import utils
from pubnub.endpoints.endpoint import Endpoint
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.errors import PNERR_PAM_INVALID_ARGUMENTS, PNERR_PAM_NO_FLAGS
from pubnub.exceptions import PubNubException
from pubnub.models.consumer.access_manager import PNAccessManagerGrantResult
class Grant(Endpoint):
GRANT_PATH = "/v2/auth/grant/sub-key/%s"
def __init__(self, pubnub):
Endpoint.__init__(self, pubnub)
self._auth_keys = []
self._channels = []
self._groups = []
self._uuids = []
self._read = None
self._write = None
self._manage = None
self._delete = None
self._ttl = None
self._get = None
self._update = None
self._join = None
self._sort_params = True
def get(self, flag):
self._get = flag
return self
def update(self, flag):
self._update = flag
return self
def join(self, flag):
self._join = flag
return self
def uuids(self, uuids):
utils.extend_list(self._uuids, uuids)
return self
def auth_keys(self, auth_keys):
utils.extend_list(self._auth_keys, auth_keys)
return self
def channels(self, channels):
utils.extend_list(self._channels, channels)
return self
def channel_groups(self, channel_groups):
utils.extend_list(self._groups, channel_groups)
return self
def read(self, flag):
self._read = flag
return self
def write(self, flag):
self._write = flag
return self
def manage(self, flag):
self._manage = flag
return self
def delete(self, flag):
self._delete = flag
return self
def ttl(self, ttl):
self._ttl = ttl
return self
def encoded_params(self):
params = {}
if self._auth_keys:
params["auth"] = utils.join_items_and_encode(self._auth_keys)
if self._channels:
params["channel"] = utils.join_channels(self._channels)
if self._groups:
params["channel-group"] = utils.join_items_and_encode(self._groups)
return params
def custom_params(self):
params = {}
if self._read is not None:
params["r"] = "1" if self._read is True else "0"
if self._write is not None:
params["w"] = "1" if self._write is True else "0"
if self._manage is not None:
params["m"] = "1" if self._manage is True else "0"
if self._delete is not None:
params["d"] = "1" if self._delete is True else "0"
if self._get is not None:
params["g"] = "1" if self._get is True else "0"
if self._update is not None:
params["u"] = "1" if self._update is True else "0"
if self._join is not None:
params["j"] = "1" if self._join is True else "0"
if self._auth_keys:
params["auth"] = utils.join_items(self._auth_keys)
if self._channels:
params["channel"] = utils.join_items(self._channels)
if self._groups:
params["channel-group"] = utils.join_items(self._groups)
if self._uuids:
params["target-uuid"] = utils.join_items(self._uuids)
if self._ttl is not None:
params["ttl"] = str(int(self._ttl))
return params
def build_path(self):
return Grant.GRANT_PATH % self.pubnub.config.subscribe_key
def http_method(self):
return HttpMethod.GET
def validate_params(self):
self.validate_subscribe_key()
self.validate_secret_key()
self.validate_publish_key()
# self.validate_channels_and_groups()
if self._channels and self._groups and self._uuids:
raise PubNubException(
pn_error=PNERR_PAM_INVALID_ARGUMENTS,
errormsg="Grants for channels or channelGroups can't be changed together with grants for UUIDs",
)
if self._uuids and not self._auth_keys:
raise PubNubException(
pn_error=PNERR_PAM_INVALID_ARGUMENTS,
errormsg="UUIDs grant management require "
"providing non empty authKeys",
)
if (
self._write is None
and self._read is None
and self._manage is None
and self._get is None
and self._update is None
and self._join is None
):
raise PubNubException(pn_error=PNERR_PAM_NO_FLAGS)
def create_response(self, envelope):
return PNAccessManagerGrantResult.from_json(envelope["payload"])
def is_auth_required(self):
return False
def affected_channels(self):
return self._channels
def affected_channels_groups(self):
return self._groups
def request_timeout(self):
return self.pubnub.config.non_subscribe_request_timeout
def connect_timeout(self):
return self.pubnub.config.connect_timeout
def operation_type(self):
return PNOperationType.PNAccessManagerGrant
def name(self):
return "Grant"
bdraco-freenub-69809e8/pubnub/endpoints/access/grant_token.py 0000664 0000000 0000000 00000007023 14645232030 0024313 0 ustar 00root root 0000000 0000000 from pubnub import utils
from pubnub.endpoints.endpoint import Endpoint
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.errors import PNERR_INVALID_META, PNERR_RESOURCES_MISSING, PNERR_TTL_MISSING
from pubnub.exceptions import PubNubException
from pubnub.models.consumer.v3.access_manager import PNGrantTokenResult
class GrantToken(Endpoint):
GRANT_TOKEN_PATH = "/v3/pam/%s/grant"
def __init__(self, pubnub):
Endpoint.__init__(self, pubnub)
self._ttl = None
self._meta = None
self._authorized_uuid = None
self._channels = []
self._groups = []
self._uuids = []
self._sort_params = True
def ttl(self, ttl):
self._ttl = ttl
return self
def meta(self, meta):
self._meta = meta
return self
def authorized_uuid(self, uuid):
self._authorized_uuid = uuid
return self
def authorized_user(self, user):
self._authorized_uuid = user
return self
def spaces(self, spaces):
self._channels = spaces
return self
def users(self, users):
self._uuids = users
return self
def channels(self, channels):
self._channels = channels
return self
def groups(self, groups):
self._groups = groups
return self
def uuids(self, uuids):
self._uuids = uuids
return self
def custom_params(self):
return {}
def build_data(self):
params = {"ttl": int(self._ttl)}
permissions = {}
resources = {}
patterns = {}
utils.parse_resources(self._channels, "channels", resources, patterns)
utils.parse_resources(self._groups, "groups", resources, patterns)
utils.parse_resources(self._uuids, "uuids", resources, patterns)
utils.parse_resources(self._uuids, "users", resources, patterns)
utils.parse_resources(self._channels, "spaces", resources, patterns)
permissions["resources"] = resources
permissions["patterns"] = patterns
if self._meta:
if isinstance(self._meta, dict):
permissions["meta"] = self._meta
else:
raise PubNubException(pn_error=PNERR_INVALID_META)
else:
permissions["meta"] = {}
if self._authorized_uuid:
permissions["uuid"] = self._authorized_uuid
params["permissions"] = permissions
return utils.write_value_as_string(params)
def build_path(self):
return GrantToken.GRANT_TOKEN_PATH % self.pubnub.config.subscribe_key
def http_method(self):
return HttpMethod.POST
def validate_params(self):
self.validate_subscribe_key()
self.validate_secret_key()
self.validate_ttl()
self.validate_resources()
def create_response(self, envelope):
return PNGrantTokenResult.from_json(envelope["data"])
def is_auth_required(self):
return False
def request_timeout(self):
return self.pubnub.config.non_subscribe_request_timeout
def connect_timeout(self):
return self.pubnub.config.connect_timeout
def operation_type(self):
return PNOperationType.PNAccessManagerGrantToken
def name(self):
return "Grant Token"
def validate_resources(self):
if not any((self._channels, self._groups, self._uuids)):
raise PubNubException(pn_error=PNERR_RESOURCES_MISSING)
def validate_ttl(self):
if not self._ttl:
raise PubNubException(pn_error=PNERR_TTL_MISSING)
bdraco-freenub-69809e8/pubnub/endpoints/access/revoke_token.py 0000664 0000000 0000000 00000002314 14645232030 0024471 0 ustar 00root root 0000000 0000000 from pubnub import utils
from pubnub.endpoints.endpoint import Endpoint
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.models.consumer.v3.access_manager import PNRevokeTokenResult
class RevokeToken(Endpoint):
REVOKE_TOKEN_PATH = "/v3/pam/%s/grant/%s"
def __init__(self, pubnub, token):
Endpoint.__init__(self, pubnub)
self.token = token
def validate_params(self):
self.validate_subscribe_key()
self.validate_secret_key()
def create_response(self, envelope):
return PNRevokeTokenResult(envelope)
def is_auth_required(self):
return False
def request_timeout(self):
return self.pubnub.config.non_subscribe_request_timeout
def connect_timeout(self):
return self.pubnub.config.connect_timeout
def http_method(self):
return HttpMethod.DELETE
def custom_params(self):
return {}
def build_path(self):
return RevokeToken.REVOKE_TOKEN_PATH % (
self.pubnub.config.subscribe_key,
utils.url_encode(self.token),
)
def operation_type(self):
return PNOperationType.PNAccessManagerRevokeToken
def name(self):
return "RevokeToken"
bdraco-freenub-69809e8/pubnub/endpoints/channel_groups/ 0000775 0000000 0000000 00000000000 14645232030 0023172 5 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/pubnub/endpoints/channel_groups/__init__.py 0000664 0000000 0000000 00000000000 14645232030 0025271 0 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/pubnub/endpoints/channel_groups/add_channel_to_channel_group.py 0000664 0000000 0000000 00000004122 14645232030 0031371 0 ustar 00root root 0000000 0000000 from pubnub import utils
from pubnub.endpoints.endpoint import Endpoint
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.errors import PNERR_CHANNELS_MISSING, PNERR_GROUP_MISSING
from pubnub.exceptions import PubNubException
from pubnub.models.consumer.channel_group import PNChannelGroupsAddChannelResult
class AddChannelToChannelGroup(Endpoint):
# /v1/channel-registration/sub-key//channel-group/?add=ch1,ch2
ADD_PATH = "/v1/channel-registration/sub-key/%s/channel-group/%s"
def __init__(self, pubnub):
Endpoint.__init__(self, pubnub)
self._channels = []
self._channel_group = None
def channels(self, channels):
if isinstance(channels, (list, tuple)):
self._channels.extend(channels)
else:
self._channels.extend(utils.split_items(channels))
return self
def channel_group(self, channel_group):
self._channel_group = channel_group
return self
def custom_params(self):
return {"add": utils.join_items(self._channels)}
def build_path(self):
return AddChannelToChannelGroup.ADD_PATH % (
self.pubnub.config.subscribe_key,
utils.url_encode(self._channel_group),
)
def http_method(self):
return HttpMethod.GET
def validate_params(self):
self.validate_subscribe_key()
if len(self._channels) == 0:
raise PubNubException(pn_error=PNERR_CHANNELS_MISSING)
if not isinstance(self._channel_group, str) or len(self._channel_group) == 0:
raise PubNubException(pn_error=PNERR_GROUP_MISSING)
def is_auth_required(self):
return True
def create_response(self, envelope):
return PNChannelGroupsAddChannelResult()
def request_timeout(self):
return self.pubnub.config.non_subscribe_request_timeout
def connect_timeout(self):
return self.pubnub.config.connect_timeout
def operation_type(self):
return PNOperationType.PNAddChannelsToGroupOperation
def name(self):
return "AddChannelToChannelGroup"
bdraco-freenub-69809e8/pubnub/endpoints/channel_groups/list_channels_in_channel_group.py 0000664 0000000 0000000 00000003505 14645232030 0031767 0 ustar 00root root 0000000 0000000 from pubnub import utils
from pubnub.endpoints.endpoint import Endpoint
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.errors import PNERR_GROUP_MISSING
from pubnub.exceptions import PubNubException
from pubnub.models.consumer.channel_group import PNChannelGroupsListResult
class ListChannelsInChannelGroup(Endpoint):
# /v1/channel-registration/sub-key//channel-group/
LIST_PATH = "/v1/channel-registration/sub-key/%s/channel-group/%s"
def __init__(self, pubnub):
Endpoint.__init__(self, pubnub)
self._channel_group = None
def channel_group(self, channel_group):
self._channel_group = channel_group
return self
def custom_params(self):
return {}
def build_path(self):
return ListChannelsInChannelGroup.LIST_PATH % (
self.pubnub.config.subscribe_key,
utils.url_encode(self._channel_group),
)
def http_method(self):
return HttpMethod.GET
def validate_params(self):
self.validate_subscribe_key()
if not isinstance(self._channel_group, str) or len(self._channel_group) == 0:
raise PubNubException(pn_error=PNERR_GROUP_MISSING)
def create_response(self, envelope):
if "payload" in envelope and "channels" in envelope["payload"]:
return PNChannelGroupsListResult(envelope["payload"]["channels"])
else:
return PNChannelGroupsListResult([])
def is_auth_required(self):
return True
def request_timeout(self):
return self.pubnub.config.non_subscribe_request_timeout
def connect_timeout(self):
return self.pubnub.config.connect_timeout
def operation_type(self):
return PNOperationType.PNChannelsForGroupOperation
def name(self):
return "ListChannelsInChannelGroup"
bdraco-freenub-69809e8/pubnub/endpoints/channel_groups/remove_channel_from_channel_group.py 0000664 0000000 0000000 00000004166 14645232030 0032467 0 ustar 00root root 0000000 0000000 from pubnub import utils
from pubnub.endpoints.endpoint import Endpoint
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.errors import PNERR_CHANNELS_MISSING, PNERR_GROUP_MISSING
from pubnub.exceptions import PubNubException
from pubnub.models.consumer.channel_group import PNChannelGroupsRemoveChannelResult
class RemoveChannelFromChannelGroup(Endpoint):
# /v1/channel-registration/sub-key//channel-group/?remove=ch1,ch2
REMOVE_PATH = "/v1/channel-registration/sub-key/%s/channel-group/%s"
def __init__(self, pubnub):
Endpoint.__init__(self, pubnub)
self._channels = []
self._channel_group = None
def channels(self, channels):
if isinstance(channels, (list, tuple)):
self._channels.extend(channels)
else:
self._channels.extend(utils.split_items(channels))
return self
def channel_group(self, channel_group):
self._channel_group = channel_group
return self
def custom_params(self):
return {"remove": utils.join_items(self._channels)}
def build_path(self):
return RemoveChannelFromChannelGroup.REMOVE_PATH % (
self.pubnub.config.subscribe_key,
utils.url_encode(self._channel_group),
)
def http_method(self):
return HttpMethod.GET
def validate_params(self):
self.validate_subscribe_key()
if len(self._channels) == 0:
raise PubNubException(pn_error=PNERR_CHANNELS_MISSING)
if not isinstance(self._channel_group, str) or len(self._channel_group) == 0:
raise PubNubException(pn_error=PNERR_GROUP_MISSING)
def is_auth_required(self):
return True
def create_response(self, envelope):
return PNChannelGroupsRemoveChannelResult()
def request_timeout(self):
return self.pubnub.config.non_subscribe_request_timeout
def connect_timeout(self):
return self.pubnub.config.connect_timeout
def operation_type(self):
return PNOperationType.PNRemoveChannelsFromGroupOperation
def name(self):
return "RemoveChannelToChannelGroup"
bdraco-freenub-69809e8/pubnub/endpoints/channel_groups/remove_channel_group.py 0000664 0000000 0000000 00000003236 14645232030 0027751 0 ustar 00root root 0000000 0000000 from pubnub import utils
from pubnub.endpoints.endpoint import Endpoint
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.errors import PNERR_GROUP_MISSING
from pubnub.exceptions import PubNubException
from pubnub.models.consumer.channel_group import PNChannelGroupsRemoveGroupResult
class RemoveChannelGroup(Endpoint):
# /v1/channel-registration/sub-key//channel-group//remove
REMOVE_PATH = "/v1/channel-registration/sub-key/%s/channel-group/%s/remove"
def __init__(self, pubnub):
Endpoint.__init__(self, pubnub)
self._channel_group = None
def channel_group(self, channel_group):
self._channel_group = channel_group
return self
def custom_params(self):
return {}
def build_path(self):
return RemoveChannelGroup.REMOVE_PATH % (
self.pubnub.config.subscribe_key,
utils.url_encode(self._channel_group),
)
def http_method(self):
return HttpMethod.GET
def validate_params(self):
self.validate_subscribe_key()
if not isinstance(self._channel_group, str) or len(self._channel_group) == 0:
raise PubNubException(pn_error=PNERR_GROUP_MISSING)
def is_auth_required(self):
return True
def create_response(self, envelope):
return PNChannelGroupsRemoveGroupResult()
def request_timeout(self):
return self.pubnub.config.non_subscribe_request_timeout
def connect_timeout(self):
return self.pubnub.config.connect_timeout
def operation_type(self):
return PNOperationType.PNRemoveGroupOperation
def name(self):
return "RemoveChannelGroup"
bdraco-freenub-69809e8/pubnub/endpoints/endpoint.py 0000664 0000000 0000000 00000023470 14645232030 0022363 0 ustar 00root root 0000000 0000000 import logging
import zlib
from abc import ABCMeta, abstractmethod
from pubnub import utils
from pubnub.enums import HttpMethod, PNStatusCategory
from pubnub.errors import (
PNERR_CHANNEL_MISSING,
PNERR_CHANNEL_OR_GROUP_MISSING,
PNERR_FILE_ID_MISSING,
PNERR_FILE_NAME_MISSING,
PNERR_FILE_OBJECT_MISSING,
PNERR_PUBLISH_KEY_MISSING,
PNERR_SECRET_KEY_MISSING,
PNERR_SUBSCRIBE_KEY_MISSING,
)
from pubnub.exceptions import PubNubException
from pubnub.models.consumer.common import PNStatus
from pubnub.models.consumer.pn_error_data import PNErrorData
from pubnub.structures import RequestOptions, ResponseInfo
logger = logging.getLogger("pubnub")
class Endpoint:
SERVER_RESPONSE_SUCCESS = 200
SERVER_RESPONSE_FORBIDDEN = 403
SERVER_RESPONSE_BAD_REQUEST = 400
__metaclass__ = ABCMeta
_path = None
def __init__(self, pubnub):
self.pubnub = pubnub
self._cancellation_event = None
self._sort_params = False
self._use_compression = self.pubnub.config.should_compress
def cancellation_event(self, event):
self._cancellation_event = event
return self
@abstractmethod
def build_path(self):
pass
@abstractmethod
def custom_params(self):
raise NotImplementedError
def build_data(self):
return None
@abstractmethod
def http_method(self):
pass
@abstractmethod
def validate_params(self):
pass
@abstractmethod
def create_response(self, endpoint):
pass
@abstractmethod
def operation_type(self):
raise NotImplementedError
@abstractmethod
def name(self):
pass
@abstractmethod
def request_timeout(self):
pass
@abstractmethod
def connect_timeout(self):
pass
def is_auth_required(self):
raise NotImplementedError
def affected_channels(self):
return None
def affected_channels_groups(self):
return None
def allow_redirects(self):
return True
def use_base_path(self):
return True
def is_compressable(self):
return False
def request_headers(self):
headers = {}
if self.__compress_request():
headers["Content-Encoding"] = "gzip"
if self.http_method() == HttpMethod.POST:
headers["Content-type"] = "application/json"
return headers
def build_file_upload_request(self):
return
def non_json_response(self):
return False
def encoded_params(self):
return {}
def get_path(self):
if not self._path:
self._path = self.build_path()
return self._path
def options(self):
data = self.build_data()
if data and self.__compress_request():
data = zlib.compress(data.encode("utf-8"), level=2)
return RequestOptions(
path=self.get_path(),
params_callback=self.build_params_callback(),
method=self.http_method(),
request_timeout=self.request_timeout(),
connect_timeout=self.connect_timeout(),
create_response=self.create_response,
create_status=self.create_status,
create_exception=self.create_exception,
operation_type=self.operation_type(),
data=data,
files=self.build_file_upload_request(),
sort_arguments=self._sort_params,
allow_redirects=self.allow_redirects(),
use_base_path=self.use_base_path(),
request_headers=self.request_headers(),
non_json_response=self.non_json_response(),
)
def sync(self):
self.validate_params()
envelope = self.pubnub.request_sync(self.options())
if envelope.status.is_error():
raise envelope.status.error_data.exception
return envelope
def prepare_options(self):
return self.pubnub.prepare_options(self.options())
def pn_async(self, callback):
try:
self.validate_params()
options = self.options()
except PubNubException as e:
callback(
None,
self.create_status(
PNStatusCategory.PNBadRequestCategory, None, None, e
),
)
return
def callback_wrapper(envelope):
callback(envelope.result, envelope.status)
return self.pubnub.request_async(
endpoint_name=self.name(),
endpoint_call_options=options,
callback=callback_wrapper,
# REVIEW: include self._cancellation_event into options?
cancellation_event=self._cancellation_event,
)
def result(self):
def handler():
self.validate_params()
return self.options()
return self.pubnub.request_result(
options_func=handler, cancellation_event=self._cancellation_event
)
def future(self):
def handler():
self.validate_params()
return self.options()
return self.pubnub.request_future(
options_func=handler, cancellation_event=self._cancellation_event
)
def deferred(self):
def handler():
self.validate_params()
return self.options()
return self.pubnub.request_deferred(
options_func=handler, cancellation_event=self._cancellation_event
)
def build_params_callback(self):
def callback(params_to_merge):
custom_params = self.custom_params()
custom_params.update(params_to_merge)
custom_params["pnsdk"] = self.pubnub.sdk_name
custom_params["uuid"] = self.pubnub.uuid
for (
query_key,
query_value,
) in self.pubnub._telemetry_manager.operation_latencies().items():
custom_params[query_key] = query_value
if self.is_auth_required():
if self.pubnub._get_token():
custom_params["auth"] = self.pubnub._get_token()
elif self.pubnub.config.auth_key:
custom_params["auth"] = self.pubnub.config.auth_key
if self.pubnub.config.secret_key:
utils.sign_request(
self,
self.pubnub,
custom_params,
self.http_method(),
self.build_data(),
)
custom_params.update(self.encoded_params())
# reassign since pnsdk should be signed unencoded
custom_params["pnsdk"] = utils.url_encode(self.pubnub.sdk_name)
return custom_params
return callback
def validate_subscribe_key(self):
if (
self.pubnub.config.subscribe_key is None
or len(self.pubnub.config.subscribe_key) == 0
):
raise PubNubException(pn_error=PNERR_SUBSCRIBE_KEY_MISSING)
def validate_secret_key(self):
if (
self.pubnub.config.secret_key is None
or len(self.pubnub.config.secret_key) == 0
):
raise PubNubException(pn_error=PNERR_SECRET_KEY_MISSING)
def validate_channel(self):
if self._channel is None or len(self._channel) == 0:
raise PubNubException(pn_error=PNERR_CHANNEL_MISSING)
def validate_channels_and_groups(self):
if len(self._channels) == 0 and len(self._groups) == 0:
raise PubNubException(pn_error=PNERR_CHANNEL_OR_GROUP_MISSING)
def validate_publish_key(self):
if (
self.pubnub.config.publish_key is None
or len(self.pubnub.config.publish_key) == 0
):
raise PubNubException(pn_error=PNERR_PUBLISH_KEY_MISSING)
def validate_file_object(self):
if not self._file_object:
raise PubNubException(pn_error=PNERR_FILE_OBJECT_MISSING)
def validate_file_name(self):
if not self._file_name:
raise PubNubException(pn_error=PNERR_FILE_NAME_MISSING)
def validate_file_id(self):
if not self._file_id:
raise PubNubException(pn_error=PNERR_FILE_ID_MISSING)
def create_status(self, category, response, response_info, exception):
if response_info is not None:
assert isinstance(response_info, ResponseInfo)
pn_status = PNStatus()
if response is None or exception is not None:
pn_status.error = True
if response is not None:
pn_status.original_response = response
if exception is not None:
pn_status.error_data = PNErrorData(str(exception), exception)
if response_info is not None:
pn_status.status_code = response_info.status_code
pn_status.tls_enabled = response_info.tls_enabled
pn_status.origin = response_info.origin
pn_status.uuid = response_info.uuid
pn_status.auth_key = response_info.auth_key
pn_status.client_request = response_info.client_request
pn_status.client_response = response_info.client_response
pn_status.operation = self.operation_type()
pn_status.category = category
pn_status.affected_channels = self.affected_channels()
pn_status.affected_groups = self.affected_channels_groups()
return pn_status
""" Used by asyncio and tornado clients to build exceptions
The only difference with create_status() method is that a status
is wrapped with an exception and also contains this exception inside
as 'status.error_data.exception'
"""
def create_exception(self, category, response, response_info, exception):
status = self.create_status(category, response, response_info, exception)
exception.status = status
return exception
def __compress_request(self):
return self.is_compressable() and self._use_compression
bdraco-freenub-69809e8/pubnub/endpoints/entities/ 0000775 0000000 0000000 00000000000 14645232030 0022007 5 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/pubnub/endpoints/entities/__init__.py 0000664 0000000 0000000 00000000000 14645232030 0024106 0 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/pubnub/endpoints/entities/endpoint.py 0000664 0000000 0000000 00000014102 14645232030 0024177 0 ustar 00root root 0000000 0000000 import logging
from abc import ABCMeta
from pubnub import utils
from pubnub.endpoints.endpoint import Endpoint
from pubnub.errors import PNERR_SPACE_MISSING, PNERR_USER_ID_MISSING
from pubnub.exceptions import PubNubException
from pubnub.models.consumer.entities.page import Next, Previous
logger = logging.getLogger("pubnub")
class EntitiesEndpoint(Endpoint):
__metaclass__ = ABCMeta
def __init__(self, pubnub):
Endpoint.__init__(self, pubnub)
def is_auth_required(self):
return True
def connect_timeout(self):
return self.pubnub.config.connect_timeout
def request_timeout(self):
return self.pubnub.config.non_subscribe_request_timeout
def validate_params(self):
self.validate_subscribe_key()
self.validate_specific_params()
def validate_specific_params(self):
pass
def encoded_params(self):
params = {}
if isinstance(self, ListEndpoint):
if self._filter:
params["filter"] = utils.url_encode(str(self._filter))
return params
def custom_params(self):
params = {}
inclusions = []
if isinstance(self, IncludeCustomEndpoint):
if self._include_custom:
inclusions.append("custom")
if isinstance(self, UserIDIncludeEndpoint):
if self._uuid_details_level:
if self._uuid_details_level == UserIDIncludeEndpoint.USER_ID:
inclusions.append("user_id")
elif (
self._uuid_details_level
== UserIDIncludeEndpoint.USER_ID_WITH_CUSTOM
):
inclusions.append("user_id.custom")
if isinstance(self, SpaceIDIncludeEndpoint):
if self._space_details_level:
if self._space_details_level == SpaceIDIncludeEndpoint.CHANNEL:
inclusions.append("space")
elif (
self._space_details_level
== SpaceIDIncludeEndpoint.CHANNEL_WITH_CUSTOM
):
inclusions.append("space.custom")
if isinstance(self, ListEndpoint):
if self._filter:
params["filter"] = str(self._filter)
if self._limit:
params["limit"] = int(self._limit)
if self._include_total_count:
params["count"] = bool(self._include_total_count)
if self._sort_keys:
joined_sort_params_array = []
for sort_key in self._sort_keys:
joined_sort_params_array.append(
f"{sort_key.key_str()}:{sort_key.dir_str()}"
)
params["sort"] = ",".join(joined_sort_params_array)
if self._page:
if isinstance(self._page, Next):
params["start"] = self._page.hash
elif isinstance(self._page, Previous):
params["end"] = self._page.hash
else:
raise ValueError()
if len(inclusions) > 0:
params["include"] = ",".join(inclusions)
return params
class CustomAwareEndpoint:
__metaclass__ = ABCMeta
def __init__(self):
self._custom = None
def custom(self, custom):
self._custom = dict(custom)
return self
class SpaceEndpoint:
__metaclass__ = ABCMeta
def __init__(self):
self._space_id = None
def space_id(self, space):
self._space_id = str(space)
return self
def _validate_space_id(self):
if self._space_id is None or len(self._space_id) == 0:
raise PubNubException(pn_error=PNERR_SPACE_MISSING)
class UserEndpoint:
__metaclass__ = ABCMeta
def __init__(self):
self._user_id = None
def user_id(self, user_id):
self._user_id = str(user_id)
return self
def _effective_user_id(self):
if self._user_id is not None:
return self._user_id
else:
return self.pubnub.config.user_id
def _validate_user_id(self):
if self._effective_user_id() is None or len(self._effective_user_id()) == 0:
raise PubNubException(pn_error=PNERR_USER_ID_MISSING)
class UsersEndpoint:
__metaclass__ = ABCMeta
def __init__(self):
self._users = None
def users(self, users):
self._users = users
return self
class SpacesEndpoint:
__metaclass__ = ABCMeta
def __init__(self):
self._spaces = None
def spaces(self, spaces):
self._spaces = spaces
return self
class ListEndpoint:
__metaclass__ = ABCMeta
def __init__(self):
self._limit = None
self._filter = None
self._include_total_count = None
self._sort_keys = None
self._page = None
def limit(self, limit):
self._limit = int(limit)
return self
def filter(self, filter):
self._filter = str(filter)
return self
def include_total_count(self, include_total_count):
self._include_total_count = bool(include_total_count)
return self
def sort(self, *sort_keys):
self._sort_keys = sort_keys
return self
def page(self, page):
self._page = page
return self
class IncludeCustomEndpoint:
__metaclass__ = ABCMeta
def __init__(self):
self._include_custom = None
def include_custom(self, include_custom):
self._include_custom = bool(include_custom)
return self
class UserIDIncludeEndpoint:
__metaclass__ = ABCMeta
USER_ID = 1
USER_ID_WITH_CUSTOM = 2
def __init__(self):
self._user_id_details_level = None
def include_user_id(self, user_id_details_level):
self._user_id_details_level = user_id_details_level
return self
class SpaceIDIncludeEndpoint:
__metaclass__ = ABCMeta
SPACE = 1
SPACE_WITH_CUSTOM = 2
def __init__(self):
self._space_details_level = None
def include_space(self, space_details_level):
self._space_details_level = space_details_level
return self
bdraco-freenub-69809e8/pubnub/endpoints/entities/membership/ 0000775 0000000 0000000 00000000000 14645232030 0024142 5 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/pubnub/endpoints/entities/membership/__init__.py 0000664 0000000 0000000 00000000000 14645232030 0026241 0 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/pubnub/endpoints/entities/membership/add_memberships.py 0000664 0000000 0000000 00000006315 14645232030 0027647 0 ustar 00root root 0000000 0000000 from pubnub import utils
from pubnub.endpoints.entities.endpoint import (
EntitiesEndpoint,
IncludeCustomEndpoint,
SpaceEndpoint,
SpacesEndpoint,
UserEndpoint,
UsersEndpoint,
)
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.errors import (
PNERR_INVALID_SPACE,
PNERR_INVALID_USER,
PNERR_SPACE_MISSING,
PNERR_USER_ID_MISSING,
)
from pubnub.exceptions import PubNubException
from pubnub.models.consumer.entities.membership import (
PNMembershipsResult,
PNSpaceMembershipsResult,
)
from pubnub.models.consumer.entities.space import Space
from pubnub.models.consumer.entities.user import User
class AddSpaceMembers(
EntitiesEndpoint, SpaceEndpoint, UsersEndpoint, IncludeCustomEndpoint
):
MEMBERSHIP_PATH = "/v2/objects/%s/channels/%s/uuids"
def __init__(self, pubnub):
EntitiesEndpoint.__init__(self, pubnub)
IncludeCustomEndpoint.__init__(self)
SpaceEndpoint.__init__(self)
UsersEndpoint.__init__(self)
def validate_specific_params(self):
if self._space_id is None or len(self._space_id) == 0:
raise PubNubException(pn_error=PNERR_SPACE_MISSING)
self._users = list(self._users)
if not all(isinstance(user, User) for user in self._users):
raise PubNubException(pn_error=PNERR_INVALID_USER)
def build_path(self):
return AddSpaceMembers.MEMBERSHIP_PATH % (
self.pubnub.config.subscribe_key,
self._space_id,
)
def build_data(self):
users = [user.to_payload_dict() for user in self._users]
payload = {"set": users, "delete": []}
return utils.write_value_as_string(payload)
def create_response(self, envelope):
return PNSpaceMembershipsResult(envelope)
def operation_type(self):
return PNOperationType.PNAddSpaceUsersOperation
def name(self):
return "Add Space Users"
def http_method(self):
return HttpMethod.PATCH
class AddUserSpaces(EntitiesEndpoint, UserEndpoint, SpacesEndpoint):
MEMBERSHIP_PATH = "/v2/objects/%s/uuids/%s/channels"
def __init__(self, pubnub):
EntitiesEndpoint.__init__(self, pubnub)
UserEndpoint.__init__(self)
SpacesEndpoint.__init__(self)
def validate_specific_params(self):
if self._user_id is None or len(self._user_id) == 0:
raise PubNubException(pn_error=PNERR_USER_ID_MISSING)
self._spaces = list(self._spaces)
if not all(isinstance(space, Space) for space in self._spaces):
raise PubNubException(pn_error=PNERR_INVALID_SPACE)
def build_path(self):
return AddUserSpaces.MEMBERSHIP_PATH % (
self.pubnub.config.subscribe_key,
self._user_id,
)
def build_data(self):
spaces = [space.to_payload_dict() for space in self._spaces]
payload = {"set": spaces, "delete": []}
return utils.write_value_as_string(payload)
def create_response(self, envelope):
return PNMembershipsResult(envelope)
def operation_type(self):
return PNOperationType.PNAddUserSpacesOperation
def name(self):
return "Add User Spaces"
def http_method(self):
return HttpMethod.PATCH
bdraco-freenub-69809e8/pubnub/endpoints/entities/membership/fetch_memberships.py 0000664 0000000 0000000 00000004112 14645232030 0030201 0 ustar 00root root 0000000 0000000 from pubnub.endpoints.entities.endpoint import (
EntitiesEndpoint,
IncludeCustomEndpoint,
ListEndpoint,
SpaceEndpoint,
UserEndpoint,
)
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.models.consumer.entities.membership import (
PNSpaceMembershipsResult,
PNUserMembershipsResult,
)
class FetchUserMemberships(
EntitiesEndpoint, IncludeCustomEndpoint, UserEndpoint, ListEndpoint
):
MEMBERSHIP_PATH = "/v2/objects/%s/uuids/%s/channels"
def __init__(self, pubnub):
EntitiesEndpoint.__init__(self, pubnub)
ListEndpoint.__init__(self)
IncludeCustomEndpoint.__init__(self)
UserEndpoint.__init__(self)
def build_path(self):
return FetchUserMemberships.MEMBERSHIP_PATH % (
self.pubnub.config.subscribe_key,
self._user_id,
)
def validate_specific_params(self):
self._validate_user_id()
def create_response(self, envelope):
return PNUserMembershipsResult(envelope)
def operation_type(self):
return PNOperationType.PNFetchUserMembershipsOperation
def name(self):
return "Fetch User Memberships"
def http_method(self):
return HttpMethod.GET
class FetchSpaceMemberships(EntitiesEndpoint, IncludeCustomEndpoint, SpaceEndpoint):
MEMBERSHIP_PATH = "/v2/objects/%s/channels/%s/uuids"
def __init__(self, pubnub):
EntitiesEndpoint.__init__(self, pubnub)
ListEndpoint.__init__(self)
IncludeCustomEndpoint.__init__(self)
UserEndpoint.__init__(self)
def build_path(self):
return FetchSpaceMemberships.MEMBERSHIP_PATH % (
self.pubnub.config.subscribe_key,
self._space_id,
)
def validate_specific_params(self):
self._validate_space_id()
def create_response(self, envelope):
return PNSpaceMembershipsResult(envelope)
def operation_type(self):
return PNOperationType.PNFetchSpaceMembershipsOperation
def name(self):
return "Fetch Space Memberships"
def http_method(self):
return HttpMethod.GET
bdraco-freenub-69809e8/pubnub/endpoints/entities/membership/remove_memberships.py 0000664 0000000 0000000 00000006126 14645232030 0030414 0 ustar 00root root 0000000 0000000 from pubnub import utils
from pubnub.endpoints.entities.endpoint import (
EntitiesEndpoint,
SpaceEndpoint,
SpacesEndpoint,
UserEndpoint,
UsersEndpoint,
)
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.errors import (
PNERR_INVALID_SPACE,
PNERR_INVALID_USER,
PNERR_SPACE_MISSING,
PNERR_USER_ID_MISSING,
)
from pubnub.exceptions import PubNubException
from pubnub.models.consumer.entities.membership import PNMembershipsResult
from pubnub.models.consumer.entities.space import Space
from pubnub.models.consumer.entities.user import User
class RemoveSpaceMembers(EntitiesEndpoint, SpaceEndpoint, UsersEndpoint):
MEMBERSHIP_PATH = "/v2/objects/%s/uuids/%s/channels"
def __init__(self, pubnub):
EntitiesEndpoint.__init__(self, pubnub)
SpaceEndpoint.__init__(self)
UsersEndpoint.__init__(self)
def validate_specific_params(self):
if self._space_id is None or len(self._space_id) == 0:
raise PubNubException(pn_error=PNERR_SPACE_MISSING)
self._users = list(self._users)
if not all(isinstance(user, User) for user in self._users):
raise PubNubException(pn_error=PNERR_INVALID_USER)
def build_path(self):
return RemoveSpaceMembers.MEMBERSHIP_PATH % (
self.pubnub.config.subscribe_key,
self.pubnub.uuid,
)
def build_data(self):
users = [user.to_payload_dict() for user in self._users]
payload = {"set": [], "delete": users}
return utils.write_value_as_string(payload)
def create_response(self, envelope):
return PNMembershipsResult(envelope)
def operation_type(self):
return PNOperationType.PNRemoveSpaceUsersOperation
def name(self):
return "Remove Space Users"
def http_method(self):
return HttpMethod.PATCH
class RemoveUserSpaces(EntitiesEndpoint, UserEndpoint, SpacesEndpoint):
MEMBERSHIP_PATH = "/v2/objects/%s/uuids/%s/channels"
def __init__(self, pubnub):
EntitiesEndpoint.__init__(self, pubnub)
UserEndpoint.__init__(self)
SpacesEndpoint.__init__(self)
def validate_specific_params(self):
if self._user_id is None or len(self._user_id) == 0:
raise PubNubException(pn_error=PNERR_USER_ID_MISSING)
self._spaces = list(self._spaces)
if not all(isinstance(space, Space) for space in self._spaces):
raise PubNubException(pn_error=PNERR_INVALID_SPACE)
def build_path(self):
return RemoveUserSpaces.MEMBERSHIP_PATH % (
self.pubnub.config.subscribe_key,
self._user_id,
)
def build_data(self):
spaces = [space.to_payload_dict() for space in self._spaces]
payload = {"set": [], "delete": spaces}
return utils.write_value_as_string(payload)
def create_response(self, envelope):
return PNMembershipsResult(envelope)
def operation_type(self):
return PNOperationType.PNRemoveUserSpacesOperation
def name(self):
return "Remove User Spaces"
def http_method(self):
return HttpMethod.PATCH
bdraco-freenub-69809e8/pubnub/endpoints/entities/membership/update_memberships.py 0000664 0000000 0000000 00000006345 14645232030 0030404 0 ustar 00root root 0000000 0000000 from pubnub import utils
from pubnub.endpoints.entities.endpoint import (
EntitiesEndpoint,
IncludeCustomEndpoint,
SpaceEndpoint,
SpacesEndpoint,
UserEndpoint,
UsersEndpoint,
)
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.errors import (
PNERR_INVALID_SPACE,
PNERR_INVALID_USER,
PNERR_SPACE_MISSING,
PNERR_USER_ID_MISSING,
)
from pubnub.exceptions import PubNubException
from pubnub.models.consumer.entities.membership import (
PNMembershipsResult,
PNSpaceMembershipsResult,
)
from pubnub.models.consumer.entities.space import Space
from pubnub.models.consumer.entities.user import User
class UpdateSpaceMembers(
EntitiesEndpoint, SpaceEndpoint, UsersEndpoint, IncludeCustomEndpoint
):
MEMBERSHIP_PATH = "/v2/objects/%s/channels/%s/uuids"
def __init__(self, pubnub):
EntitiesEndpoint.__init__(self, pubnub)
IncludeCustomEndpoint.__init__(self)
SpaceEndpoint.__init__(self)
UsersEndpoint.__init__(self)
def validate_specific_params(self):
if self._space_id is None or len(self._space_id) == 0:
raise PubNubException(pn_error=PNERR_SPACE_MISSING)
self._users = list(self._users)
if not all(isinstance(user, User) for user in self._users):
raise PubNubException(pn_error=PNERR_INVALID_USER)
def build_path(self):
return UpdateSpaceMembers.MEMBERSHIP_PATH % (
self.pubnub.config.subscribe_key,
self._space_id,
)
def build_data(self):
users = [user.to_payload_dict() for user in self._users]
payload = {"set": users, "delete": []}
return utils.write_value_as_string(payload)
def create_response(self, envelope):
return PNSpaceMembershipsResult(envelope)
def operation_type(self):
return PNOperationType.PNUpdateSpaceUsersOperation
def name(self):
return "Update Space Users"
def http_method(self):
return HttpMethod.PATCH
class UpdateUserSpaces(EntitiesEndpoint, UserEndpoint, SpacesEndpoint):
MEMBERSHIP_PATH = "/v2/objects/%s/uuids/%s/channels"
def __init__(self, pubnub):
EntitiesEndpoint.__init__(self, pubnub)
UserEndpoint.__init__(self)
SpacesEndpoint.__init__(self)
def validate_specific_params(self):
if self._user_id is None or len(self._user_id) == 0:
raise PubNubException(pn_error=PNERR_USER_ID_MISSING)
self._spaces = list(self._spaces)
if not all(isinstance(space, Space) for space in self._spaces):
raise PubNubException(pn_error=PNERR_INVALID_SPACE)
def build_path(self):
return UpdateUserSpaces.MEMBERSHIP_PATH % (
self.pubnub.config.subscribe_key,
self._user_id,
)
def build_data(self):
spaces = [space.to_payload_dict() for space in self._spaces]
payload = {"set": spaces, "delete": []}
return utils.write_value_as_string(payload)
def create_response(self, envelope):
return PNMembershipsResult(envelope)
def operation_type(self):
return PNOperationType.PNUpdateUserSpacesOperation
def name(self):
return "Update User Spaces"
def http_method(self):
return HttpMethod.PATCH
bdraco-freenub-69809e8/pubnub/endpoints/entities/space/ 0000775 0000000 0000000 00000000000 14645232030 0023102 5 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/pubnub/endpoints/entities/space/__init__.py 0000664 0000000 0000000 00000000000 14645232030 0025201 0 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/pubnub/endpoints/entities/space/create_space.py 0000664 0000000 0000000 00000004075 14645232030 0026100 0 ustar 00root root 0000000 0000000 from pubnub.endpoints.entities.endpoint import (
CustomAwareEndpoint,
EntitiesEndpoint,
IncludeCustomEndpoint,
SpaceEndpoint,
)
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.models.consumer.entities.space import PNCreateSpaceResult
from pubnub.utils import write_value_as_string
class CreateSpace(
EntitiesEndpoint, SpaceEndpoint, IncludeCustomEndpoint, CustomAwareEndpoint
):
CREATE_SPACE_PATH = "/v2/objects/%s/channels/%s"
def __init__(self, pubnub):
EntitiesEndpoint.__init__(self, pubnub)
SpaceEndpoint.__init__(self)
CustomAwareEndpoint.__init__(self)
IncludeCustomEndpoint.__init__(self)
self._name = None
self._description = None
self._status = None
self._type = None
def space_status(self, space_status):
self._status = space_status
self._include_status = True
return self
def space_type(self, space_type):
self._type = space_type
self._include_type = True
return self
def set_name(self, name):
self._name = str(name)
return self
def description(self, description):
self._description = str(description)
return self
def validate_specific_params(self):
self._validate_space_id()
def build_path(self):
return CreateSpace.CREATE_SPACE_PATH % (
self.pubnub.config.subscribe_key,
self._space_id,
)
def build_data(self):
payload = {
"name": self._name,
"description": self._description,
"custom": self._custom,
}
if self._status:
payload["status"] = self._status
if self._type:
payload["type"] = self._type
return write_value_as_string(payload)
def create_response(self, envelope):
return PNCreateSpaceResult(envelope)
def operation_type(self):
return PNOperationType.PNCreateSpaceOperation
def name(self):
return "Create space"
def http_method(self):
return HttpMethod.PATCH
bdraco-freenub-69809e8/pubnub/endpoints/entities/space/fetch_space.py 0000664 0000000 0000000 00000002023 14645232030 0025715 0 ustar 00root root 0000000 0000000 from pubnub.endpoints.entities.endpoint import (
EntitiesEndpoint,
IncludeCustomEndpoint,
SpaceEndpoint,
)
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.models.consumer.entities.space import PNFetchSpaceResult
class FetchSpace(EntitiesEndpoint, SpaceEndpoint, IncludeCustomEndpoint):
FETCH_SPACE_PATH = "/v2/objects/%s/channels/%s"
def __init__(self, pubnub):
EntitiesEndpoint.__init__(self, pubnub)
SpaceEndpoint.__init__(self)
IncludeCustomEndpoint.__init__(self)
def build_path(self):
return FetchSpace.FETCH_SPACE_PATH % (
self.pubnub.config.subscribe_key,
self._space_id,
)
def validate_specific_params(self):
self._validate_space_id()
def create_response(self, envelope):
return PNFetchSpaceResult(envelope)
def operation_type(self):
return PNOperationType.PNFetchSpaceOperation
def name(self):
return "Fetch Space"
def http_method(self):
return HttpMethod.GET
bdraco-freenub-69809e8/pubnub/endpoints/entities/space/fetch_spaces.py 0000664 0000000 0000000 00000001671 14645232030 0026110 0 ustar 00root root 0000000 0000000 from pubnub.endpoints.entities.endpoint import (
EntitiesEndpoint,
IncludeCustomEndpoint,
ListEndpoint,
)
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.models.consumer.entities.space import PNFetchSpacesResult
class FetchSpaces(EntitiesEndpoint, ListEndpoint, IncludeCustomEndpoint):
FETCH_SPACES_PATH = "/v2/objects/%s/channels"
inclusions = ["status", "type"]
def __init__(self, pubnub):
EntitiesEndpoint.__init__(self, pubnub)
ListEndpoint.__init__(self)
IncludeCustomEndpoint.__init__(self)
def build_path(self):
return FetchSpaces.FETCH_SPACES_PATH % self.pubnub.config.subscribe_key
def create_response(self, envelope):
return PNFetchSpacesResult(envelope)
def operation_type(self):
return PNOperationType.PNFetchSpacesOperation
def name(self):
return "Fetch Spaces"
def http_method(self):
return HttpMethod.GET
bdraco-freenub-69809e8/pubnub/endpoints/entities/space/remove_space.py 0000664 0000000 0000000 00000001662 14645232030 0026131 0 ustar 00root root 0000000 0000000 from pubnub.endpoints.entities.endpoint import EntitiesEndpoint, SpaceEndpoint
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.models.consumer.entities.space import PNRemoveSpaceResult
class RemoveSpace(EntitiesEndpoint, SpaceEndpoint):
REMOVE_SPACE_PATH = "/v2/objects/%s/channels/%s"
def __init__(self, pubnub):
EntitiesEndpoint.__init__(self, pubnub)
SpaceEndpoint.__init__(self)
def build_path(self):
return RemoveSpace.REMOVE_SPACE_PATH % (
self.pubnub.config.subscribe_key,
self._space_id,
)
def validate_specific_params(self):
self._validate_space_id()
def create_response(self, envelope):
return PNRemoveSpaceResult(envelope)
def operation_type(self):
return PNOperationType.PNRemoveSpaceOperation
def name(self):
return "Remove Space"
def http_method(self):
return HttpMethod.DELETE
bdraco-freenub-69809e8/pubnub/endpoints/entities/space/update_space.py 0000664 0000000 0000000 00000004075 14645232030 0026117 0 ustar 00root root 0000000 0000000 from pubnub.endpoints.entities.endpoint import (
CustomAwareEndpoint,
EntitiesEndpoint,
IncludeCustomEndpoint,
SpaceEndpoint,
)
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.models.consumer.entities.space import PNUpdateSpaceResult
from pubnub.utils import write_value_as_string
class UpdateSpace(
EntitiesEndpoint, SpaceEndpoint, IncludeCustomEndpoint, CustomAwareEndpoint
):
UPDATE_SPACE_PATH = "/v2/objects/%s/channels/%s"
def __init__(self, pubnub):
EntitiesEndpoint.__init__(self, pubnub)
SpaceEndpoint.__init__(self)
CustomAwareEndpoint.__init__(self)
IncludeCustomEndpoint.__init__(self)
self._name = None
self._description = None
self._status = None
self._type = None
def space_status(self, space_status):
self._status = space_status
self._include_status = True
return self
def space_type(self, space_type):
self._type = space_type
self._include_type = True
return self
def set_name(self, name):
self._name = str(name)
return self
def description(self, description):
self._description = str(description)
return self
def validate_specific_params(self):
self._validate_space_id()
def build_path(self):
return UpdateSpace.UPDATE_SPACE_PATH % (
self.pubnub.config.subscribe_key,
self._space_id,
)
def build_data(self):
payload = {
"name": self._name,
"description": self._description,
"custom": self._custom,
}
if self._status:
payload["status"] = self._status
if self._type:
payload["type"] = self._type
return write_value_as_string(payload)
def create_response(self, envelope):
return PNUpdateSpaceResult(envelope)
def operation_type(self):
return PNOperationType.PNUpdateSpaceOperation
def name(self):
return "Updatea space"
def http_method(self):
return HttpMethod.PATCH
bdraco-freenub-69809e8/pubnub/endpoints/entities/user/ 0000775 0000000 0000000 00000000000 14645232030 0022765 5 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/pubnub/endpoints/entities/user/__init__.py 0000664 0000000 0000000 00000000000 14645232030 0025064 0 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/pubnub/endpoints/entities/user/create_user.py 0000664 0000000 0000000 00000004273 14645232030 0025646 0 ustar 00root root 0000000 0000000 from pubnub.endpoints.entities.endpoint import (
CustomAwareEndpoint,
EntitiesEndpoint,
IncludeCustomEndpoint,
UserEndpoint,
)
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.models.consumer.entities.user import PNCreateUserResult
from pubnub.utils import write_value_as_string
class CreateUser(
EntitiesEndpoint, UserEndpoint, IncludeCustomEndpoint, CustomAwareEndpoint
):
CREATE_USER_PATH = "/v2/objects/%s/uuids/%s"
def __init__(self, pubnub):
EntitiesEndpoint.__init__(self, pubnub)
UserEndpoint.__init__(self)
IncludeCustomEndpoint.__init__(self)
CustomAwareEndpoint.__init__(self)
self._name = None
self._email = None
self._external_id = None
self._profile_url = None
def user_status(self, user_status):
self._status = user_status
self._include_status = True
return self
def user_type(self, user_type):
self._type = user_type
self._include_type = True
return self
def set_name(self, name):
self._name = str(name)
return self
def email(self, email):
self._email = str(email)
return self
def external_id(self, external_id):
self._external_id = str(external_id)
return self
def profile_url(self, profile_url):
self._profile_url = str(profile_url)
return self
def build_path(self):
return CreateUser.CREATE_USER_PATH % (
self.pubnub.config.subscribe_key,
self._effective_user_id(),
)
def build_data(self):
payload = {
"name": self._name,
"email": self._email,
"externalId": self._external_id,
"profileUrl": self._profile_url,
"custom": self._custom,
}
return write_value_as_string(payload)
def validate_specific_params(self):
self._validate_user_id()
def create_response(self, envelope):
return PNCreateUserResult(envelope)
def operation_type(self):
return PNOperationType.PNCreateUserOperation
def name(self):
return "Create User"
def http_method(self):
return HttpMethod.PATCH
bdraco-freenub-69809e8/pubnub/endpoints/entities/user/fetch_user.py 0000664 0000000 0000000 00000002016 14645232030 0025465 0 ustar 00root root 0000000 0000000 from pubnub.endpoints.entities.endpoint import (
EntitiesEndpoint,
IncludeCustomEndpoint,
UserEndpoint,
)
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.models.consumer.entities.user import PNFetchUserResult
class FetchUser(EntitiesEndpoint, UserEndpoint, IncludeCustomEndpoint):
FETCH_USER_PATH = "/v2/objects/%s/uuids/%s"
def __init__(self, pubnub):
EntitiesEndpoint.__init__(self, pubnub)
UserEndpoint.__init__(self)
IncludeCustomEndpoint.__init__(self)
def build_path(self):
return FetchUser.FETCH_USER_PATH % (
self.pubnub.config.subscribe_key,
self._effective_user_id(),
)
def validate_specific_params(self):
self._validate_user_id()
def create_response(self, envelope):
return PNFetchUserResult(envelope)
def operation_type(self):
return PNOperationType.PNFetchUserOperation
def name(self):
return "Fetch User"
def http_method(self):
return HttpMethod.GET
bdraco-freenub-69809e8/pubnub/endpoints/entities/user/fetch_users.py 0000664 0000000 0000000 00000001611 14645232030 0025650 0 ustar 00root root 0000000 0000000 from pubnub.endpoints.entities.endpoint import (
EntitiesEndpoint,
IncludeCustomEndpoint,
ListEndpoint,
)
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.models.consumer.entities.user import PNFetchUsersResult
class FetchUsers(EntitiesEndpoint, ListEndpoint, IncludeCustomEndpoint):
FETCH_USERS_PATH = "/v2/objects/%s/uuids"
def __init__(self, pubnub):
EntitiesEndpoint.__init__(self, pubnub)
ListEndpoint.__init__(self)
IncludeCustomEndpoint.__init__(self)
def build_path(self):
return FetchUsers.FETCH_USERS_PATH % self.pubnub.config.subscribe_key
def create_response(self, envelope):
return PNFetchUsersResult(envelope)
def operation_type(self):
return PNOperationType.PNFetchUsersOperation
def name(self):
return "Fetch Users"
def http_method(self):
return HttpMethod.GET
bdraco-freenub-69809e8/pubnub/endpoints/entities/user/remove_user.py 0000664 0000000 0000000 00000001655 14645232030 0025701 0 ustar 00root root 0000000 0000000 from pubnub.endpoints.entities.endpoint import EntitiesEndpoint, UserEndpoint
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.models.consumer.entities.user import PNRemoveUserResult
class RemoveUser(EntitiesEndpoint, UserEndpoint):
REMOVE_USER_PATH = "/v2/objects/%s/uuids/%s"
def __init__(self, pubnub):
EntitiesEndpoint.__init__(self, pubnub)
UserEndpoint.__init__(self)
def build_path(self):
return RemoveUser.REMOVE_USER_PATH % (
self.pubnub.config.subscribe_key,
self._effective_user_id(),
)
def validate_specific_params(self):
self._validate_user_id()
def create_response(self, envelope):
return PNRemoveUserResult(envelope)
def operation_type(self):
return PNOperationType.PNRemoveUserOperation
def name(self):
return "Remove User"
def http_method(self):
return HttpMethod.DELETE
bdraco-freenub-69809e8/pubnub/endpoints/entities/user/update_user.py 0000664 0000000 0000000 00000004273 14645232030 0025665 0 ustar 00root root 0000000 0000000 from pubnub.endpoints.entities.endpoint import (
CustomAwareEndpoint,
EntitiesEndpoint,
IncludeCustomEndpoint,
UserEndpoint,
)
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.models.consumer.entities.user import PNUpdateUserResult
from pubnub.utils import write_value_as_string
class UpdateUser(
EntitiesEndpoint, UserEndpoint, IncludeCustomEndpoint, CustomAwareEndpoint
):
UPDATE_USER_PATH = "/v2/objects/%s/uuids/%s"
def __init__(self, pubnub):
EntitiesEndpoint.__init__(self, pubnub)
UserEndpoint.__init__(self)
IncludeCustomEndpoint.__init__(self)
CustomAwareEndpoint.__init__(self)
self._name = None
self._email = None
self._external_id = None
self._profile_url = None
def user_status(self, user_status):
self._status = user_status
self._include_status = True
return self
def user_type(self, user_type):
self._type = user_type
self._include_type = True
return self
def set_name(self, name):
self._name = str(name)
return self
def email(self, email):
self._email = str(email)
return self
def external_id(self, external_id):
self._external_id = str(external_id)
return self
def profile_url(self, profile_url):
self._profile_url = str(profile_url)
return self
def build_path(self):
return UpdateUser.UPDATE_USER_PATH % (
self.pubnub.config.subscribe_key,
self._effective_user_id(),
)
def build_data(self):
payload = {
"name": self._name,
"email": self._email,
"externalId": self._external_id,
"profileUrl": self._profile_url,
"custom": self._custom,
}
return write_value_as_string(payload)
def validate_specific_params(self):
self._validate_user_id()
def create_response(self, envelope):
return PNUpdateUserResult(envelope)
def operation_type(self):
return PNOperationType.PNUpdateUserOperation
def name(self):
return "Update User"
def http_method(self):
return HttpMethod.PATCH
bdraco-freenub-69809e8/pubnub/endpoints/fetch_messages.py 0000664 0000000 0000000 00000014062 14645232030 0023520 0 ustar 00root root 0000000 0000000 import logging
from pubnub import utils
from pubnub.endpoints.endpoint import Endpoint
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.errors import (
PNERR_CHANNEL_MISSING,
PNERR_HISTORY_MESSAGE_ACTIONS_MULTIPLE_CHANNELS,
)
from pubnub.exceptions import PubNubException
from pubnub.models.consumer.history import PNFetchMessagesResult
logger = logging.getLogger("pubnub")
class FetchMessages(Endpoint):
FETCH_MESSAGES_PATH = "/v3/history/sub-key/%s/channel/%s"
FETCH_MESSAGES_WITH_ACTIONS_PATH = "/v3/history-with-actions/sub-key/%s/channel/%s"
SINGLE_CHANNEL_MAX_MESSAGES = 100
DEFAULT_SINGLE_CHANNEL_MESSAGES = 100
MULTIPLE_CHANNELS_MAX_MESSAGES = 25
DEFAULT_MULTIPLE_CHANNELS_MESSAGES = 25
MAX_MESSAGES_ACTIONS = 25
DEFAULT_MESSAGES_ACTIONS = 25
def __init__(self, pubnub):
Endpoint.__init__(self, pubnub)
self._channels = []
self._start = None
self._end = None
self._count = None
self._include_meta = None
self._include_message_actions = None
self._include_message_type = None
self._include_uuid = None
def channels(self, channels):
utils.extend_list(self._channels, channels)
return self
def count(self, count):
assert isinstance(count, int)
self._count = count
return self
def maximum_per_channel(self, maximum_per_channel):
return self.count(maximum_per_channel)
def start(self, start):
assert isinstance(start, int)
self._start = start
return self
def end(self, end):
assert isinstance(end, int)
self._end = end
return self
def include_meta(self, include_meta):
assert isinstance(include_meta, bool)
self._include_meta = include_meta
return self
def include_message_actions(self, include_message_actions):
assert isinstance(include_message_actions, bool)
self._include_message_actions = include_message_actions
return self
def include_message_type(self, include_message_type):
assert isinstance(include_message_type, bool)
self._include_message_type = include_message_type
return self
def include_uuid(self, include_uuid):
assert isinstance(include_uuid, bool)
self._include_uuid = include_uuid
return self
def custom_params(self):
params = {"max": int(self._count)}
if self._start is not None:
params["start"] = str(self._start)
if self._end is not None:
params["end"] = str(self._end)
if self._include_meta is not None:
params["include_meta"] = "true" if self._include_meta else "false"
if self._include_message_type is not None:
params["include_message_type"] = (
"true" if self._include_message_type else "false"
)
if self.include_message_actions and self._include_uuid is not None:
params["include_uuid"] = "true" if self._include_uuid else "false"
return params
def build_path(self):
if self._include_message_actions is False:
return FetchMessages.FETCH_MESSAGES_PATH % (
self.pubnub.config.subscribe_key,
utils.join_channels(self._channels),
)
else:
return FetchMessages.FETCH_MESSAGES_WITH_ACTIONS_PATH % (
self.pubnub.config.subscribe_key,
utils.url_encode(self._channels[0]),
)
def http_method(self):
return HttpMethod.GET
def is_auth_required(self):
return True
def validate_params(self):
self.validate_subscribe_key()
if self._channels is None or len(self._channels) == 0:
raise PubNubException(pn_error=PNERR_CHANNEL_MISSING)
if self._include_meta is None:
self._include_meta = False
if self._include_message_actions is None:
self._include_message_actions = False
if not self._include_message_actions:
if len(self._channels) == 1:
if self._count is None or self._count < 1:
self._count = FetchMessages.DEFAULT_SINGLE_CHANNEL_MESSAGES
logger.info("count param defaulting to %d", self._count)
elif self._count > FetchMessages.SINGLE_CHANNEL_MAX_MESSAGES:
self._count = FetchMessages.DEFAULT_SINGLE_CHANNEL_MESSAGES
logger.info("count param defaulting to %d", self._count)
else:
if self._count is None or self._count < 1:
self._count = FetchMessages.DEFAULT_MULTIPLE_CHANNELS_MESSAGES
logger.info("count param defaulting to %d", self._count)
elif self._count > FetchMessages.MULTIPLE_CHANNELS_MAX_MESSAGES:
self._count = FetchMessages.DEFAULT_MULTIPLE_CHANNELS_MESSAGES
logger.info("count param defaulting to %d", self._count)
else:
if len(self._channels) > 1:
raise PubNubException(
pn_error=PNERR_HISTORY_MESSAGE_ACTIONS_MULTIPLE_CHANNELS
)
if (
self._count is None
or self._count < 1
or self._count > FetchMessages.MAX_MESSAGES_ACTIONS
):
self._count = FetchMessages.DEFAULT_MESSAGES_ACTIONS
logger.info("count param defaulting to %d", self._count)
def create_response(self, envelope): # pylint: disable=W0221
return PNFetchMessagesResult.from_json(
json_input=envelope,
include_message_actions=self._include_message_actions,
start_timetoken=self._start,
end_timetoken=self._end,
)
def request_timeout(self):
return self.pubnub.config.non_subscribe_request_timeout
def connect_timeout(self):
return self.pubnub.config.connect_timeout
def operation_type(self):
return PNOperationType.PNFetchMessagesOperation
def name(self):
return "Fetch messages"
bdraco-freenub-69809e8/pubnub/endpoints/file_operations/ 0000775 0000000 0000000 00000000000 14645232030 0023345 5 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/pubnub/endpoints/file_operations/__init__.py 0000664 0000000 0000000 00000000000 14645232030 0025444 0 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/pubnub/endpoints/file_operations/delete_file.py 0000664 0000000 0000000 00000002630 14645232030 0026161 0 ustar 00root root 0000000 0000000 from pubnub import utils
from pubnub.endpoints.file_operations.file_based_endpoint import FileOperationEndpoint
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.models.consumer.file import PNDeleteFileResult
class DeleteFile(FileOperationEndpoint):
DELETE_FILE_URL = "/v1/files/%s/channels/%s/files/%s/%s"
def __init__(self, pubnub):
FileOperationEndpoint.__init__(self, pubnub)
self._file_id = None
self._file_name = None
def build_path(self):
return DeleteFile.DELETE_FILE_URL % (
self.pubnub.config.subscribe_key,
utils.url_encode(self._channel),
self._file_id,
self._file_name,
)
def file_id(self, file_id):
self._file_id = file_id
return self
def file_name(self, file_name):
self._file_name = file_name
return self
def http_method(self):
return HttpMethod.DELETE
def custom_params(self):
return {}
def is_auth_required(self):
return True
def validate_params(self):
self.validate_subscribe_key()
self.validate_channel()
self.validate_file_name()
self.validate_file_id()
def create_response(self, envelope):
return PNDeleteFileResult(envelope)
def operation_type(self):
return PNOperationType.PNDeleteFileOperation
def name(self):
return "Delete file"
bdraco-freenub-69809e8/pubnub/endpoints/file_operations/download_file.py 0000664 0000000 0000000 00000005001 14645232030 0026521 0 ustar 00root root 0000000 0000000 from pubnub.crypto import PubNubFileCrypto
from pubnub.endpoints.file_operations.file_based_endpoint import FileOperationEndpoint
from pubnub.endpoints.file_operations.get_file_url import GetFileDownloadUrl
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.models.consumer.file import PNDownloadFileResult
from pubnub.request_handlers.requests_handler import RequestsRequestHandler
class DownloadFileNative(FileOperationEndpoint):
def __init__(self, pubnub):
FileOperationEndpoint.__init__(self, pubnub)
self._file_id = None
self._file_name = None
self._pubnub = pubnub
self._download_data = None
self._cipher_key = None
def cipher_key(self, cipher_key):
self._cipher_key = cipher_key
return self
def build_path(self):
return self._download_data.result.file_url
def http_method(self):
return HttpMethod.GET
def is_auth_required(self):
return False
def custom_params(self):
return {}
def file_id(self, file_id):
self._file_id = file_id
return self
def file_name(self, file_name):
self._file_name = file_name
return self
def decrypt_payload(self, data):
return PubNubFileCrypto(self._pubnub.config).decrypt(
self._cipher_key or self._pubnub.config.cipher_key, data
)
def validate_params(self):
self.validate_subscribe_key()
self.validate_channel()
self.validate_file_name()
self.validate_file_id()
def create_response(self, envelope):
if self._cipher_key or self._pubnub.config.cipher_key:
return PNDownloadFileResult(self.decrypt_payload(envelope.content))
else:
return PNDownloadFileResult(envelope.content)
def non_json_response(self):
return True
def operation_type(self):
return PNOperationType.PNDownloadFileAction
def use_base_path(self):
return False
def build_params_callback(self):
return lambda a: {}
def name(self):
return "Downloading file"
def sync(self):
self._download_data = (
GetFileDownloadUrl(self._pubnub)
.channel(self._channel)
.file_name(self._file_name)
.file_id(self._file_id)
.sync()
)
return super().sync()
def pn_async(self, callback):
return RequestsRequestHandler(self._pubnub).async_file_based_operation(
self.sync, callback, "File Download"
)
bdraco-freenub-69809e8/pubnub/endpoints/file_operations/download_file_asyncio.py 0000664 0000000 0000000 00000001632 14645232030 0030254 0 ustar 00root root 0000000 0000000 from pubnub.endpoints.file_operations.download_file import DownloadFileNative
from pubnub.endpoints.file_operations.get_file_url import GetFileDownloadUrl
from pubnub.models.consumer.file import PNDownloadFileResult
class DownloadFileAsyncio(DownloadFileNative):
def create_response(self, envelope, data=None):
if self._cipher_key or self._pubnub.config.cipher_key:
data = self.decrypt_payload(data)
return PNDownloadFileResult(data)
async def future(self):
self._download_data = (
await GetFileDownloadUrl(self._pubnub)
.channel(self._channel)
.file_name(self._file_name)
.file_id(self._file_id)
.future()
)
downloaded_file = await super().future()
return downloaded_file
async def result(self):
response_envelope = await self.future()
return response_envelope.result
bdraco-freenub-69809e8/pubnub/endpoints/file_operations/fetch_upload_details.py 0000664 0000000 0000000 00000002644 14645232030 0030067 0 ustar 00root root 0000000 0000000 from pubnub import utils
from pubnub.endpoints.file_operations.file_based_endpoint import FileOperationEndpoint
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.models.consumer.file import PNFetchFileUploadS3DataResult
class FetchFileUploadS3Data(FileOperationEndpoint):
GENERATE_FILE_UPLOAD_DATA = "/v1/files/%s/channels/%s/generate-upload-url"
def __init__(self, pubnub):
FileOperationEndpoint.__init__(self, pubnub)
self._file_name = None
def build_path(self):
return FetchFileUploadS3Data.GENERATE_FILE_UPLOAD_DATA % (
self.pubnub.config.subscribe_key,
utils.url_encode(self._channel),
)
def build_data(self):
params = {"name": self._file_name}
return utils.write_value_as_string(params)
def http_method(self):
return HttpMethod.POST
def custom_params(self):
return {}
def is_auth_required(self):
return True
def file_name(self, file_name):
self._file_name = file_name
return self
def validate_params(self):
self.validate_subscribe_key()
self.validate_channel()
self.validate_file_name()
def create_response(self, envelope):
return PNFetchFileUploadS3DataResult(envelope)
def operation_type(self):
return PNOperationType.PNFetchFileUploadS3DataAction
def name(self):
return "Fetch file upload S3 data"
bdraco-freenub-69809e8/pubnub/endpoints/file_operations/file_based_endpoint.py 0000664 0000000 0000000 00000000704 14645232030 0027675 0 ustar 00root root 0000000 0000000 from pubnub.endpoints.endpoint import Endpoint
class FileOperationEndpoint(Endpoint):
def __init__(self, pubnub):
Endpoint.__init__(self, pubnub)
self._channel = None
def channel(self, channel):
self._channel = channel
return self
def request_timeout(self):
return self.pubnub.config.non_subscribe_request_timeout
def connect_timeout(self):
return self.pubnub.config.connect_timeout
bdraco-freenub-69809e8/pubnub/endpoints/file_operations/get_file_url.py 0000664 0000000 0000000 00000003737 14645232030 0026371 0 ustar 00root root 0000000 0000000 from pubnub import utils
from pubnub.endpoints.file_operations.file_based_endpoint import FileOperationEndpoint
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.models.consumer.file import PNGetFileDownloadURLResult
class GetFileDownloadUrl(FileOperationEndpoint):
GET_FILE_DOWNLOAD_URL = "/v1/files/%s/channels/%s/files/%s/%s"
def __init__(self, pubnub, file_name=None, file_id=None):
FileOperationEndpoint.__init__(self, pubnub)
self._file_id = file_id
self._file_name = file_name
def build_path(self):
return GetFileDownloadUrl.GET_FILE_DOWNLOAD_URL % (
self.pubnub.config.subscribe_key,
utils.url_encode(self._channel),
self._file_id,
self._file_name,
)
def get_complete_url(self):
endpoint_options = self.options()
endpoint_options.merge_params_in(self.custom_params())
query_params = "?" + endpoint_options.query_string
return (
self.pubnub.config.scheme_extended()
+ self.pubnub.base_origin
+ self.build_path()
+ query_params
)
def file_id(self, file_id):
self._file_id = file_id
return self
def file_name(self, file_name):
self._file_name = file_name
return self
def http_method(self):
return HttpMethod.GET
def custom_params(self):
return {}
def is_auth_required(self):
return True
def non_json_response(self):
return True
def validate_params(self):
self.validate_subscribe_key()
self.validate_channel()
self.validate_file_id()
self.validate_file_name()
def create_response(self, envelope, data=None):
return PNGetFileDownloadURLResult(envelope)
def operation_type(self):
return PNOperationType.PNGetFileDownloadURLAction
def allow_redirects(self):
return False
def name(self):
return "Get file download url"
bdraco-freenub-69809e8/pubnub/endpoints/file_operations/list_files.py 0000664 0000000 0000000 00000002031 14645232030 0026050 0 ustar 00root root 0000000 0000000 from pubnub import utils
from pubnub.endpoints.file_operations.file_based_endpoint import FileOperationEndpoint
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.models.consumer.file import PNGetFilesResult
class ListFiles(FileOperationEndpoint):
LIST_FILES_URL = "/v1/files/%s/channels/%s/files"
def __init__(self, pubnub):
FileOperationEndpoint.__init__(self, pubnub)
def build_path(self):
return ListFiles.LIST_FILES_URL % (
self.pubnub.config.subscribe_key,
utils.url_encode(self._channel),
)
def http_method(self):
return HttpMethod.GET
def custom_params(self):
return {}
def is_auth_required(self):
return True
def validate_params(self):
self.validate_subscribe_key()
self.validate_channel()
def create_response(self, envelope):
return PNGetFilesResult(envelope)
def operation_type(self):
return PNOperationType.PNGetFilesAction
def name(self):
return "List files"
bdraco-freenub-69809e8/pubnub/endpoints/file_operations/publish_file_message.py 0000664 0000000 0000000 00000006036 14645232030 0030075 0 ustar 00root root 0000000 0000000 from pubnub import utils
from pubnub.endpoints.file_operations.file_based_endpoint import FileOperationEndpoint
from pubnub.endpoints.mixins import TimeTokenOverrideMixin
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.models.consumer.file import PNPublishFileMessageResult
class PublishFileMessage(FileOperationEndpoint, TimeTokenOverrideMixin):
PUBLISH_FILE_MESSAGE = "/v1/files/publish-file/%s/%s/0/%s/0/%s"
def __init__(self, pubnub):
super().__init__(pubnub)
self._file_id = None
self._file_name = None
self._pubnub = pubnub
self._message = None
self._should_store = None
self._ttl = 0
self._meta = None
self._cipher_key = None
self._replicate = None
self._ptto = None
def meta(self, meta):
self._meta = meta
return self
def should_store(self, should_store):
self._should_store = bool(should_store)
return self
def cipher_key(self, cipher_key):
self._cipher_key = cipher_key
return self
def message(self, message):
self._message = message
return self
def file_id(self, file_id):
self._file_id = file_id
return self
def ttl(self, ttl):
self._ttl = ttl
return self
def file_name(self, file_name):
self._file_name = file_name
return self
def _encrypt_message(self, message):
if self._cipher_key or self._pubnub.config.cipher_key:
return self._pubnub.config.crypto.encrypt(
self._cipher_key or self._pubnub.config.cipher_key,
utils.write_value_as_string(message),
)
else:
return message
def _build_message(self):
message = {
"message": self._message,
"file": {"id": self._file_id, "name": self._file_name},
}
return self._encrypt_message(message)
def build_path(self):
message = self._build_message()
return PublishFileMessage.PUBLISH_FILE_MESSAGE % (
self.pubnub.config.publish_key,
self.pubnub.config.subscribe_key,
utils.url_encode(self._channel),
utils.url_write(message),
)
def http_method(self):
return HttpMethod.GET
def custom_params(self):
params = TimeTokenOverrideMixin.custom_params(self)
params.update(
{
"meta": utils.url_write(self._meta),
"ttl": self._ttl,
"store": 1 if self._should_store else 0,
}
)
return params
def is_auth_required(self):
return True
def validate_params(self):
self.validate_subscribe_key()
self.validate_channel()
self.validate_file_name()
self.validate_file_id()
def create_response(self, envelope):
return PNPublishFileMessageResult(envelope)
def operation_type(self):
return PNOperationType.PNSendFileAction
def name(self):
return "Sending file upload notification"
bdraco-freenub-69809e8/pubnub/endpoints/file_operations/send_file.py 0000664 0000000 0000000 00000010722 14645232030 0025651 0 ustar 00root root 0000000 0000000 from pubnub.crypto import PubNubFileCrypto
from pubnub.endpoints.file_operations.fetch_upload_details import FetchFileUploadS3Data
from pubnub.endpoints.file_operations.file_based_endpoint import FileOperationEndpoint
from pubnub.endpoints.file_operations.publish_file_message import PublishFileMessage
from pubnub.endpoints.mixins import TimeTokenOverrideMixin
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.models.consumer.file import PNSendFileResult
from pubnub.request_handlers.requests_handler import RequestsRequestHandler
class SendFileNative(FileOperationEndpoint, TimeTokenOverrideMixin):
def __init__(self, pubnub):
super().__init__(pubnub)
self._file_name = None
self._pubnub = pubnub
self._file_upload_envelope = None
self._message = None
self._should_store = None
self._ttl = 0
self._meta = None
self._cipher_key = None
self._file_object = None
self._replicate = None
self._ptto = None
def file_object(self, fd):
self._file_object = fd
return self
def build_params_callback(self):
return lambda a: {}
def build_path(self):
return self._file_upload_envelope.result.data["url"]
def encrypt_payload(self):
if self._cipher_key or self._pubnub.config.cipher_key:
try:
payload = self._file_object.read()
except AttributeError:
payload = self._file_object
return PubNubFileCrypto(self._pubnub.config).encrypt(
self._cipher_key or self._pubnub.config.cipher_key, payload
)
else:
return self._file_object
def build_file_upload_request(self):
file = self.encrypt_payload()
multipart_body = {}
for form_field in self._file_upload_envelope.result.data["form_fields"]:
multipart_body[form_field["key"]] = (None, form_field["value"])
multipart_body["file"] = (self._file_name, file, None)
return multipart_body
def http_method(self):
return HttpMethod.POST
def use_compression(self, compress=True):
self._use_compression = bool(compress)
return self
def is_compressable(self):
return True
def custom_params(self):
return {}
def validate_params(self):
self.validate_subscribe_key()
self.validate_channel()
self.validate_file_object()
self.validate_file_name()
def use_base_path(self):
return False
def non_json_response(self):
return True
def is_auth_required(self):
return False
def should_store(self, should_store):
self._should_store = bool(should_store)
return self
def ttl(self, ttl):
self._ttl = ttl
return self
def meta(self, meta):
self._meta = meta
return self
def message(self, message):
self._message = message
return self
def file_name(self, file_name):
self._file_name = file_name
return self
def cipher_key(self, cipher_key):
self._cipher_key = cipher_key
return self
def create_response(self, envelope, data=None):
return PNSendFileResult(envelope, self._file_upload_envelope)
def operation_type(self):
return PNOperationType.PNSendFileAction
def request_headers(self):
return {}
def name(self):
return "Send file to S3"
def sync(self):
self._file_upload_envelope = (
FetchFileUploadS3Data(self._pubnub)
.channel(self._channel)
.file_name(self._file_name)
.sync()
)
response_envelope = super().sync()
publish_file_response = (
PublishFileMessage(self._pubnub)
.channel(self._channel)
.meta(self._meta)
.message(self._message)
.file_id(response_envelope.result.file_id)
.file_name(response_envelope.result.name)
.should_store(self._should_store)
.ttl(self._ttl)
.replicate(self._replicate)
.ptto(self._ptto)
.cipher_key(self._cipher_key)
.sync()
)
response_envelope.result.timestamp = publish_file_response.result.timestamp
return response_envelope
def pn_async(self, callback):
return RequestsRequestHandler(self._pubnub).async_file_based_operation(
self.sync, callback, "File Download"
)
bdraco-freenub-69809e8/pubnub/endpoints/file_operations/send_file_asyncio.py 0000664 0000000 0000000 00000003742 14645232030 0027402 0 ustar 00root root 0000000 0000000 import aiohttp
from pubnub.endpoints.file_operations.fetch_upload_details import FetchFileUploadS3Data
from pubnub.endpoints.file_operations.publish_file_message import PublishFileMessage
from pubnub.endpoints.file_operations.send_file import SendFileNative
class AsyncioSendFile(SendFileNative):
def build_file_upload_request(self):
file = self.encrypt_payload()
form_data = aiohttp.FormData()
for form_field in self._file_upload_envelope.result.data["form_fields"]:
form_data.add_field(
form_field["key"],
form_field["value"],
content_type="multipart/form-data",
)
form_data.add_field(
"file",
file,
filename=self._file_name,
content_type="application/octet-stream",
)
return form_data
def options(self):
request_options = super(SendFileNative, self).options()
request_options.data = request_options.files
return request_options
async def future(self):
self._file_upload_envelope = (
await FetchFileUploadS3Data(self._pubnub)
.channel(self._channel)
.file_name(self._file_name)
.future()
)
response_envelope = await super(SendFileNative, self).future()
publish_file_response = (
await PublishFileMessage(self._pubnub)
.channel(self._channel)
.meta(self._meta)
.message(self._message)
.file_id(response_envelope.result.file_id)
.file_name(response_envelope.result.name)
.should_store(self._should_store)
.ttl(self._ttl)
.cipher_key(self._cipher_key)
.future()
)
response_envelope.result.timestamp = publish_file_response.result.timestamp
return response_envelope
async def result(self):
response_envelope = await self.future()
return response_envelope.result
bdraco-freenub-69809e8/pubnub/endpoints/history.py 0000664 0000000 0000000 00000006232 14645232030 0022241 0 ustar 00root root 0000000 0000000 from pubnub import utils
from pubnub.endpoints.endpoint import Endpoint
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.models.consumer.history import PNHistoryResult
class History(Endpoint):
HISTORY_PATH = "/v2/history/sub-key/%s/channel/%s"
MAX_COUNT = 100
def __init__(self, pubnub):
Endpoint.__init__(self, pubnub)
self._channel = None
self._start = None
self._end = None
self._reverse = None
self._count = None
self._include_timetoken = None
self._include_meta = None
def channel(self, channel):
self._channel = channel
return self
def start(self, start):
assert isinstance(start, int)
self._start = start
return self
def end(self, end):
assert isinstance(end, int)
self._end = end
return self
def reverse(self, reverse):
assert isinstance(reverse, bool)
self._reverse = reverse
return self
def count(self, count):
assert isinstance(count, int)
self._count = count
return self
def include_timetoken(self, include_timetoken):
assert isinstance(include_timetoken, bool)
self._include_timetoken = include_timetoken
return self
def include_meta(self, include_meta):
assert isinstance(include_meta, bool)
self._include_meta = include_meta
return self
def custom_params(self):
params = {}
if self._start is not None:
params["start"] = str(self._start)
if self._end is not None:
params["end"] = str(self._end)
if self._count is not None and 0 < self._count <= History.MAX_COUNT:
params["count"] = str(self._count)
else:
params["count"] = "100"
if self._reverse is not None:
params["reverse"] = "true" if self._reverse else "false"
if self._include_timetoken is not None:
params["include_token"] = "true" if self._include_timetoken else "false"
if self._include_meta is not None:
params["include_meta"] = "true" if self._include_meta else "false"
return params
def build_path(self):
return History.HISTORY_PATH % (
self.pubnub.config.subscribe_key,
utils.url_encode(self._channel),
)
def http_method(self):
return HttpMethod.GET
def is_auth_required(self):
return True
def validate_params(self):
self.validate_subscribe_key()
self.validate_channel()
def create_response(self, envelope):
return PNHistoryResult.from_json(
json_input=envelope,
crypto=self.pubnub.config.crypto,
include_timetoken=self._include_timetoken,
include_meta=self._include_meta,
cipher=self.pubnub.config.cipher_key,
)
def request_timeout(self):
return self.pubnub.config.non_subscribe_request_timeout
def connect_timeout(self):
return self.pubnub.config.connect_timeout
def operation_type(self):
return PNOperationType.PNHistoryOperation
def name(self):
return "History"
bdraco-freenub-69809e8/pubnub/endpoints/history_delete.py 0000664 0000000 0000000 00000003150 14645232030 0023557 0 ustar 00root root 0000000 0000000 from pubnub import utils
from pubnub.endpoints.endpoint import Endpoint
from pubnub.enums import HttpMethod, PNOperationType
class HistoryDelete(Endpoint): # pylint: disable=W0612
HISTORY_DELETE_PATH = "/v3/history/sub-key/%s/channel/%s"
def __init__(self, pubnub):
Endpoint.__init__(self, pubnub)
self._channel = None
self._start = None
self._end = None
def channel(self, channel):
self._channel = channel
return self
def start(self, start):
self._start = start
return self
def end(self, end):
self._end = end
return self
def custom_params(self):
params = {}
if self._start is not None:
params["start"] = str(self._start)
if self._end is not None:
params["end"] = str(self._end)
return params
def build_path(self):
return HistoryDelete.HISTORY_DELETE_PATH % (
self.pubnub.config.subscribe_key,
utils.url_encode(self._channel),
)
def http_method(self):
return HttpMethod.DELETE
def is_auth_required(self):
return True
def validate_params(self):
self.validate_subscribe_key()
self.validate_channel()
def create_response(self, endpoint):
return {}
def request_timeout(self):
return self.pubnub.config.non_subscribe_request_timeout
def connect_timeout(self):
return self.pubnub.config.connect_timeout
def operation_type(self):
return PNOperationType.PNHistoryDeleteOperation
def name(self):
return "History delete"
bdraco-freenub-69809e8/pubnub/endpoints/message_actions/ 0000775 0000000 0000000 00000000000 14645232030 0023327 5 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/pubnub/endpoints/message_actions/__init__.py 0000664 0000000 0000000 00000000000 14645232030 0025426 0 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/pubnub/endpoints/message_actions/add_message_action.py 0000664 0000000 0000000 00000005200 14645232030 0027467 0 ustar 00root root 0000000 0000000 from pubnub import utils
from pubnub.endpoints.endpoint import Endpoint
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.errors import (
PNERR_MESSAGE_ACTION_MISSING,
PNERR_MESSAGE_ACTION_TYPE_MISSING,
PNERR_MESSAGE_ACTION_VALUE_MISSING,
PNERR_MESSAGE_TIMETOKEN_MISSING,
)
from pubnub.exceptions import PubNubException
from pubnub.models.consumer.message_actions import PNAddMessageActionResult
class AddMessageAction(Endpoint):
ADD_MESSAGE_ACTION_PATH = "/v1/message-actions/%s/channel/%s/message/%s"
def __init__(self, pubnub):
Endpoint.__init__(self, pubnub)
self._channel = None
self._message_action = None
def channel(self, channel):
self._channel = str(channel)
return self
def message_action(self, message_action):
self._message_action = message_action
return self
def custom_params(self):
return {}
def build_data(self):
params = {
"type": self._message_action.type,
"value": self._message_action.value,
}
return utils.write_value_as_string(params)
def build_path(self):
return AddMessageAction.ADD_MESSAGE_ACTION_PATH % (
self.pubnub.config.subscribe_key,
utils.url_encode(self._channel),
self._message_action.message_timetoken,
)
def http_method(self):
return HttpMethod.POST
def validate_params(self):
self.validate_subscribe_key()
self.validate_channel()
self.validate_message_action()
def create_response(self, envelope): # pylint: disable=W0221
return PNAddMessageActionResult(envelope["data"])
def is_auth_required(self):
return True
def request_timeout(self):
return self.pubnub.config.non_subscribe_request_timeout
def connect_timeout(self):
return self.pubnub.config.connect_timeout
def operation_type(self):
return PNOperationType.PNAddMessageAction
def name(self):
return "Add message action"
def validate_message_action(self):
if self._message_action is None:
raise PubNubException(pn_error=PNERR_MESSAGE_ACTION_MISSING)
if self._message_action.message_timetoken is None:
raise PubNubException(pn_error=PNERR_MESSAGE_TIMETOKEN_MISSING)
if self._message_action.type is None or len(self._message_action.type) == 0:
raise PubNubException(pn_error=PNERR_MESSAGE_ACTION_TYPE_MISSING)
if self._message_action.value is None or len(self._message_action.value) == 0:
raise PubNubException(pn_error=PNERR_MESSAGE_ACTION_VALUE_MISSING)
bdraco-freenub-69809e8/pubnub/endpoints/message_actions/get_message_actions.py 0000664 0000000 0000000 00000004427 14645232030 0027713 0 ustar 00root root 0000000 0000000 from pubnub import utils
from pubnub.endpoints.endpoint import Endpoint
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.models.consumer.message_actions import (
PNGetMessageActionsResult,
PNMessageAction,
)
class GetMessageActions(Endpoint):
GET_MESSAGE_ACTIONS_PATH = "/v1/message-actions/%s/channel/%s"
MAX_LIMIT = 100
def __init__(self, pubnub):
Endpoint.__init__(self, pubnub)
self._channel = None
self._start = None
self._end = None
self._limit = GetMessageActions.MAX_LIMIT
def channel(self, channel):
self._channel = str(channel)
return self
def start(self, start):
assert isinstance(start, str)
self._start = start
return self
def end(self, end):
assert isinstance(end, str)
self._end = end
return self
def limit(self, limit):
assert isinstance(limit, str)
self._limit = limit
return self
def custom_params(self):
params = {}
if self._start is not None:
params["start"] = self._start
if self._end is not None and self._start is None:
params["end"] = self._end
if self._limit != GetMessageActions.MAX_LIMIT:
params["limit"] = self._limit
return params
def build_path(self):
return GetMessageActions.GET_MESSAGE_ACTIONS_PATH % (
self.pubnub.config.subscribe_key,
utils.url_encode(self._channel),
)
def http_method(self):
return HttpMethod.GET
def is_auth_required(self):
return True
def validate_params(self):
self.validate_subscribe_key()
self.validate_channel()
def create_response(self, envelope): # pylint: disable=W0221
result = envelope
result["actions"] = []
for action in result["data"]:
result["actions"].append(PNMessageAction(action))
return PNGetMessageActionsResult(result)
def request_timeout(self):
return self.pubnub.config.non_subscribe_request_timeout
def connect_timeout(self):
return self.pubnub.config.connect_timeout
def operation_type(self):
return PNOperationType.PNGetMessageActions
def name(self):
return "Get message actions"
bdraco-freenub-69809e8/pubnub/endpoints/message_actions/remove_message_action.py 0000664 0000000 0000000 00000004305 14645232030 0030241 0 ustar 00root root 0000000 0000000 from pubnub import utils
from pubnub.endpoints.endpoint import Endpoint
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.errors import (
PNERR_MESSAGE_ACTION_TIMETOKEN_MISSING,
PNERR_MESSAGE_TIMETOKEN_MISSING,
)
from pubnub.exceptions import PubNubException
class RemoveMessageAction(Endpoint):
REMOVE_MESSAGE_ACTION_PATH = (
"/v1/message-actions/%s/channel/%s/message/%s/action/%s"
)
def __init__(self, pubnub):
Endpoint.__init__(self, pubnub)
self._channel = None
self._message_timetoken = None
self._action_timetoken = None
def channel(self, channel):
self._channel = str(channel)
return self
def message_timetoken(self, message_timetoken):
self._message_timetoken = message_timetoken
return self
def action_timetoken(self, action_timetoken):
self._action_timetoken = action_timetoken
return self
def custom_params(self):
return {}
def build_data(self):
return None
def build_path(self):
return RemoveMessageAction.REMOVE_MESSAGE_ACTION_PATH % (
self.pubnub.config.subscribe_key,
utils.url_encode(self._channel),
self._message_timetoken,
self._action_timetoken,
)
def http_method(self):
return HttpMethod.DELETE
def validate_params(self):
self.validate_subscribe_key()
self.validate_channel()
self.validate_timetokens()
def create_response(self, envelope): # pylint: disable=W0221
return {}
def is_auth_required(self):
return True
def request_timeout(self):
return self.pubnub.config.non_subscribe_request_timeout
def connect_timeout(self):
return self.pubnub.config.connect_timeout
def operation_type(self):
return PNOperationType.PNDeleteMessageAction
def name(self):
return "Remove message action"
def validate_timetokens(self):
if self._message_timetoken is None:
raise PubNubException(pn_error=PNERR_MESSAGE_TIMETOKEN_MISSING)
if self._action_timetoken is None:
raise PubNubException(pn_error=PNERR_MESSAGE_ACTION_TIMETOKEN_MISSING)
bdraco-freenub-69809e8/pubnub/endpoints/message_count.py 0000664 0000000 0000000 00000004120 14645232030 0023366 0 ustar 00root root 0000000 0000000 from pubnub import utils
from pubnub.endpoints.endpoint import Endpoint
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.exceptions import PubNubException
from pubnub.models.consumer.message_count import PNMessageCountResult
class MessageCount(Endpoint):
MESSAGE_COUNT_PATH = "/v3/history/sub-key/%s/message-counts/%s"
def __init__(self, pubnub):
Endpoint.__init__(self, pubnub)
self._channel = []
self._channels_timetoken = []
def channel(self, channel):
utils.extend_list(self._channel, channel)
return self
def channel_timetokens(self, timetokens):
timetokens = [str(item) for item in timetokens]
utils.extend_list(self._channels_timetoken, timetokens)
return self
def custom_params(self):
params = {}
if len(self._channels_timetoken) > 0:
if len(self._channels_timetoken) > 1:
params["channelsTimetoken"] = utils.join_items(self._channels_timetoken)
else:
params["timetoken"] = self._channels_timetoken[0]
return params
def build_path(self):
return MessageCount.MESSAGE_COUNT_PATH % (
self.pubnub.config.subscribe_key,
utils.join_channels(self._channel),
)
def http_method(self):
return HttpMethod.GET
def is_auth_required(self):
return True
def validate_params(self):
self.validate_subscribe_key()
self.validate_channel()
if len(self._channels_timetoken) != len(self._channel):
raise PubNubException(
"The number of channels and the number of timetokens do not match."
)
def create_response(self, result): # pylint: disable=W0221
return PNMessageCountResult(result)
def request_timeout(self):
return self.pubnub.config.non_subscribe_request_timeout
def connect_timeout(self):
return self.pubnub.config.connect_timeout
def operation_type(self):
return PNOperationType.PNMessageCountOperation
def name(self):
return "Message Count"
bdraco-freenub-69809e8/pubnub/endpoints/mixins.py 0000664 0000000 0000000 00000001542 14645232030 0022046 0 ustar 00root root 0000000 0000000 from pubnub.errors import PNERR_UUID_MISSING
from pubnub.exceptions import PubNubException
class UUIDValidatorMixin:
def validate_uuid(self):
if self._uuid is None or not isinstance(self._uuid, str):
raise PubNubException(pn_error=PNERR_UUID_MISSING)
class TimeTokenOverrideMixin:
def replicate(self, replicate):
self._replicate = replicate
return self
def ptto(self, timetoken):
if timetoken:
assert isinstance(timetoken, int)
self._ptto = timetoken
return self
def custom_params(self):
params = {}
if self._replicate is not None:
if self._replicate:
params["norep"] = "false"
else:
params["norep"] = "true"
if self._ptto:
params["ptto"] = self._ptto
return params
bdraco-freenub-69809e8/pubnub/endpoints/objects_v2/ 0000775 0000000 0000000 00000000000 14645232030 0022223 5 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/pubnub/endpoints/objects_v2/__init__.py 0000664 0000000 0000000 00000000000 14645232030 0024322 0 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/pubnub/endpoints/objects_v2/channel/ 0000775 0000000 0000000 00000000000 14645232030 0023633 5 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/pubnub/endpoints/objects_v2/channel/__init__.py 0000664 0000000 0000000 00000000000 14645232030 0025732 0 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/pubnub/endpoints/objects_v2/channel/get_all_channels.py 0000664 0000000 0000000 00000001720 14645232030 0027467 0 ustar 00root root 0000000 0000000 from pubnub.endpoints.objects_v2.objects_endpoint import (
IncludeCustomEndpoint,
ListEndpoint,
ObjectsEndpoint,
)
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.models.consumer.objects_v2.channel import PNGetAllChannelMetadataResult
class GetAllChannels(ObjectsEndpoint, ListEndpoint, IncludeCustomEndpoint):
GET_ALL_CHANNELS_PATH = "/v2/objects/%s/channels"
def __init__(self, pubnub):
ObjectsEndpoint.__init__(self, pubnub)
ListEndpoint.__init__(self)
IncludeCustomEndpoint.__init__(self)
def build_path(self):
return GetAllChannels.GET_ALL_CHANNELS_PATH % self.pubnub.config.subscribe_key
def create_response(self, envelope):
return PNGetAllChannelMetadataResult(envelope)
def operation_type(self):
return PNOperationType.PNGetAllChannelMetadataOperation
def name(self):
return "Get all Channels"
def http_method(self):
return HttpMethod.GET
bdraco-freenub-69809e8/pubnub/endpoints/objects_v2/channel/get_channel.py 0000664 0000000 0000000 00000002072 14645232030 0026455 0 ustar 00root root 0000000 0000000 from pubnub.endpoints.objects_v2.objects_endpoint import (
ChannelEndpoint,
IncludeCustomEndpoint,
ObjectsEndpoint,
)
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.models.consumer.objects_v2.channel import PNGetChannelMetadataResult
class GetChannel(ObjectsEndpoint, ChannelEndpoint, IncludeCustomEndpoint):
GET_CHANNEL_PATH = "/v2/objects/%s/channels/%s"
def __init__(self, pubnub):
ObjectsEndpoint.__init__(self, pubnub)
ChannelEndpoint.__init__(self)
IncludeCustomEndpoint.__init__(self)
def build_path(self):
return GetChannel.GET_CHANNEL_PATH % (
self.pubnub.config.subscribe_key,
self._channel,
)
def validate_specific_params(self):
self._validate_channel()
def create_response(self, envelope):
return PNGetChannelMetadataResult(envelope)
def operation_type(self):
return PNOperationType.PNGetChannelMetadataOperation
def name(self):
return "Get Channel"
def http_method(self):
return HttpMethod.GET
bdraco-freenub-69809e8/pubnub/endpoints/objects_v2/channel/remove_channel.py 0000664 0000000 0000000 00000001766 14645232030 0027204 0 ustar 00root root 0000000 0000000 from pubnub.endpoints.objects_v2.objects_endpoint import (
ChannelEndpoint,
ObjectsEndpoint,
)
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.models.consumer.objects_v2.channel import PNRemoveChannelMetadataResult
class RemoveChannel(ObjectsEndpoint, ChannelEndpoint):
REMOVE_CHANNEL_PATH = "/v2/objects/%s/channels/%s"
def __init__(self, pubnub):
ObjectsEndpoint.__init__(self, pubnub)
ChannelEndpoint.__init__(self)
def build_path(self):
return RemoveChannel.REMOVE_CHANNEL_PATH % (
self.pubnub.config.subscribe_key,
self._channel,
)
def validate_specific_params(self):
self._validate_channel()
def create_response(self, envelope):
return PNRemoveChannelMetadataResult(envelope)
def operation_type(self):
return PNOperationType.PNRemoveChannelMetadataOperation
def name(self):
return "Remove Channel"
def http_method(self):
return HttpMethod.DELETE
bdraco-freenub-69809e8/pubnub/endpoints/objects_v2/channel/set_channel.py 0000664 0000000 0000000 00000003210 14645232030 0026464 0 ustar 00root root 0000000 0000000 from pubnub import utils
from pubnub.endpoints.objects_v2.objects_endpoint import (
ChannelEndpoint,
CustomAwareEndpoint,
IncludeCustomEndpoint,
ObjectsEndpoint,
)
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.models.consumer.objects_v2.channel import PNSetChannelMetadataResult
class SetChannel(
ObjectsEndpoint, ChannelEndpoint, IncludeCustomEndpoint, CustomAwareEndpoint
):
SET_CHANNEL_PATH = "/v2/objects/%s/channels/%s"
def __init__(self, pubnub):
ObjectsEndpoint.__init__(self, pubnub)
ChannelEndpoint.__init__(self)
CustomAwareEndpoint.__init__(self)
IncludeCustomEndpoint.__init__(self)
self._name = None
self._description = None
def set_name(self, name):
self._name = str(name)
return self
def description(self, description):
self._description = str(description)
return self
def validate_specific_params(self):
self._validate_channel()
def build_path(self):
return SetChannel.SET_CHANNEL_PATH % (
self.pubnub.config.subscribe_key,
self._channel,
)
def build_data(self):
payload = {
"name": self._name,
"description": self._description,
"custom": self._custom,
}
return utils.write_value_as_string(payload)
def create_response(self, envelope):
return PNSetChannelMetadataResult(envelope)
def operation_type(self):
return PNOperationType.PNSetChannelMetadataOperation
def name(self):
return "Set UUID"
def http_method(self):
return HttpMethod.PATCH
bdraco-freenub-69809e8/pubnub/endpoints/objects_v2/members/ 0000775 0000000 0000000 00000000000 14645232030 0023655 5 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/pubnub/endpoints/objects_v2/members/__init__.py 0000664 0000000 0000000 00000000000 14645232030 0025754 0 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/pubnub/endpoints/objects_v2/members/get_channel_members.py 0000664 0000000 0000000 00000002437 14645232030 0030216 0 ustar 00root root 0000000 0000000 from pubnub.endpoints.objects_v2.objects_endpoint import (
ChannelEndpoint,
IncludeCustomEndpoint,
ListEndpoint,
ObjectsEndpoint,
UUIDIncludeEndpoint,
)
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.models.consumer.objects_v2.channel_members import PNGetChannelMembersResult
class GetChannelMembers(
ObjectsEndpoint,
ChannelEndpoint,
ListEndpoint,
IncludeCustomEndpoint,
UUIDIncludeEndpoint,
):
GET_CHANNEL_MEMBERS_PATH = "/v2/objects/%s/channels/%s/uuids"
def __init__(self, pubnub):
ObjectsEndpoint.__init__(self, pubnub)
ChannelEndpoint.__init__(self)
ListEndpoint.__init__(self)
IncludeCustomEndpoint.__init__(self)
UUIDIncludeEndpoint.__init__(self)
def build_path(self):
return GetChannelMembers.GET_CHANNEL_MEMBERS_PATH % (
self.pubnub.config.subscribe_key,
self._channel,
)
def validate_specific_params(self):
self._validate_channel()
def create_response(self, envelope):
return PNGetChannelMembersResult(envelope)
def operation_type(self):
return PNOperationType.PNGetChannelMembersOperation
def name(self):
return "Get Channel Members"
def http_method(self):
return HttpMethod.GET
bdraco-freenub-69809e8/pubnub/endpoints/objects_v2/members/manage_channel_members.py 0000664 0000000 0000000 00000004014 14645232030 0030660 0 ustar 00root root 0000000 0000000 from pubnub import utils
from pubnub.endpoints.objects_v2.objects_endpoint import (
ChannelEndpoint,
IncludeCustomEndpoint,
ListEndpoint,
ObjectsEndpoint,
UUIDIncludeEndpoint,
)
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.models.consumer.objects_v2.channel_members import (
PNManageChannelMembersResult,
)
class ManageChannelMembers(
ObjectsEndpoint,
ChannelEndpoint,
ListEndpoint,
IncludeCustomEndpoint,
UUIDIncludeEndpoint,
):
MANAGE_CHANNELS_MEMBERS_PATH = "/v2/objects/%s/channels/%s/uuids"
def __init__(self, pubnub):
ObjectsEndpoint.__init__(self, pubnub)
ChannelEndpoint.__init__(self)
ListEndpoint.__init__(self)
IncludeCustomEndpoint.__init__(self)
UUIDIncludeEndpoint.__init__(self)
self._uuids_to_set = []
self._uuids_to_remove = []
def set(self, uuids_to_set):
self._uuids_to_set = list(uuids_to_set)
return self
def remove(self, uuids_to_remove):
self._uuids_to_remove = list(uuids_to_remove)
return self
def validate_specific_params(self):
self._validate_channel()
def build_path(self):
return ManageChannelMembers.MANAGE_CHANNELS_MEMBERS_PATH % (
self.pubnub.config.subscribe_key,
self._channel,
)
def build_data(self):
uuids_to_set = []
uuids_to_remove = []
for uuid in self._uuids_to_set:
uuids_to_set.append(uuid.to_payload_dict())
for uuid in self._uuids_to_remove:
uuids_to_remove.append(uuid.to_payload_dict())
payload = {"set": uuids_to_set, "delete": uuids_to_remove}
return utils.write_value_as_string(payload)
def create_response(self, envelope):
return PNManageChannelMembersResult(envelope)
def operation_type(self):
return PNOperationType.PNManageChannelMembersOperation
def name(self):
return "Manage Channels Members"
def http_method(self):
return HttpMethod.PATCH
bdraco-freenub-69809e8/pubnub/endpoints/objects_v2/members/remove_channel_members.py 0000664 0000000 0000000 00000003313 14645232030 0030726 0 ustar 00root root 0000000 0000000 from pubnub import utils
from pubnub.endpoints.objects_v2.objects_endpoint import (
ChannelEndpoint,
IncludeCustomEndpoint,
ListEndpoint,
ObjectsEndpoint,
UUIDIncludeEndpoint,
)
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.models.consumer.objects_v2.channel_members import (
PNRemoveChannelMembersResult,
)
class RemoveChannelMembers(
ObjectsEndpoint,
ChannelEndpoint,
ListEndpoint,
IncludeCustomEndpoint,
UUIDIncludeEndpoint,
):
REMOVE_CHANNEL_MEMBERS_PATH = "/v2/objects/%s/channels/%s/uuids"
def __init__(self, pubnub):
ObjectsEndpoint.__init__(self, pubnub)
ListEndpoint.__init__(self)
ChannelEndpoint.__init__(self)
IncludeCustomEndpoint.__init__(self)
UUIDIncludeEndpoint.__init__(self)
self._uuids = []
def uuids(self, uuids):
self._uuids = list(uuids)
return self
def build_path(self):
return RemoveChannelMembers.REMOVE_CHANNEL_MEMBERS_PATH % (
self.pubnub.config.subscribe_key,
self._channel,
)
def build_data(self):
uuids_to_delete = []
for uuid in self._uuids:
uuids_to_delete.append(uuid.to_payload_dict())
payload = {"set": [], "delete": uuids_to_delete}
return utils.write_value_as_string(payload)
def validate_specific_params(self):
self._validate_channel()
def create_response(self, envelope):
return PNRemoveChannelMembersResult(envelope)
def operation_type(self):
return PNOperationType.PNRemoveChannelMembersOperation
def name(self):
return "Remove Channel Members"
def http_method(self):
return HttpMethod.PATCH
bdraco-freenub-69809e8/pubnub/endpoints/objects_v2/members/set_channel_members.py 0000664 0000000 0000000 00000003241 14645232030 0030224 0 ustar 00root root 0000000 0000000 from pubnub import utils
from pubnub.endpoints.objects_v2.objects_endpoint import (
ChannelEndpoint,
IncludeCustomEndpoint,
ListEndpoint,
ObjectsEndpoint,
UUIDIncludeEndpoint,
)
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.models.consumer.objects_v2.channel_members import PNSetChannelMembersResult
class SetChannelMembers(
ObjectsEndpoint,
ChannelEndpoint,
ListEndpoint,
IncludeCustomEndpoint,
UUIDIncludeEndpoint,
):
SET_CHANNEL_MEMBERS_PATH = "/v2/objects/%s/channels/%s/uuids"
def __init__(self, pubnub):
ObjectsEndpoint.__init__(self, pubnub)
ListEndpoint.__init__(self)
ChannelEndpoint.__init__(self)
IncludeCustomEndpoint.__init__(self)
UUIDIncludeEndpoint.__init__(self)
self._uuids = []
def uuids(self, uuids):
self._uuids = list(uuids)
return self
def validate_specific_params(self):
self._validate_channel()
def build_path(self):
return SetChannelMembers.SET_CHANNEL_MEMBERS_PATH % (
self.pubnub.config.subscribe_key,
self._channel,
)
def build_data(self):
uuids_to_set = []
for uuid in self._uuids:
uuids_to_set.append(uuid.to_payload_dict())
payload = {"set": uuids_to_set, "delete": []}
return utils.write_value_as_string(payload)
def create_response(self, envelope):
return PNSetChannelMembersResult(envelope)
def operation_type(self):
return PNOperationType.PNSetChannelMembersOperation
def name(self):
return "Set Channel Members"
def http_method(self):
return HttpMethod.PATCH
bdraco-freenub-69809e8/pubnub/endpoints/objects_v2/memberships/ 0000775 0000000 0000000 00000000000 14645232030 0024541 5 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/pubnub/endpoints/objects_v2/memberships/__init__.py 0000664 0000000 0000000 00000000000 14645232030 0026640 0 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/pubnub/endpoints/objects_v2/memberships/get_memberships.py 0000664 0000000 0000000 00000002406 14645232030 0030272 0 ustar 00root root 0000000 0000000 from pubnub.endpoints.objects_v2.objects_endpoint import (
ChannelIncludeEndpoint,
IncludeCustomEndpoint,
ListEndpoint,
ObjectsEndpoint,
UuidEndpoint,
)
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.models.consumer.objects_v2.memberships import PNGetMembershipsResult
class GetMemberships(
ObjectsEndpoint,
UuidEndpoint,
ListEndpoint,
IncludeCustomEndpoint,
ChannelIncludeEndpoint,
):
GET_MEMBERSHIPS_PATH = "/v2/objects/%s/uuids/%s/channels"
def __init__(self, pubnub):
ObjectsEndpoint.__init__(self, pubnub)
UuidEndpoint.__init__(self)
ListEndpoint.__init__(self)
IncludeCustomEndpoint.__init__(self)
ChannelIncludeEndpoint.__init__(self)
def build_path(self):
return GetMemberships.GET_MEMBERSHIPS_PATH % (
self.pubnub.config.subscribe_key,
self._effective_uuid(),
)
def validate_specific_params(self):
self._validate_uuid()
def create_response(self, envelope):
return PNGetMembershipsResult(envelope)
def operation_type(self):
return PNOperationType.PNGetMembershipsOperation
def name(self):
return "Get Memberships"
def http_method(self):
return HttpMethod.GET
bdraco-freenub-69809e8/pubnub/endpoints/objects_v2/memberships/manage_memberships.py 0000664 0000000 0000000 00000004442 14645232030 0030745 0 ustar 00root root 0000000 0000000 from pubnub import utils
from pubnub.endpoints.objects_v2.objects_endpoint import (
ChannelIncludeEndpoint,
IncludeCustomEndpoint,
ListEndpoint,
ObjectsEndpoint,
UuidEndpoint,
)
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.models.consumer.objects_v2.memberships import PNManageMembershipsResult
class ManageMemberships(
ObjectsEndpoint,
UuidEndpoint,
ListEndpoint,
IncludeCustomEndpoint,
ChannelIncludeEndpoint,
):
MANAGE_MEMBERSHIPS_PATH = "/v2/objects/%s/uuids/%s/channels"
def __init__(self, pubnub):
ObjectsEndpoint.__init__(self, pubnub)
UuidEndpoint.__init__(self)
ListEndpoint.__init__(self)
IncludeCustomEndpoint.__init__(self)
ChannelIncludeEndpoint.__init__(self)
self._channel_memberships_to_set = []
self._channel_memberships_to_remove = []
def set(self, channel_memberships_to_set):
self._channel_memberships_to_set = list(channel_memberships_to_set)
return self
def remove(self, channel_memberships_to_remove):
self._channel_memberships_to_remove = list(channel_memberships_to_remove)
return self
def validate_specific_params(self):
self._validate_uuid()
def build_path(self):
return ManageMemberships.MANAGE_MEMBERSHIPS_PATH % (
self.pubnub.config.subscribe_key,
self._effective_uuid(),
)
def build_data(self):
channel_memberships_to_set = []
channel_memberships_to_remove = []
for channel_membership in self._channel_memberships_to_set:
channel_memberships_to_set.append(channel_membership.to_payload_dict())
for channel_membership in self._channel_memberships_to_remove:
channel_memberships_to_remove.append(channel_membership.to_payload_dict())
payload = {
"set": channel_memberships_to_set,
"delete": channel_memberships_to_remove,
}
return utils.write_value_as_string(payload)
def create_response(self, envelope):
return PNManageMembershipsResult(envelope)
def operation_type(self):
return PNOperationType.PNManageMembershipsOperation
def name(self):
return "Manage Memberships"
def http_method(self):
return HttpMethod.PATCH
bdraco-freenub-69809e8/pubnub/endpoints/objects_v2/memberships/remove_memberships.py 0000664 0000000 0000000 00000003503 14645232030 0031007 0 ustar 00root root 0000000 0000000 from pubnub import utils
from pubnub.endpoints.objects_v2.objects_endpoint import (
ChannelIncludeEndpoint,
IncludeCustomEndpoint,
ListEndpoint,
ObjectsEndpoint,
UuidEndpoint,
)
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.models.consumer.objects_v2.memberships import PNRemoveMembershipsResult
class RemoveMemberships(
ObjectsEndpoint,
UuidEndpoint,
ListEndpoint,
IncludeCustomEndpoint,
ChannelIncludeEndpoint,
):
REMOVE_MEMBERSHIPS_PATH = "/v2/objects/%s/uuids/%s/channels"
def __init__(self, pubnub):
ObjectsEndpoint.__init__(self, pubnub)
ListEndpoint.__init__(self)
UuidEndpoint.__init__(self)
IncludeCustomEndpoint.__init__(self)
ChannelIncludeEndpoint.__init__(self)
self._channel_memberships = []
def channel_memberships(self, channel_memberships):
self._channel_memberships = list(channel_memberships)
return self
def build_path(self):
return RemoveMemberships.REMOVE_MEMBERSHIPS_PATH % (
self.pubnub.config.subscribe_key,
self._effective_uuid(),
)
def build_data(self):
channel_memberships_to_delete = []
for channel_membership in self._channel_memberships:
channel_memberships_to_delete.append(channel_membership.to_payload_dict())
payload = {"set": [], "delete": channel_memberships_to_delete}
return utils.write_value_as_string(payload)
def validate_specific_params(self):
self._validate_uuid()
def create_response(self, envelope):
return PNRemoveMembershipsResult(envelope)
def operation_type(self):
return PNOperationType.PNRemoveMembershipsOperation
def name(self):
return "Remove Memberships"
def http_method(self):
return HttpMethod.PATCH
bdraco-freenub-69809e8/pubnub/endpoints/objects_v2/memberships/set_memberships.py 0000664 0000000 0000000 00000003440 14645232030 0030305 0 ustar 00root root 0000000 0000000 from pubnub import utils
from pubnub.endpoints.objects_v2.objects_endpoint import (
ChannelIncludeEndpoint,
IncludeCustomEndpoint,
ListEndpoint,
ObjectsEndpoint,
UuidEndpoint,
)
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.models.consumer.objects_v2.memberships import PNSetMembershipsResult
class SetMemberships(
ObjectsEndpoint,
ListEndpoint,
IncludeCustomEndpoint,
ChannelIncludeEndpoint,
UuidEndpoint,
):
SET_MEMBERSHIP_PATH = "/v2/objects/%s/uuids/%s/channels"
def __init__(self, pubnub):
ObjectsEndpoint.__init__(self, pubnub)
UuidEndpoint.__init__(self)
ListEndpoint.__init__(self)
IncludeCustomEndpoint.__init__(self)
ChannelIncludeEndpoint.__init__(self)
self._channel_memberships = []
def channel_memberships(self, channel_memberships):
self._channel_memberships = list(channel_memberships)
return self
def validate_specific_params(self):
self._validate_uuid()
def build_path(self):
return SetMemberships.SET_MEMBERSHIP_PATH % (
self.pubnub.config.subscribe_key,
self._effective_uuid(),
)
def build_data(self):
channel_memberships_to_set = []
for channel_membership in self._channel_memberships:
channel_memberships_to_set.append(channel_membership.to_payload_dict())
payload = {"set": channel_memberships_to_set, "delete": []}
return utils.write_value_as_string(payload)
def create_response(self, envelope):
return PNSetMembershipsResult(envelope)
def operation_type(self):
return PNOperationType.PNSetMembershipsOperation
def name(self):
return "Set Memberships"
def http_method(self):
return HttpMethod.PATCH
bdraco-freenub-69809e8/pubnub/endpoints/objects_v2/objects_endpoint.py 0000664 0000000 0000000 00000013142 14645232030 0026127 0 ustar 00root root 0000000 0000000 import logging
from abc import ABCMeta
from pubnub import utils
from pubnub.endpoints.endpoint import Endpoint
from pubnub.errors import PNERR_CHANNEL_MISSING, PNERR_UUID_MISSING
from pubnub.exceptions import PubNubException
from pubnub.models.consumer.objects_v2.page import Next, Previous
logger = logging.getLogger("pubnub")
class ObjectsEndpoint(Endpoint):
__metaclass__ = ABCMeta
def __init__(self, pubnub):
Endpoint.__init__(self, pubnub)
def is_auth_required(self):
return True
def connect_timeout(self):
return self.pubnub.config.connect_timeout
def request_timeout(self):
return self.pubnub.config.non_subscribe_request_timeout
def validate_params(self):
self.validate_subscribe_key()
self.validate_specific_params()
def validate_specific_params(self):
pass
def encoded_params(self):
params = {}
if isinstance(self, ListEndpoint):
if self._filter:
params["filter"] = utils.url_encode(str(self._filter))
return params
def custom_params(self):
params = {}
inclusions = []
if isinstance(self, IncludeCustomEndpoint):
if self._include_custom:
inclusions.append("custom")
if isinstance(self, UUIDIncludeEndpoint):
if self._uuid_details_level:
if self._uuid_details_level == UUIDIncludeEndpoint.UUID:
inclusions.append("uuid")
elif self._uuid_details_level == UUIDIncludeEndpoint.UUID_WITH_CUSTOM:
inclusions.append("uuid.custom")
if isinstance(self, ChannelIncludeEndpoint):
if self._channel_details_level:
if self._channel_details_level == ChannelIncludeEndpoint.CHANNEL:
inclusions.append("channel")
elif (
self._channel_details_level
== ChannelIncludeEndpoint.CHANNEL_WITH_CUSTOM
):
inclusions.append("channel.custom")
if isinstance(self, ListEndpoint):
if self._filter:
params["filter"] = str(self._filter)
if self._limit:
params["limit"] = int(self._limit)
if self._include_total_count:
params["count"] = bool(self._include_total_count)
if self._sort_keys:
joined_sort_params_array = []
for sort_key in self._sort_keys:
joined_sort_params_array.append(
f"{sort_key.key_str()}:{sort_key.dir_str()}"
)
params["sort"] = ",".join(joined_sort_params_array)
if self._page:
if isinstance(self._page, Next):
params["start"] = self._page.hash
elif isinstance(self._page, Previous):
params["end"] = self._page.hash
else:
raise ValueError()
if len(inclusions) > 0:
params["include"] = ",".join(inclusions)
return params
class CustomAwareEndpoint:
__metaclass__ = ABCMeta
def __init__(self):
self._custom = None
def custom(self, custom):
self._custom = dict(custom)
return self
class ChannelEndpoint:
__metaclass__ = ABCMeta
def __init__(self):
self._channel = None
def channel(self, channel):
self._channel = str(channel)
return self
def _validate_channel(self):
if self._channel is None or len(self._channel) == 0:
raise PubNubException(pn_error=PNERR_CHANNEL_MISSING)
class UuidEndpoint:
__metaclass__ = ABCMeta
def __init__(self):
self._uuid = None
def uuid(self, uuid):
self._uuid = str(uuid)
return self
def _effective_uuid(self):
if self._uuid is not None:
return self._uuid
else:
return self.pubnub.config.uuid
def _validate_uuid(self):
if self._effective_uuid() is None or len(self._effective_uuid()) == 0:
raise PubNubException(pn_error=PNERR_UUID_MISSING)
class ListEndpoint:
__metaclass__ = ABCMeta
def __init__(self):
self._limit = None
self._filter = None
self._include_total_count = None
self._sort_keys = None
self._page = None
def limit(self, limit):
self._limit = int(limit)
return self
def filter(self, filter):
self._filter = str(filter)
return self
def include_total_count(self, include_total_count):
self._include_total_count = bool(include_total_count)
return self
def sort(self, *sort_keys):
self._sort_keys = sort_keys
return self
def page(self, page):
self._page = page
return self
class IncludeCustomEndpoint:
__metaclass__ = ABCMeta
def __init__(self):
self._include_custom = None
def include_custom(self, include_custom):
self._include_custom = bool(include_custom)
return self
class UUIDIncludeEndpoint:
__metaclass__ = ABCMeta
UUID = 1
UUID_WITH_CUSTOM = 2
def __init__(self):
self._uuid_details_level = None
def include_uuid(self, uuid_details_level):
self._uuid_details_level = uuid_details_level
return self
class ChannelIncludeEndpoint:
__metaclass__ = ABCMeta
CHANNEL = 1
CHANNEL_WITH_CUSTOM = 2
def __init__(self):
self._channel_details_level = None
def include_channel(self, channel_details_level):
self._channel_details_level = channel_details_level
return self
bdraco-freenub-69809e8/pubnub/endpoints/objects_v2/uuid/ 0000775 0000000 0000000 00000000000 14645232030 0023171 5 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/pubnub/endpoints/objects_v2/uuid/__init__.py 0000664 0000000 0000000 00000000000 14645232030 0025270 0 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/pubnub/endpoints/objects_v2/uuid/get_all_uuid.py 0000664 0000000 0000000 00000001654 14645232030 0026206 0 ustar 00root root 0000000 0000000 from pubnub.endpoints.objects_v2.objects_endpoint import (
IncludeCustomEndpoint,
ListEndpoint,
ObjectsEndpoint,
)
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.models.consumer.objects_v2.uuid import PNGetAllUUIDMetadataResult
class GetAllUuid(ObjectsEndpoint, ListEndpoint, IncludeCustomEndpoint):
GET_ALL_UID_PATH = "/v2/objects/%s/uuids"
def __init__(self, pubnub):
ObjectsEndpoint.__init__(self, pubnub)
ListEndpoint.__init__(self)
IncludeCustomEndpoint.__init__(self)
def build_path(self):
return GetAllUuid.GET_ALL_UID_PATH % self.pubnub.config.subscribe_key
def create_response(self, envelope):
return PNGetAllUUIDMetadataResult(envelope)
def operation_type(self):
return PNOperationType.PNGetAllUuidMetadataOperation
def name(self):
return "Get all UUIDs"
def http_method(self):
return HttpMethod.GET
bdraco-freenub-69809e8/pubnub/endpoints/objects_v2/uuid/get_uuid.py 0000664 0000000 0000000 00000002027 14645232030 0025351 0 ustar 00root root 0000000 0000000 from pubnub.endpoints.objects_v2.objects_endpoint import (
IncludeCustomEndpoint,
ObjectsEndpoint,
UuidEndpoint,
)
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.models.consumer.objects_v2.uuid import PNGetUUIDMetadataResult
class GetUuid(ObjectsEndpoint, UuidEndpoint, IncludeCustomEndpoint):
GET_UID_PATH = "/v2/objects/%s/uuids/%s"
def __init__(self, pubnub):
ObjectsEndpoint.__init__(self, pubnub)
UuidEndpoint.__init__(self)
IncludeCustomEndpoint.__init__(self)
def build_path(self):
return GetUuid.GET_UID_PATH % (
self.pubnub.config.subscribe_key,
self._effective_uuid(),
)
def validate_specific_params(self):
self._validate_uuid()
def create_response(self, envelope):
return PNGetUUIDMetadataResult(envelope)
def operation_type(self):
return PNOperationType.PNGetUuidMetadataOperation
def name(self):
return "Get UUID"
def http_method(self):
return HttpMethod.GET
bdraco-freenub-69809e8/pubnub/endpoints/objects_v2/uuid/remove_uuid.py 0000664 0000000 0000000 00000001706 14645232030 0026072 0 ustar 00root root 0000000 0000000 from pubnub.endpoints.objects_v2.objects_endpoint import ObjectsEndpoint, UuidEndpoint
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.models.consumer.objects_v2.uuid import PNRemoveUUIDMetadataResult
class RemoveUuid(ObjectsEndpoint, UuidEndpoint):
REMOVE_UID_PATH = "/v2/objects/%s/uuids/%s"
def __init__(self, pubnub):
ObjectsEndpoint.__init__(self, pubnub)
UuidEndpoint.__init__(self)
def build_path(self):
return RemoveUuid.REMOVE_UID_PATH % (
self.pubnub.config.subscribe_key,
self._effective_uuid(),
)
def validate_specific_params(self):
self._validate_uuid()
def create_response(self, envelope):
return PNRemoveUUIDMetadataResult(envelope)
def operation_type(self):
return PNOperationType.PNRemoveUuidMetadataOperation
def name(self):
return "Remove UUID"
def http_method(self):
return HttpMethod.DELETE
bdraco-freenub-69809e8/pubnub/endpoints/objects_v2/uuid/set_uuid.py 0000664 0000000 0000000 00000003656 14645232030 0025376 0 ustar 00root root 0000000 0000000 from pubnub import utils
from pubnub.endpoints.objects_v2.objects_endpoint import (
CustomAwareEndpoint,
IncludeCustomEndpoint,
ObjectsEndpoint,
UuidEndpoint,
)
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.models.consumer.objects_v2.uuid import PNSetUUIDMetadataResult
class SetUuid(
ObjectsEndpoint, UuidEndpoint, IncludeCustomEndpoint, CustomAwareEndpoint
):
SET_UID_PATH = "/v2/objects/%s/uuids/%s"
def __init__(self, pubnub):
ObjectsEndpoint.__init__(self, pubnub)
UuidEndpoint.__init__(self)
IncludeCustomEndpoint.__init__(self)
CustomAwareEndpoint.__init__(self)
self._name = None
self._email = None
self._external_id = None
self._profile_url = None
def set_name(self, name):
self._name = str(name)
return self
def email(self, email):
self._email = str(email)
return self
def external_id(self, external_id):
self._external_id = str(external_id)
return self
def profile_url(self, profile_url):
self._profile_url = str(profile_url)
return self
def build_path(self):
return SetUuid.SET_UID_PATH % (
self.pubnub.config.subscribe_key,
self._effective_uuid(),
)
def build_data(self):
payload = {
"name": self._name,
"email": self._email,
"externalId": self._external_id,
"profileUrl": self._profile_url,
"custom": self._custom,
}
return utils.write_value_as_string(payload)
def validate_specific_params(self):
self._validate_uuid()
def create_response(self, envelope):
return PNSetUUIDMetadataResult(envelope)
def operation_type(self):
return PNOperationType.PNSetUuidMetadataOperation
def name(self):
return "Set UUID"
def http_method(self):
return HttpMethod.PATCH
bdraco-freenub-69809e8/pubnub/endpoints/presence/ 0000775 0000000 0000000 00000000000 14645232030 0021767 5 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/pubnub/endpoints/presence/__init__.py 0000664 0000000 0000000 00000000000 14645232030 0024066 0 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/pubnub/endpoints/presence/get_state.py 0000664 0000000 0000000 00000004344 14645232030 0024325 0 ustar 00root root 0000000 0000000 from pubnub import utils
from pubnub.endpoints.endpoint import Endpoint
from pubnub.endpoints.mixins import UUIDValidatorMixin
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.models.consumer.presence import PNGetStateResult
class GetState(Endpoint, UUIDValidatorMixin):
# /v2/presence/sub-key//channel//uuid//data?state=
GET_STATE_PATH = "/v2/presence/sub-key/%s/channel/%s/uuid/%s"
def __init__(self, pubnub):
Endpoint.__init__(self, pubnub)
self._channels = []
self._groups = []
self._uuid = self.pubnub.uuid
def channels(self, channels):
utils.extend_list(self._channels, channels)
return self
def uuid(self, uuid):
self._uuid = uuid
return self
def channel_groups(self, channel_groups):
utils.extend_list(self._groups, channel_groups)
return self
def custom_params(self):
params = {}
if len(self._groups) > 0:
params["channel-group"] = utils.join_items(self._groups)
return params
def build_path(self):
return GetState.GET_STATE_PATH % (
self.pubnub.config.subscribe_key,
utils.join_channels(self._channels),
utils.url_encode(self._uuid),
)
def http_method(self):
return HttpMethod.GET
def validate_params(self):
self.validate_subscribe_key()
self.validate_channels_and_groups()
self.validate_uuid()
def create_response(self, envelope):
if len(self._channels) == 1 and len(self._groups) == 0:
channels = {self._channels[0]: envelope["payload"]}
else:
channels = envelope["payload"]["channels"]
return PNGetStateResult(channels)
def is_auth_required(self):
return True
def affected_channels(self):
return self._channels
def affected_channels_groups(self):
return self._groups
def request_timeout(self):
return self.pubnub.config.non_subscribe_request_timeout
def connect_timeout(self):
return self.pubnub.config.connect_timeout
def operation_type(self):
return PNOperationType.PNGetState
def name(self):
return "GetState"
bdraco-freenub-69809e8/pubnub/endpoints/presence/heartbeat.py 0000664 0000000 0000000 00000004220 14645232030 0024276 0 ustar 00root root 0000000 0000000 from pubnub import utils
from pubnub.endpoints.endpoint import Endpoint
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.errors import PNERR_CHANNEL_OR_GROUP_MISSING
from pubnub.exceptions import PubNubException
class Heartbeat(Endpoint):
# /v2/presence/sub-key//channel//heartbeat?uuid=
HEARTBEAT_PATH = "/v2/presence/sub-key/%s/channel/%s/heartbeat"
def __init__(self, pubnub):
super().__init__(pubnub)
self._channels = []
self._groups = []
self._state = None
def channels(self, channels):
utils.extend_list(self._channels, channels)
return self
def channel_groups(self, channel_groups):
utils.extend_list(self._groups, channel_groups)
return self
def state(self, state):
self._state = state
return self
def http_method(self):
return HttpMethod.GET
def validate_params(self):
self.validate_subscribe_key()
if len(self._channels) == 0 and len(self._groups) == 0:
raise PubNubException(pn_error=PNERR_CHANNEL_OR_GROUP_MISSING)
def build_path(self):
channels = utils.join_channels(self._channels)
return Heartbeat.HEARTBEAT_PATH % (self.pubnub.config.subscribe_key, channels)
def custom_params(self):
params = {"heartbeat": str(self.pubnub.config.presence_timeout)}
if len(self._groups) > 0:
params["channel-group"] = utils.join_items(self._groups)
if self._state is not None and len(self._state) > 0:
params["state"] = utils.url_write(self._state)
return params
def create_response(self, envelope):
return True
def is_auth_required(self):
return True
def affected_channels(self):
return None
def affected_channels_groups(self):
return None
def request_timeout(self):
return self.pubnub.config.non_subscribe_request_timeout
def connect_timeout(self):
return self.pubnub.config.connect_timeout
def operation_type(self):
return PNOperationType.PNHeartbeatOperation
def name(self):
return "Heartbeat"
bdraco-freenub-69809e8/pubnub/endpoints/presence/here_now.py 0000664 0000000 0000000 00000004375 14645232030 0024160 0 ustar 00root root 0000000 0000000 from pubnub import utils
from pubnub.endpoints.endpoint import Endpoint
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.models.consumer.presence import PNHereNowResult
class HereNow(Endpoint):
HERE_NOW_PATH = "/v2/presence/sub-key/%s/channel/%s"
HERE_NOW_GLOBAL_PATH = "/v2/presence/sub-key/%s"
def __init__(self, pubnub):
Endpoint.__init__(self, pubnub)
self._channels = []
self._channel_groups = []
self._include_state = False
self._include_uuids = True
def channels(self, channels):
utils.extend_list(self._channels, channels)
return self
def channel_groups(self, channel_groups):
utils.extend_list(self._channel_groups, channel_groups)
return self
def include_state(self, should_include_state):
self._include_state = should_include_state
return self
def include_uuids(self, include_uuids):
self._include_uuids = include_uuids
return self
def custom_params(self):
params = {}
if len(self._channel_groups) > 0:
params["channel-group"] = utils.join_items_and_encode(self._channel_groups)
if self._include_state:
params["state"] = "1"
if not self._include_uuids:
params["disable_uuids"] = "1"
return params
def build_path(self):
if len(self._channels) == 0 and len(self._channel_groups) == 0:
return HereNow.HERE_NOW_GLOBAL_PATH % self.pubnub.config.subscribe_key
else:
return HereNow.HERE_NOW_PATH % (
self.pubnub.config.subscribe_key,
utils.join_channels(self._channels),
)
def http_method(self):
return HttpMethod.GET
def validate_params(self):
self.validate_subscribe_key()
def is_auth_required(self):
return True
def create_response(self, envelope):
return PNHereNowResult.from_json(envelope, self._channels)
def request_timeout(self):
return self.pubnub.config.non_subscribe_request_timeout
def connect_timeout(self):
return self.pubnub.config.connect_timeout
def operation_type(self):
return PNOperationType.PNHereNowOperation
def name(self):
return "HereNow"
bdraco-freenub-69809e8/pubnub/endpoints/presence/leave.py 0000664 0000000 0000000 00000004167 14645232030 0023445 0 ustar 00root root 0000000 0000000 from pubnub import utils
from pubnub.endpoints.endpoint import Endpoint
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.errors import PNERR_CHANNEL_OR_GROUP_MISSING
from pubnub.exceptions import PubNubException
class Leave(Endpoint):
# /v2/presence/sub-key//channel//leave?uuid=
LEAVE_PATH = "/v2/presence/sub-key/%s/channel/%s/leave"
def __init__(self, pubnub):
Endpoint.__init__(self, pubnub)
self._channels = []
self._groups = []
def channels(self, channels):
if isinstance(channels, (list, tuple)):
self._channels.extend(channels)
else:
self._channels.extend(utils.split_items(channels))
return self
def channel_groups(self, channel_groups):
if isinstance(channel_groups, (list, tuple)):
self._groups.extend(channel_groups)
else:
self._groups.extend(utils.split_items(channel_groups))
return self
def custom_params(self):
params = {}
if len(self._groups) > 0:
params["channel-group"] = utils.join_items(self._groups)
return params
def build_path(self):
return Leave.LEAVE_PATH % (
self.pubnub.config.subscribe_key,
utils.join_channels(self._channels),
)
def http_method(self):
return HttpMethod.GET
def validate_params(self):
self.validate_subscribe_key()
if len(self._channels) == 0 and len(self._groups) == 0:
raise PubNubException(pn_error=PNERR_CHANNEL_OR_GROUP_MISSING)
def create_response(self, envelope):
return envelope
def is_auth_required(self):
return True
def affected_channels(self):
return self._channels
def affected_channels_groups(self):
return self._groups
def request_timeout(self):
return self.pubnub.config.non_subscribe_request_timeout
def connect_timeout(self):
return self.pubnub.config.connect_timeout
def operation_type(self):
return PNOperationType.PNUnsubscribeOperation
def name(self):
return "Leave"
bdraco-freenub-69809e8/pubnub/endpoints/presence/set_state.py 0000664 0000000 0000000 00000006130 14645232030 0024334 0 ustar 00root root 0000000 0000000 from pubnub import utils
from pubnub.dtos import StateOperation
from pubnub.endpoints.endpoint import Endpoint
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.errors import (
PNERR_STATE_MISSING,
PNERR_STATE_SETTER_FOR_GROUPS_NOT_SUPPORTED_YET,
)
from pubnub.exceptions import PubNubException
from pubnub.models.consumer.presence import PNSetStateResult
class SetState(Endpoint):
# /v2/presence/sub-key//channel//uuid//data?state=
SET_STATE_PATH = "/v2/presence/sub-key/%s/channel/%s/uuid/%s/data"
def __init__(self, pubnub, subscription_manager=None):
Endpoint.__init__(self, pubnub)
self._subscription_manager = subscription_manager
self._channels = []
self._groups = []
self._state = None
def channels(self, channels):
utils.extend_list(self._channels, channels)
return self
def channel_groups(self, channel_groups):
utils.extend_list(self._groups, channel_groups)
return self
def state(self, state):
self._state = state
return self
def encoded_params(self):
return {"state": utils.url_write(self._state)}
def custom_params(self):
if self._subscription_manager is not None:
self._subscription_manager.adapt_state_builder(
StateOperation(
channels=self._channels,
channel_groups=self._groups,
state=self._state,
)
)
params = {"state": utils.write_value_as_string(self._state)}
if len(self._groups) > 0:
params["channel-group"] = utils.join_items_and_encode(self._groups)
return params
def build_path(self):
return SetState.SET_STATE_PATH % (
self.pubnub.config.subscribe_key,
utils.join_channels(self._channels),
utils.url_encode(self.pubnub.uuid),
)
def http_method(self):
return HttpMethod.GET
def validate_params(self):
self.validate_subscribe_key()
self.validate_channels_and_groups()
if len(self._channels) == 0 and len(self._groups) > 0:
raise PubNubException(
pn_error=PNERR_STATE_SETTER_FOR_GROUPS_NOT_SUPPORTED_YET
)
if self._state is None or not isinstance(self._state, dict):
raise PubNubException(pn_error=PNERR_STATE_MISSING)
def create_response(self, envelope):
if "status" in envelope and envelope["status"] == 200:
return PNSetStateResult(envelope["payload"])
else:
return envelope
def is_auth_required(self):
return True
def affected_channels(self):
return self._channels
def affected_channels_groups(self):
return self._groups
def request_timeout(self):
return self.pubnub.config.non_subscribe_request_timeout
def connect_timeout(self):
return self.pubnub.config.connect_timeout
def operation_type(self):
return PNOperationType.PNSetStateOperation
def name(self):
return "SetState"
bdraco-freenub-69809e8/pubnub/endpoints/presence/where_now.py 0000664 0000000 0000000 00000002604 14645232030 0024340 0 ustar 00root root 0000000 0000000 from pubnub import utils
from pubnub.endpoints.endpoint import Endpoint
from pubnub.endpoints.mixins import UUIDValidatorMixin
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.models.consumer.presence import PNWhereNowResult
class WhereNow(Endpoint, UUIDValidatorMixin):
# /v2/presence/sub-key//uuid/
WHERE_NOW_PATH = "/v2/presence/sub-key/%s/uuid/%s"
def __init__(self, pubnub):
Endpoint.__init__(self, pubnub)
self._uuid = pubnub.config.uuid
def uuid(self, uuid):
self._uuid = uuid
return self
def custom_params(self):
return {}
def build_path(self):
return WhereNow.WHERE_NOW_PATH % (
self.pubnub.config.subscribe_key,
utils.url_encode(self._uuid),
)
def http_method(self):
return HttpMethod.GET
def validate_params(self):
self.validate_subscribe_key()
self.validate_uuid()
def is_auth_required(self):
return True
def create_response(self, envelope):
return PNWhereNowResult.from_json(envelope)
def request_timeout(self):
return self.pubnub.config.non_subscribe_request_timeout
def connect_timeout(self):
return self.pubnub.config.connect_timeout
def operation_type(self):
return PNOperationType.PNWhereNowOperation
def name(self):
return "WhereNow"
bdraco-freenub-69809e8/pubnub/endpoints/pubsub/ 0000775 0000000 0000000 00000000000 14645232030 0021463 5 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/pubnub/endpoints/pubsub/__init__.py 0000664 0000000 0000000 00000000000 14645232030 0023562 0 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/pubnub/endpoints/pubsub/fire.py 0000664 0000000 0000000 00000010146 14645232030 0022764 0 ustar 00root root 0000000 0000000 from pubnub import utils
from pubnub.endpoints.endpoint import Endpoint
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.errors import PNERR_MESSAGE_MISSING
from pubnub.exceptions import PubNubException
from pubnub.models.consumer.pubsub import PNFireResult
class Fire(Endpoint):
# /publish//////[?argument(s)]
FIRE_GET_PATH = "/publish/%s/%s/0/%s/%s/%s"
FIRE_POST_PATH = "/publish/%s/%s/0/%s/%s"
def __init__(self, pubnub):
Endpoint.__init__(self, pubnub)
self._channel = None
self._message = None
self._use_post = None
self._meta = None
def channel(self, channel):
self._channel = str(channel)
return self
def message(self, message):
self._message = message
return self
def use_post(self, use_post):
self._use_post = bool(use_post)
return self
def is_compressable(self):
return True
def use_compression(self, compress=True):
self._use_compression = bool(compress)
return self
def meta(self, meta):
self._meta = meta
return self
def build_data(self):
if self._use_post is True:
cipher = self.pubnub.config.cipher_key
if cipher is not None:
return (
'"'
+ self.pubnub.config.crypto.encrypt(
cipher, utils.write_value_as_string(self._message)
)
+ '"'
)
else:
return utils.write_value_as_string(self._message)
else:
return None
def custom_params(self):
params = {}
if self._meta is not None:
params["meta"] = utils.write_value_as_string(self._meta)
params["store"] = "0"
params["norep"] = "1"
if self.pubnub.config.auth_key is not None:
params["auth"] = utils.url_encode(self.pubnub.config.auth_key)
return params
def build_path(self):
if self._use_post:
return Fire.FIRE_POST_PATH % (
self.pubnub.config.publish_key,
self.pubnub.config.subscribe_key,
utils.url_encode(self._channel),
0,
)
else:
cipher = self.pubnub.config.cipher_key
stringified_message = utils.write_value_as_string(self._message)
if cipher is not None:
stringified_message = (
'"'
+ self.pubnub.config.crypto.encrypt(cipher, stringified_message)
+ '"'
)
stringified_message = utils.url_encode(stringified_message)
return Fire.FIRE_GET_PATH % (
self.pubnub.config.publish_key,
self.pubnub.config.subscribe_key,
utils.url_encode(self._channel),
0,
stringified_message,
)
def http_method(self):
if self._use_post is True:
return HttpMethod.POST
else:
return HttpMethod.GET
def validate_params(self):
self.validate_channel()
if self._message is None:
raise PubNubException(pn_error=PNERR_MESSAGE_MISSING)
self.validate_subscribe_key()
self.validate_publish_key()
def create_response(self, envelope):
"""
:param envelope: an already serialized json response
:return:
"""
if envelope is None:
return None
timetoken = int(envelope[2])
res = PNFireResult(envelope, timetoken)
return res
def is_auth_required(self):
return True
def affected_channels(self):
return None
def affected_channels_groups(self):
return None
def request_timeout(self):
return self.pubnub.config.non_subscribe_request_timeout
def connect_timeout(self):
return self.pubnub.config.connect_timeout
def operation_type(self):
return PNOperationType.PNFireOperation
def name(self):
return "Fire"
bdraco-freenub-69809e8/pubnub/endpoints/pubsub/publish.py 0000664 0000000 0000000 00000011617 14645232030 0023511 0 ustar 00root root 0000000 0000000 from pubnub import utils
from pubnub.endpoints.endpoint import Endpoint
from pubnub.endpoints.mixins import TimeTokenOverrideMixin
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.errors import PNERR_MESSAGE_MISSING
from pubnub.exceptions import PubNubException
from pubnub.models.consumer.pubsub import PNPublishResult
class Publish(Endpoint, TimeTokenOverrideMixin):
# /publish//////[?argument(s)]
PUBLISH_GET_PATH = "/publish/%s/%s/0/%s/%s/%s"
PUBLISH_POST_PATH = "/publish/%s/%s/0/%s/%s"
def __init__(self, pubnub):
super().__init__(pubnub)
self._channel = None
self._message = None
self._should_store = None
self._use_post = None
self._meta = None
self._replicate = None
self._ptto = None
self._ttl = None
def channel(self, channel):
self._channel = str(channel)
return self
def message(self, message):
self._message = message
return self
def use_post(self, use_post):
self._use_post = bool(use_post)
return self
def use_compression(self, compress=True):
self._use_compression = bool(compress)
return self
def is_compressable(self):
return True
def should_store(self, should_store):
self._should_store = bool(should_store)
return self
def meta(self, meta):
self._meta = meta
return self
def ttl(self, ttl):
self._ttl = ttl
return self
def build_data(self):
if self._use_post is True:
cipher = self.pubnub.config.cipher_key
if cipher is not None:
return (
'"'
+ self.pubnub.config.crypto.encrypt(
cipher, utils.write_value_as_string(self._message)
)
+ '"'
)
else:
return utils.write_value_as_string(self._message)
else:
return None
def encoded_params(self):
if self._meta:
return {"meta": utils.url_write(self._meta)}
else:
return {}
def custom_params(self):
params = TimeTokenOverrideMixin.custom_params(self)
if self._ttl:
params["ttl"] = self._ttl
if self._meta:
params["meta"] = utils.write_value_as_string(self._meta)
if self._should_store is not None:
if self._should_store:
params["store"] = "1"
else:
params["store"] = "0"
# REVIEW: should auth key be assigned here?
if self.pubnub.config.auth_key is not None:
params["auth"] = utils.url_encode(self.pubnub.config.auth_key)
return params
def build_path(self):
if self._use_post:
return Publish.PUBLISH_POST_PATH % (
self.pubnub.config.publish_key,
self.pubnub.config.subscribe_key,
utils.url_encode(self._channel),
0,
)
else:
cipher = self.pubnub.config.cipher_key
stringified_message = utils.write_value_as_string(self._message)
if cipher is not None:
stringified_message = (
'"'
+ self.pubnub.config.crypto.encrypt(cipher, stringified_message)
+ '"'
)
stringified_message = utils.url_encode(stringified_message)
return Publish.PUBLISH_GET_PATH % (
self.pubnub.config.publish_key,
self.pubnub.config.subscribe_key,
utils.url_encode(self._channel),
0,
stringified_message,
)
def http_method(self):
if self._use_post is True:
return HttpMethod.POST
else:
return HttpMethod.GET
def validate_params(self):
self.validate_channel()
if self._message is None:
raise PubNubException(pn_error=PNERR_MESSAGE_MISSING)
self.validate_subscribe_key()
self.validate_publish_key()
def create_response(self, envelope):
"""
:param envelope: an already serialized json response
:return:
"""
if envelope is None:
return None
timetoken = int(envelope[2])
res = PNPublishResult(envelope, timetoken)
return res
def is_auth_required(self):
return True
def affected_channels(self):
return None
def affected_channels_groups(self):
return None
def request_timeout(self):
return self.pubnub.config.non_subscribe_request_timeout
def connect_timeout(self):
return self.pubnub.config.connect_timeout
def operation_type(self):
return PNOperationType.PNPublishOperation
def name(self):
return "Publish"
bdraco-freenub-69809e8/pubnub/endpoints/pubsub/subscribe.py 0000664 0000000 0000000 00000005300 14645232030 0024014 0 ustar 00root root 0000000 0000000 from pubnub import utils
from pubnub.endpoints.endpoint import Endpoint
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.errors import PNERR_CHANNEL_OR_GROUP_MISSING
from pubnub.exceptions import PubNubException
class Subscribe(Endpoint):
# /subscribe////
SUBSCRIBE_PATH = "/v2/subscribe/%s/%s/0"
def __init__(self, pubnub):
super().__init__(pubnub)
self._channels = []
self._groups = []
self._region = None
self._filter_expression = None
self._timetoken = None
self._with_presence = None
def channels(self, channels):
utils.extend_list(self._channels, channels)
return self
def channel_groups(self, groups):
utils.extend_list(self._groups, groups)
return self
def timetoken(self, timetoken):
self._timetoken = timetoken
return self
def filter_expression(self, expr):
self._filter_expression = expr
return self
def region(self, region):
self._region = region
return self
def http_method(self):
return HttpMethod.GET
def validate_params(self):
self.validate_subscribe_key()
if len(self._channels) == 0 and len(self._groups) == 0:
raise PubNubException(pn_error=PNERR_CHANNEL_OR_GROUP_MISSING)
def build_path(self):
channels = utils.join_channels(self._channels)
return Subscribe.SUBSCRIBE_PATH % (self.pubnub.config.subscribe_key, channels)
def custom_params(self):
params = {}
if len(self._groups) > 0:
params["channel-group"] = utils.join_items_and_encode(self._groups)
if self._filter_expression is not None and len(self._filter_expression) > 0:
params["filter-expr"] = utils.url_encode(self._filter_expression)
if self._timetoken is not None:
params["tt"] = str(self._timetoken)
if self._region is not None:
params["tr"] = self._region
if not self.pubnub.config.heartbeat_default_values:
params["heartbeat"] = self.pubnub.config.presence_timeout
return params
def create_response(self, envelope):
return envelope
def is_auth_required(self):
return True
def affected_channels(self):
return self._channels
def affected_channels_groups(self):
return self._groups
def request_timeout(self):
return self.pubnub.config.subscribe_request_timeout
def connect_timeout(self):
return self.pubnub.config.connect_timeout
def operation_type(self):
return PNOperationType.PNSubscribeOperation
def name(self):
return "Subscribe"
bdraco-freenub-69809e8/pubnub/endpoints/push/ 0000775 0000000 0000000 00000000000 14645232030 0021142 5 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/pubnub/endpoints/push/__init__.py 0000664 0000000 0000000 00000000000 14645232030 0023241 0 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/pubnub/endpoints/push/add_channels_to_push.py 0000664 0000000 0000000 00000006514 14645232030 0025666 0 ustar 00root root 0000000 0000000 from pubnub import utils
from pubnub.endpoints.endpoint import Endpoint
from pubnub.enums import HttpMethod, PNOperationType, PNPushEnvironment, PNPushType
from pubnub.errors import (
PNERR_CHANNEL_MISSING,
PNERR_PUSH_DEVICE_MISSING,
PNERR_PUSH_TOPIC_MISSING,
PNERROR_PUSH_TYPE_MISSING,
)
from pubnub.exceptions import PubNubException
from pubnub.models.consumer.push import PNPushAddChannelResult
class AddChannelsToPush(Endpoint):
# v1/push/sub-key/{subKey}/devices/{pushToken}
ADD_PATH = "/v1/push/sub-key/%s/devices/%s"
# v2/push/sub-key/{subKey}/devices-apns2/{deviceApns2}
ADD_PATH_APNS2 = "/v2/push/sub-key/%s/devices-apns2/%s"
def __init__(self, pubnub):
Endpoint.__init__(self, pubnub)
self._channels = None
self._device_id = None
self._push_type = None
self._topic = None
self._environment = None
def channels(self, channels):
self._channels = channels
return self
def device_id(self, device_id):
self._device_id = device_id
return self
def push_type(self, push_type):
self._push_type = push_type
return self
def topic(self, topic):
self._topic = topic
return self
def environment(self, environment):
self._environment = environment
return self
def custom_params(self):
params = {}
params["add"] = utils.join_items(self._channels)
if self._push_type != PNPushType.APNS2:
params["type"] = utils.push_type_to_string(self._push_type)
else:
if self._environment is None:
self._environment = PNPushEnvironment.DEVELOPMENT
params["environment"] = self._environment
params["topic"] = self._topic
return params
def build_path(self):
if self._push_type != PNPushType.APNS2:
return AddChannelsToPush.ADD_PATH % (
self.pubnub.config.subscribe_key,
self._device_id,
)
else:
return AddChannelsToPush.ADD_PATH_APNS2 % (
self.pubnub.config.subscribe_key,
self._device_id,
)
def http_method(self):
return HttpMethod.GET
def validate_params(self):
self.validate_subscribe_key()
if not isinstance(self._channels, list) or len(self._channels) == 0:
raise PubNubException(pn_error=PNERR_CHANNEL_MISSING)
if not isinstance(self._device_id, str) or len(self._device_id) == 0:
raise PubNubException(pn_error=PNERR_PUSH_DEVICE_MISSING)
if self._push_type is None:
raise PubNubException(pn_error=PNERROR_PUSH_TYPE_MISSING)
if self._push_type == PNPushType.APNS2:
if not isinstance(self._topic, str) or len(self._topic) == 0:
raise PubNubException(pn_error=PNERR_PUSH_TOPIC_MISSING)
def create_response(self, envelope):
return PNPushAddChannelResult()
def is_auth_required(self):
return True
def request_timeout(self):
return self.pubnub.config.non_subscribe_request_timeout
def connect_timeout(self):
return self.pubnub.config.connect_timeout
def operation_type(self):
return PNOperationType.PNAddPushNotificationsOnChannelsOperation
def name(self):
return "AddChannelsToPush"
bdraco-freenub-69809e8/pubnub/endpoints/push/list_push_provisions.py 0000664 0000000 0000000 00000006243 14645232030 0026026 0 ustar 00root root 0000000 0000000 from pubnub import utils
from pubnub.endpoints.endpoint import Endpoint
from pubnub.enums import HttpMethod, PNOperationType, PNPushEnvironment, PNPushType
from pubnub.errors import (
PNERR_PUSH_DEVICE_MISSING,
PNERR_PUSH_TOPIC_MISSING,
PNERROR_PUSH_TYPE_MISSING,
)
from pubnub.exceptions import PubNubException
from pubnub.models.consumer.push import PNPushListProvisionsResult
class ListPushProvisions(Endpoint):
# v1/push/sub-key/{subKey}/devices/{pushToken}
LIST_PATH = "/v1/push/sub-key/%s/devices/%s"
# v2/push/sub-key/{subKey}/devices-apns2/{deviceApns2}
LIST_PATH_APNS2 = "/v2/push/sub-key/%s/devices-apns2/%s"
def __init__(self, pubnub):
Endpoint.__init__(self, pubnub)
self._device_id = None
self._push_type = None
self._topic = None
self._environment = None
def device_id(self, device_id):
self._device_id = device_id
return self
def push_type(self, push_type):
self._push_type = push_type
return self
def topic(self, topic):
self._topic = topic
return self
def environment(self, environment):
self._environment = environment
return self
def custom_params(self):
params = {}
if self._push_type != PNPushType.APNS2:
params["type"] = utils.push_type_to_string(self._push_type)
else:
if self._environment is None:
self._environment = PNPushEnvironment.DEVELOPMENT
params["environment"] = self._environment
params["topic"] = self._topic
return params
def build_path(self):
if self._push_type != PNPushType.APNS2:
return ListPushProvisions.LIST_PATH % (
self.pubnub.config.subscribe_key,
self._device_id,
)
else:
return ListPushProvisions.LIST_PATH_APNS2 % (
self.pubnub.config.subscribe_key,
self._device_id,
)
def http_method(self):
return HttpMethod.GET
def validate_params(self):
self.validate_subscribe_key()
if not isinstance(self._device_id, str) or len(self._device_id) == 0:
raise PubNubException(pn_error=PNERR_PUSH_DEVICE_MISSING)
if self._push_type is None:
raise PubNubException(pn_error=PNERROR_PUSH_TYPE_MISSING)
if self._push_type == PNPushType.APNS2:
if not isinstance(self._topic, str) or len(self._topic) == 0:
raise PubNubException(pn_error=PNERR_PUSH_TOPIC_MISSING)
def create_response(self, channels):
if channels is not None and len(channels) > 0 and isinstance(channels, list):
return PNPushListProvisionsResult(channels)
else:
return PNPushListProvisionsResult([])
def is_auth_required(self):
return True
def request_timeout(self):
return self.pubnub.config.non_subscribe_request_timeout
def connect_timeout(self):
return self.pubnub.config.connect_timeout
def operation_type(self):
return PNOperationType.PNPushNotificationEnabledChannelsOperation
def name(self):
return "ListPushProvisions"
bdraco-freenub-69809e8/pubnub/endpoints/push/remove_channels_from_push.py 0000664 0000000 0000000 00000006547 14645232030 0026762 0 ustar 00root root 0000000 0000000 from pubnub import utils
from pubnub.endpoints.endpoint import Endpoint
from pubnub.enums import HttpMethod, PNOperationType, PNPushEnvironment, PNPushType
from pubnub.errors import (
PNERR_CHANNEL_MISSING,
PNERR_PUSH_DEVICE_MISSING,
PNERR_PUSH_TOPIC_MISSING,
PNERROR_PUSH_TYPE_MISSING,
)
from pubnub.exceptions import PubNubException
from pubnub.models.consumer.push import PNPushRemoveChannelResult
class RemoveChannelsFromPush(Endpoint):
# v1/push/sub-key/{subKey}/devices/{pushToken}
REMOVE_PATH = "/v1/push/sub-key/%s/devices/%s"
# v2/push/sub-key/{subKey}/devices-apns2/{deviceApns2}
REMOVE_PATH_APNS2 = "/v2/push/sub-key/%s/devices-apns2/%s"
def __init__(self, pubnub):
Endpoint.__init__(self, pubnub)
self._channels = None
self._device_id = None
self._push_type = None
self._topic = None
self._environment = None
def channels(self, channels):
self._channels = channels
return self
def device_id(self, device_id):
self._device_id = device_id
return self
def push_type(self, push_type):
self._push_type = push_type
return self
def topic(self, topic):
self._topic = topic
return self
def environment(self, environment):
self._environment = environment
return self
def custom_params(self):
params = {"remove": utils.join_items(self._channels)}
if self._push_type != PNPushType.APNS2:
params["type"] = utils.push_type_to_string(self._push_type)
else:
if self._environment is None:
self._environment = PNPushEnvironment.DEVELOPMENT
params["environment"] = self._environment
params["topic"] = self._topic
return params
def build_path(self):
if self._push_type != PNPushType.APNS2:
return RemoveChannelsFromPush.REMOVE_PATH % (
self.pubnub.config.subscribe_key,
self._device_id,
)
else:
return RemoveChannelsFromPush.REMOVE_PATH_APNS2 % (
self.pubnub.config.subscribe_key,
self._device_id,
)
def http_method(self):
return HttpMethod.GET
def validate_params(self):
self.validate_subscribe_key()
if not isinstance(self._channels, list) or len(self._channels) == 0:
raise PubNubException(pn_error=PNERR_CHANNEL_MISSING)
if not isinstance(self._device_id, str) or len(self._device_id) == 0:
raise PubNubException(pn_error=PNERR_PUSH_DEVICE_MISSING)
if self._push_type is None:
raise PubNubException(pn_error=PNERROR_PUSH_TYPE_MISSING)
if self._push_type == PNPushType.APNS2:
if not isinstance(self._topic, str) or len(self._topic) == 0:
raise PubNubException(pn_error=PNERR_PUSH_TOPIC_MISSING)
def create_response(self, envelope):
return PNPushRemoveChannelResult()
def is_auth_required(self):
return True
def request_timeout(self):
return self.pubnub.config.non_subscribe_request_timeout
def connect_timeout(self):
return self.pubnub.config.connect_timeout
def operation_type(self):
return PNOperationType.PNRemovePushNotificationsFromChannelsOperation
def name(self):
return "RemoveChannelsFromPush"
bdraco-freenub-69809e8/pubnub/endpoints/push/remove_device.py 0000664 0000000 0000000 00000006056 14645232030 0024337 0 ustar 00root root 0000000 0000000 from pubnub import utils
from pubnub.endpoints.endpoint import Endpoint
from pubnub.enums import HttpMethod, PNOperationType, PNPushEnvironment, PNPushType
from pubnub.errors import (
PNERR_PUSH_DEVICE_MISSING,
PNERR_PUSH_TOPIC_MISSING,
PNERROR_PUSH_TYPE_MISSING,
)
from pubnub.exceptions import PubNubException
from pubnub.models.consumer.push import PNPushRemoveAllChannelsResult
class RemoveDeviceFromPush(Endpoint):
# v1/push/sub-key/{subKey}/devices/{pushToken}/remove
REMOVE_PATH = "/v1/push/sub-key/%s/devices/%s/remove"
# v2/push/sub-key/{subKey}/devices-apns2/{deviceApns2}/remove
REMOVE_PATH_APNS2 = "/v2/push/sub-key/%s/devices-apns2/%s/remove"
def __init__(self, pubnub):
Endpoint.__init__(self, pubnub)
self._device_id = None
self._push_type = None
self._topic = None
self._environment = None
def device_id(self, device_id):
self._device_id = device_id
return self
def push_type(self, push_type):
self._push_type = push_type
return self
def topic(self, topic):
self._topic = topic
return self
def environment(self, environment):
self._environment = environment
return self
def custom_params(self):
params = {}
if self._push_type != PNPushType.APNS2:
params["type"] = utils.push_type_to_string(self._push_type)
else:
if self._environment is None:
self._environment = PNPushEnvironment.DEVELOPMENT
params["environment"] = self._environment
params["topic"] = self._topic
return params
def build_path(self):
if self._push_type != PNPushType.APNS2:
return RemoveDeviceFromPush.REMOVE_PATH % (
self.pubnub.config.subscribe_key,
self._device_id,
)
else:
return RemoveDeviceFromPush.REMOVE_PATH_APNS2 % (
self.pubnub.config.subscribe_key,
self._device_id,
)
def http_method(self):
return HttpMethod.GET
def validate_params(self):
self.validate_subscribe_key()
if not isinstance(self._device_id, str) or len(self._device_id) == 0:
raise PubNubException(pn_error=PNERR_PUSH_DEVICE_MISSING)
if self._push_type is None:
raise PubNubException(pn_error=PNERROR_PUSH_TYPE_MISSING)
if self._push_type == PNPushType.APNS2:
if not isinstance(self._topic, str) or len(self._topic) == 0:
raise PubNubException(pn_error=PNERR_PUSH_TOPIC_MISSING)
def create_response(self, envelope):
return PNPushRemoveAllChannelsResult()
def is_auth_required(self):
return True
def request_timeout(self):
return self.pubnub.config.non_subscribe_request_timeout
def connect_timeout(self):
return self.pubnub.config.connect_timeout
def operation_type(self):
return PNOperationType.PNRemoveAllPushNotificationsOperation
def name(self):
return "RemoveDeviceFromPush"
bdraco-freenub-69809e8/pubnub/endpoints/signal.py 0000664 0000000 0000000 00000003115 14645232030 0022012 0 ustar 00root root 0000000 0000000 from pubnub import utils
from pubnub.endpoints.endpoint import Endpoint
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.models.consumer.signal import PNSignalResult
class Signal(Endpoint):
SIGNAL_PATH = "/signal/%s/%s/0/%s/0/%s"
def __init__(self, pubnub):
Endpoint.__init__(self, pubnub)
self._channel = None
self._message = None
def channel(self, channel):
self._channel = str(channel)
return self
def message(self, message):
self._message = message
return self
def build_path(self):
stringified_message = utils.write_value_as_string(self._message)
msg = utils.url_encode(stringified_message)
return Signal.SIGNAL_PATH % (
self.pubnub.config.publish_key,
self.pubnub.config.subscribe_key,
utils.url_encode(self._channel),
msg,
)
def custom_params(self):
return {}
def http_method(self):
return HttpMethod.GET
def is_auth_required(self):
return True
def validate_params(self):
self.validate_subscribe_key()
self.validate_publish_key()
self.validate_channel()
def create_response(self, result): # pylint: disable=W0221
return PNSignalResult(result)
def request_timeout(self):
return self.pubnub.config.non_subscribe_request_timeout
def connect_timeout(self):
return self.pubnub.config.connect_timeout
def operation_type(self):
return PNOperationType.PNSignalOperation
def name(self):
return "Signal"
bdraco-freenub-69809e8/pubnub/endpoints/time.py 0000664 0000000 0000000 00000001520 14645232030 0021471 0 ustar 00root root 0000000 0000000 from pubnub.endpoints.endpoint import Endpoint
from pubnub.enums import HttpMethod, PNOperationType
from pubnub.models.consumer.time import PNTimeResponse
class Time(Endpoint):
TIME_PATH = "/time/0"
def custom_params(self):
return {}
def build_path(self):
return Time.TIME_PATH
def http_method(self):
return HttpMethod.GET
def validate_params(self):
pass
def is_auth_required(self):
return False
def create_response(self, envelope):
return PNTimeResponse(envelope)
def request_timeout(self):
return self.pubnub.config.non_subscribe_request_timeout
def connect_timeout(self):
return self.pubnub.config.connect_timeout
def operation_type(self):
return PNOperationType.PNTimeOperation
def name(self):
return "Time"
bdraco-freenub-69809e8/pubnub/enums.py 0000664 0000000 0000000 00000010115 14645232030 0017657 0 ustar 00root root 0000000 0000000 from enum import Enum
class HttpMethod:
GET = 1
POST = 2
DELETE = 3
PATCH = 4
@classmethod
def string(cls, method):
if method == cls.GET:
return "GET"
elif method == cls.POST:
return "POST"
elif method == cls.DELETE:
return "DELETE"
elif method == cls.PATCH:
return "PATCH"
class PNStatusCategory:
PNUnknownCategory = 1
PNAcknowledgmentCategory = 2
PNAccessDeniedCategory = 3
PNTimeoutCategory = 4
PNNetworkIssuesCategory = 5
PNConnectedCategory = 6
PNReconnectedCategory = 7
PNDisconnectedCategory = 8
PNUnexpectedDisconnectCategory = 9
PNCancelledCategory = 10
PNBadRequestCategory = 11
PNMalformedFilterExpressionCategory = 12
PNMalformedResponseCategory = 13
PNDecryptionErrorCategory = 14
PNTLSConnectionFailedCategory = 15
PNTLSUntrustedCertificateCategory = 16
PNInternalExceptionCategory = 17
class PNOperationType:
PNSubscribeOperation = 1
PNUnsubscribeOperation = 2
PNPublishOperation = 3
PNHistoryOperation = 4
PNWhereNowOperation = 5
PNHeartbeatOperation = 6
PNSetStateOperation = 7
PNAddChannelsToGroupOperation = 8
PNRemoveChannelsFromGroupOperation = 9
PNChannelGroupsOperation = 10
PNRemoveGroupOperation = 11
PNChannelsForGroupOperation = 12
PNPushNotificationEnabledChannelsOperation = 13
PNAddPushNotificationsOnChannelsOperation = 14
PNRemovePushNotificationsFromChannelsOperation = 15
PNRemoveAllPushNotificationsOperation = 16
PNTimeOperation = 17
PNHereNowOperation = 18
PNGetState = 19
PNAccessManagerAudit = 20
PNAccessManagerGrant = 21
PNAccessManagerRevoke = 22
PNHistoryDeleteOperation = 23
PNMessageCountOperation = 24
PNFireOperation = 25
PNSignalOperation = 26
PNAccessManagerRevokeToken = 40
PNAccessManagerGrantToken = 41
PNAddMessageAction = 42
PNGetMessageActions = 43
PNDeleteMessageAction = 44
PNFetchMessagesOperation = 45
PNGetFilesAction = 46
PNDeleteFileOperation = 47
PNGetFileDownloadURLAction = 48
PNFetchFileUploadS3DataAction = 49
PNDownloadFileAction = 50
PNSendFileAction = 51
PNSendFileNotification = 52
PNSetUuidMetadataOperation = 53
PNGetUuidMetadataOperation = 54
PNRemoveUuidMetadataOperation = 55
PNGetAllUuidMetadataOperation = 56
PNSetChannelMetadataOperation = 57
PNGetChannelMetadataOperation = 58
PNRemoveChannelMetadataOperation = 59
PNGetAllChannelMetadataOperation = 60
PNSetChannelMembersOperation = 61
PNGetChannelMembersOperation = 62
PNRemoveChannelMembersOperation = 63
PNManageChannelMembersOperation = 64
PNSetMembershipsOperation = 65
PNGetMembershipsOperation = 66
PNRemoveMembershipsOperation = 67
PNManageMembershipsOperation = 68
PNCreateSpaceOperation = 69
PNUpdateSpaceOperation = 70
PNFetchSpaceOperation = 71
PNFetchSpacesOperation = 72
PNRemoveSpaceOperation = 73
PNCreateUserOperation = 74
PNUpdateUserOperation = 75
PNFetchUserOperation = 76
PNFetchUsersOperation = 77
PNRemoveUserOperation = 78
PNAddUserSpacesOperation = 79
PNAddSpaceUsersOperation = 80
PNUpdateUserSpacesOperation = 81
PNUpdateSpaceUsersOperation = 82
PNRemoveUserSpacesOperation = 81
PNRemoveSpaceUsersOperation = 82
PNFetchUserMembershipsOperation = 85
PNFetchSpaceMembershipsOperation = 86
class PNHeartbeatNotificationOptions:
NONE = 1
FAILURES = 2
ALL = 3
class PNReconnectionPolicy:
NONE = 1
LINEAR = 2
EXPONENTIAL = 3
class PNPushType:
APNS = 1
MPNS = 2
GCM = 3
APNS2 = 4
class PNResourceType:
CHANNEL = "channel"
GROUP = "group"
USER = "user"
SPACE = "space"
class PNMatchType:
RESOURCE = "resource"
PATTERN = "pattern"
class PNPushEnvironment:
DEVELOPMENT = "development"
PRODUCTION = "production"
class PAMPermissions(Enum):
READ = 1
WRITE = 2
MANAGE = 4
DELETE = 8
CREATE = 16
GET = 32
UPDATE = 64
JOIN = 128
bdraco-freenub-69809e8/pubnub/errors.py 0000664 0000000 0000000 00000006227 14645232030 0020055 0 ustar 00root root 0000000 0000000 PNERR_CLIENT_TIMEOUT = "Client Timeout"
PNERR__TIMEOUT = "Timeout Occurred"
PNERR_REQUEST_CANCELLED = "HTTP Client Error"
# TODO: clarify to not confuse with 4xx and 5xx http erros
PNERR_HTTP_ERROR = "HTTP Error"
PNERR_CONNECTION_ERROR = "Connection Error"
PNERR_TOO_MANY_REDIRECTS_ERROR = "Too many redirects"
# For 5xx server responses
PNERR_SERVER_ERROR = "HTTP Server Error"
# For 4xx server responses
PNERR_CLIENT_ERROR = "HTTP Client Error"
PNERR_UNKNOWN_ERROR = "Unknown Error"
PNERR_CHANNEL_MISSING = "Channel missing"
PNERR_CHANNELS_MISSING = "Channels missing"
PNERR_GROUP_MISSING = "Channel group missing"
PNERR_MESSAGE_MISSING = "Message missing"
PNERR_SUBSCRIBE_KEY_MISSING = "Subscribe key not configured"
PNERR_SECRET_KEY_MISSING = "Secret key is not configured"
PNERR_PUBLISH_KEY_MISSING = "Publish key not configured"
PNERR_PUBLISH_META_WRONG_TYPE = "Publish meta should be dict"
PNERR_DEFERRED_NOT_IMPLEMENTED = (
"Deferred endpoint call is not implemented by this platform"
)
PNERR_JSON_DECODING_FAILED = "JSON decoding failed"
PNERR_JSON_NOT_SERIALIZABLE = "Trying to publish not JSON serializable object"
PNERR_CHANNEL_OR_GROUP_MISSING = "Channel or group missing"
PNERR_STATE_MISSING = "State missing or not a dict"
PNERR_UUID_MISSING = "uuid missing or not a string"
PNERR_STATE_SETTER_FOR_GROUPS_NOT_SUPPORTED_YET = (
"State setter for channel groups is not supported yet"
)
PNERR_PUSH_DEVICE_MISSING = "Device ID is missing for push operation"
PNERROR_PUSH_TYPE_MISSING = "Push Type is missing"
PNERR_PAM_NO_FLAGS = "At least one flag should be specified"
PNERR_PAM_INVALID_ARGUMENTS = "Invalid arguments"
PNERR_RESOURCES_MISSING = "Resources missing"
PNERR_TTL_MISSING = "TTL missing"
PNERR_INVALID_META = "Invalid meta parameter"
PNERR_INVALID_ACCESS_TOKEN = "Invalid access token"
PNERR_MESSAGE_ACTION_MISSING = "Message action is missing"
PNERR_MESSAGE_ACTION_TYPE_MISSING = "Message action type is missing"
PNERR_MESSAGE_ACTION_VALUE_MISSING = "Message action value is missing"
PNERR_MESSAGE_TIMETOKEN_MISSING = "Message timetoken is missing"
PNERR_MESSAGE_ACTION_TIMETOKEN_MISSING = "Message action timetoken is missing"
PNERR_HISTORY_MESSAGE_ACTIONS_MULTIPLE_CHANNELS = (
"History can return message action data for a single channel only. "
"Either pass a single channel or disable the include_message_action"
"s flag. "
)
PNERR_PUSH_TOPIC_MISSING = (
"Push notification topic is missing. Required only if push type is APNS2."
)
PNERR_FILE_OBJECT_MISSING = "File object is missing."
PNERR_FILE_NAME_MISSING = "File name is missing."
PNERR_FILE_ID_MISSING = "File id is missing."
PNERR_SPACE_MISSING = "Space missing"
PNERR_SPACES_MISSING = "Spaces missing"
PNERR_USER_ID_MISSING = "user_id missing or not a string"
PNERR_USER_SPACE_PAIRS_MISSING = "User/Space pair is missing"
PNERR_MISUSE_OF_USER_AND_USERS = "user_id and users should not be used together"
PNERR_MISUSE_OF_SPACE_AND_SPACES = "space_id and spaces should not be used together"
PNERR_MISUSE_OF_USER_AND_SPACE = "user_id and space_id should not be used together"
PNERR_INVALID_USER = "Provided user is not valid instance of User"
PNERR_INVALID_SPACE = "Provided space is not valid instance of Space"
bdraco-freenub-69809e8/pubnub/event_engine/ 0000775 0000000 0000000 00000000000 14645232030 0020626 5 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/pubnub/event_engine/__init__.py 0000664 0000000 0000000 00000000000 14645232030 0022725 0 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/pubnub/event_engine/dispatcher.py 0000664 0000000 0000000 00000003132 14645232030 0023325 0 ustar 00root root 0000000 0000000 from pubnub.event_engine import manage_effects
from pubnub.event_engine.models import effects
class Dispatcher:
_pubnub = None
_managed_effects_factory = None
def __init__(self, event_engine) -> None:
self._event_engine = event_engine
self._managed_effects = {}
self._effect_emitter = manage_effects.EmitEffect()
def set_pn(self, pubnub_instance):
self._pubnub = pubnub_instance
self._effect_emitter.set_pn(pubnub_instance)
def dispatch_effect(self, effect: effects.PNEffect):
if not self._managed_effects_factory:
self._managed_effects_factory = manage_effects.ManagedEffectFactory(
self._pubnub, self._event_engine
)
if isinstance(effect, effects.PNEmittableEffect):
self.emit_effect(effect)
elif isinstance(effect, effects.PNManageableEffect):
self.dispatch_managed_effect(effect)
elif isinstance(effect, effects.PNCancelEffect):
self.dispatch_cancel_effect(effect)
def emit_effect(self, effect: effects.PNEffect):
self._effect_emitter.emit(effect)
def dispatch_managed_effect(self, effect: effects.PNEffect):
managed_effect = self._managed_effects_factory.create(effect)
managed_effect.run()
self._managed_effects[effect.__class__.__name__] = managed_effect
def dispatch_cancel_effect(self, effect: effects.PNEffect):
if effect.cancel_effect in self._managed_effects:
self._managed_effects[effect.cancel_effect].stop()
del self._managed_effects[effect.cancel_effect]
bdraco-freenub-69809e8/pubnub/event_engine/manage_effects.py 0000664 0000000 0000000 00000027310 14645232030 0024132 0 ustar 00root root 0000000 0000000 import asyncio
import logging
import math
from queue import SimpleQueue
from typing import Union
from pubnub.endpoints.pubsub.subscribe import Subscribe
from pubnub.enums import PNReconnectionPolicy
from pubnub.event_engine.models import effects, events
from pubnub.models.consumer.common import PNStatus
from pubnub.models.consumer.pubsub import PNMessageResult
from pubnub.models.server.subscribe import SubscribeMessage
from pubnub.pubnub import PubNub
class ManagedEffect:
pubnub: PubNub = None
event_engine = None
effect: Union[effects.PNManageableEffect, effects.PNCancelEffect]
stop_event = None
def set_pn(self, pubnub: PubNub):
self.pubnub = pubnub
def __init__(
self,
pubnub_instance,
event_engine_instance,
effect: Union[effects.PNManageableEffect, effects.PNCancelEffect],
) -> None:
self.effect = effect
self.event_engine = event_engine_instance
self.pubnub = pubnub_instance
def run(self):
pass
def run_async(self):
pass
def stop(self):
logging.debug(f"stop called on {self.__class__.__name__}")
if self.stop_event:
logging.debug(
f"stop_event({id(self.stop_event)}).set() called on {self.__class__.__name__}"
)
self.stop_event.set()
def get_new_stop_event(self):
event = asyncio.Event()
logging.debug(
f"creating new stop_event({id(event)}) for {self.__class__.__name__}"
)
return event
class ManageHandshakeEffect(ManagedEffect):
def run(self):
channels = self.effect.channels
groups = self.effect.groups
if hasattr(self.pubnub, "event_loop"):
self.stop_event = self.get_new_stop_event()
loop: asyncio.AbstractEventLoop = self.pubnub.event_loop
if loop.is_running():
loop.create_task(
self.handshake_async(channels, groups, self.stop_event)
)
else:
loop.run_until_complete(
self.handshake_async(channels, groups, self.stop_event)
)
else:
# TODO: the synchronous way
pass
async def handshake_async(self, channels, groups, stop_event):
request = (
Subscribe(self.pubnub)
.channels(channels)
.channel_groups(groups)
.cancellation_event(stop_event)
)
handshake = await request.future()
if handshake.status.error:
logging.warning(f"Handshake failed: {handshake.status.error_data.__dict__}")
handshake_failure = events.HandshakeFailureEvent(
handshake.status.error_data, 1
)
self.event_engine.trigger(handshake_failure)
else:
cursor = handshake.result["t"]
timetoken = cursor["t"]
region = cursor["r"]
handshake_success = events.HandshakeSuccessEvent(timetoken, region)
self.event_engine.trigger(handshake_success)
class ManagedReceiveMessagesEffect(ManagedEffect):
effect: effects.ReceiveMessagesEffect
def run(self):
channels = self.effect.channels
groups = self.effect.groups
timetoken = self.effect.timetoken
region = self.effect.region
if hasattr(self.pubnub, "event_loop"):
self.stop_event = self.get_new_stop_event()
loop: asyncio.AbstractEventLoop = self.pubnub.event_loop
if loop.is_running():
loop.create_task(
self.receive_messages_async(channels, groups, timetoken, region)
)
else:
loop.run_until_complete(
self.receive_messages_async(channels, groups, timetoken, region)
)
else:
# TODO: the synchronous way
pass
async def receive_messages_async(self, channels, groups, timetoken, region):
subscribe = Subscribe(self.pubnub)
if channels:
subscribe.channels(channels)
if groups:
subscribe.channel_groups(groups)
if timetoken:
subscribe.timetoken(timetoken)
if region:
subscribe.region(region)
subscribe.cancellation_event(self.stop_event)
response = await subscribe.future()
if response and response.result:
if not response.status.error:
cursor = response.result["t"]
timetoken = cursor["t"]
region = cursor["r"]
messages = response.result["m"]
recieve_success = events.ReceiveSuccessEvent(
timetoken, region=region, messages=messages
)
self.event_engine.trigger(recieve_success)
self.stop_event.set()
class ManagedReconnectEffect(ManagedEffect):
effect: effects.ReconnectEffect
reconnection_policy: PNReconnectionPolicy
give_up_event: events.PNFailureEvent
failure_event: events.PNFailureEvent
success_event: events.PNCursorEvent
def __init__(
self,
pubnub_instance,
event_engine_instance,
effect: Union[effects.PNManageableEffect, effects.PNCancelEffect],
) -> None:
super().__init__(pubnub_instance, event_engine_instance, effect)
self.reconnection_policy = pubnub_instance.config.reconnect_policy
self.interval = pubnub_instance.config.RECONNECTION_INTERVAL
self.min_backoff = pubnub_instance.config.RECONNECTION_MIN_EXPONENTIAL_BACKOFF
self.max_backoff = pubnub_instance.config.RECONNECTION_MAX_EXPONENTIAL_BACKOFF
def calculate_reconnection_delay(self, attempt):
if not attempt:
attempt = 1
if self.reconnection_policy is PNReconnectionPolicy.LINEAR:
delay = self.interval
elif self.reconnection_policy is PNReconnectionPolicy.EXPONENTIAL:
delay = int(math.pow(2, attempt - 5 * math.floor((attempt - 1) / 5)) - 1)
return delay
def run(self):
if self.reconnection_policy is PNReconnectionPolicy.NONE:
self.event_engine.trigger(
self.give_up_event(
reason=self.effect.reason, attempt=self.effect.attempts
)
)
else:
attempt = self.effect.attempts
delay = self.calculate_reconnection_delay(attempt)
logging.warning(f"will reconnect in {delay}s")
if hasattr(self.pubnub, "event_loop"):
loop: asyncio.AbstractEventLoop = self.pubnub.event_loop
if loop.is_running():
self.delayed_reconnect_coro = loop.create_task(
self.delayed_reconnect_async(delay, attempt)
)
else:
self.delayed_reconnect_coro = loop.run_until_complete(
self.delayed_reconnect_async(delay, attempt)
)
else:
# TODO: the synchronous way
pass
async def delayed_reconnect_async(self, delay, attempt):
self.stop_event = self.get_new_stop_event()
await asyncio.sleep(delay)
request = (
Subscribe(self.pubnub)
.channels(self.effect.channels)
.channel_groups(self.effect.groups)
.cancellation_event(self.stop_event)
)
if self.effect.timetoken:
request.timetoken(self.effect.timetoken)
if self.effect.region:
request.region(self.effect.region)
reconnect = await request.future()
if reconnect.status.error:
logging.warning(f"Reconnect failed: {reconnect.status.error_data.__dict__}")
reconnect_failure = self.failure_event(reconnect.status.error_data, attempt)
self.event_engine.trigger(reconnect_failure)
else:
cursor = reconnect.result["t"]
timetoken = cursor["t"]
region = cursor["r"]
reconnect_success = self.success_event(timetoken, region)
self.event_engine.trigger(reconnect_success)
def stop(self):
logging.debug(f"stop called on {self.__class__.__name__}")
if self.stop_event:
logging.debug(
f"stop_event({id(self.stop_event)}).set() called on {self.__class__.__name__}"
)
self.stop_event.set()
if self.delayed_reconnect_coro:
try:
self.delayed_reconnect_coro.cancel()
except asyncio.exceptions.CancelledError:
pass
class ManagedHandshakeReconnectEffect(ManagedReconnectEffect):
def __init__(
self,
pubnub_instance,
event_engine_instance,
effect: Union[effects.PNManageableEffect, effects.PNCancelEffect],
) -> None:
self.give_up_event = events.HandshakeReconnectGiveupEvent
self.failure_event = events.HandshakeReconnectFailureEvent
self.success_event = events.HandshakeReconnectSuccessEvent
super().__init__(pubnub_instance, event_engine_instance, effect)
class ManagedReceiveReconnectEffect(ManagedReconnectEffect):
def __init__(
self,
pubnub_instance,
event_engine_instance,
effect: Union[effects.PNManageableEffect, effects.PNCancelEffect],
) -> None:
self.give_up_event = events.HandshakeReconnectGiveupEvent
self.failure_event = events.HandshakeReconnectFailureEvent
self.success_event = events.HandshakeReconnectSuccessEvent
super().__init__(pubnub_instance, event_engine_instance, effect)
class ManagedEffectFactory:
_managed_effects = {
effects.HandshakeEffect.__name__: ManageHandshakeEffect,
effects.ReceiveMessagesEffect.__name__: ManagedReceiveMessagesEffect,
effects.HandshakeReconnectEffect.__name__: ManagedHandshakeReconnectEffect,
}
def __init__(self, pubnub_instance, event_engine_instance) -> None:
self._pubnub = pubnub_instance
self._event_engine = event_engine_instance
def create(self, effect: ManagedEffect):
if effect.__class__.__name__ not in self._managed_effects:
# TODO replace below with raise unsupported managed effect exception
return ManagedEffect(self._pubnub, self._event_engine, effect)
return self._managed_effects[effect.__class__.__name__](
self._pubnub, self._event_engine, effect
)
class EmitEffect:
pubnub: PubNub
def set_pn(self, pubnub: PubNub):
self.pubnub = pubnub
self.queue = SimpleQueue
def emit(self, effect: effects.PNEmittableEffect):
if isinstance(effect, effects.EmitMessagesEffect):
self.emit_message(effect)
if isinstance(effect, effects.EmitStatusEffect):
self.emit_status(effect)
def emit_message(self, effect: effects.EmitMessagesEffect):
for message in effect.messages:
subscribe_message = SubscribeMessage().from_json(message)
pn_message_result = PNMessageResult(
message=subscribe_message.payload,
subscription=subscribe_message.subscription_match,
channel=subscribe_message.channel,
timetoken=int(message["p"]["t"]),
user_metadata=subscribe_message.publish_metadata,
publisher=subscribe_message.issuing_client_id,
)
self.pubnub._subscription_manager._listener_manager.announce_message(
pn_message_result
)
def emit_status(self, effect: effects.EmitStatusEffect):
pn_status = PNStatus()
pn_status.category = effect.status
pn_status.error = False
self.pubnub._subscription_manager._listener_manager.announce_status(pn_status)
bdraco-freenub-69809e8/pubnub/event_engine/models/ 0000775 0000000 0000000 00000000000 14645232030 0022111 5 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/pubnub/event_engine/models/__init__.py 0000664 0000000 0000000 00000000000 14645232030 0024210 0 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/pubnub/event_engine/models/effects.py 0000664 0000000 0000000 00000004600 14645232030 0024102 0 ustar 00root root 0000000 0000000 from typing import Union
from pubnub.enums import PNStatusCategory
from pubnub.exceptions import PubNubException
class PNEffect:
pass
class PNManageableEffect(PNEffect):
pass
class PNCancelEffect(PNEffect):
cancel_effect: str
class HandshakeEffect(PNManageableEffect):
def __init__(
self,
channels: Union[None, list[str]] = None,
groups: Union[None, list[str]] = None,
) -> None:
super().__init__()
self.channels = channels
self.groups = groups
class CancelHandshakeEffect(PNCancelEffect):
cancel_effect = HandshakeEffect.__name__
class ReceiveMessagesEffect(PNManageableEffect):
def __init__(
self,
channels: Union[None, list[str]] = None,
groups: Union[None, list[str]] = None,
timetoken: Union[None, str] = None,
region: Union[None, int] = None,
) -> None:
super().__init__()
self.channels = channels
self.groups = groups
self.timetoken = timetoken
self.region = region
class CancelReceiveMessagesEffect(PNCancelEffect):
cancel_effect = ReceiveMessagesEffect.__name__
class ReconnectEffect(PNManageableEffect):
def __init__(
self,
channels: Union[None, list[str]] = None,
groups: Union[None, list[str]] = None,
timetoken: Union[None, str] = None,
region: Union[None, int] = None,
attempts: Union[None, int] = None,
reason: Union[None, PubNubException] = None,
) -> None:
self.channels = channels
self.groups = groups
self.attempts = attempts
self.reason = reason
self.timetoken = timetoken
self.region = region
class HandshakeReconnectEffect(ReconnectEffect):
pass
class CancelHandshakeReconnectEffect(PNCancelEffect):
cancel_effect = HandshakeReconnectEffect.__name__
class ReceiveReconnectEffect(ReconnectEffect):
pass
class CancelReceiveReconnectEffect(PNCancelEffect):
cancel_effect = ReceiveReconnectEffect.__name__
class PNEmittableEffect(PNEffect):
pass
class EmitMessagesEffect(PNEmittableEffect):
def __init__(self, messages: Union[None, list[str]]) -> None:
super().__init__()
self.messages = messages
class EmitStatusEffect(PNEmittableEffect):
def __init__(self, status: Union[None, PNStatusCategory]) -> None:
super().__init__()
self.status = status
bdraco-freenub-69809e8/pubnub/event_engine/models/events.py 0000664 0000000 0000000 00000004737 14645232030 0024002 0 ustar 00root root 0000000 0000000 from typing import Optional
from pubnub.exceptions import PubNubException
class PNEvent:
def get_name(self) -> str:
return self.__class__.__name__
class PNFailureEvent(PNEvent):
def __init__(self, reason: PubNubException, attempt: int) -> None:
self.reason = reason
self.attempt = attempt
super().__init__()
class PNCursorEvent(PNEvent):
def __init__(self, timetoken: str, region: Optional[int] = None) -> None:
self.timetoken = timetoken
self.region = region
class PNChannelGroupsEvent(PNEvent):
def __init__(self, channels: list[str], groups: list[str]) -> None:
self.channels = channels
self.groups = groups
class SubscriptionChangedEvent(PNChannelGroupsEvent):
def __init__(self, channels: list[str], groups: list[str]) -> None:
PNChannelGroupsEvent.__init__(self, channels, groups)
class SubscriptionRestoredEvent(PNCursorEvent, PNChannelGroupsEvent):
def __init__(
self,
timetoken: str,
channels: list[str],
groups: list[str],
region: Optional[int] = None,
) -> None:
PNCursorEvent.__init__(self, timetoken, region)
PNChannelGroupsEvent.__init__(self, channels, groups)
class HandshakeSuccessEvent(PNCursorEvent):
def __init__(self, timetoken: str, region: Optional[int] = None) -> None:
super().__init__(timetoken, region)
class HandshakeFailureEvent(PNFailureEvent):
pass
class HandshakeReconnectSuccessEvent(PNCursorEvent):
pass
class HandshakeReconnectFailureEvent(PNFailureEvent):
pass
class HandshakeReconnectGiveupEvent(PNFailureEvent):
pass
class HandshakeReconnectRetryEvent(PNEvent):
pass
class ReceiveSuccessEvent(PNCursorEvent):
def __init__(
self, timetoken: str, messages: list, region: Optional[int] = None
) -> None:
PNCursorEvent.__init__(self, timetoken, region)
self.messages = messages
class ReceiveFailureEvent(PNFailureEvent):
pass
class ReceiveReconnectSuccessEvent(PNCursorEvent):
def __init__(
self, timetoken: str, messages: list, region: Optional[int] = None
) -> None:
PNCursorEvent.__init__(self, timetoken, region)
self.messages = messages
class ReceiveReconnectFailureEvent(PNFailureEvent):
pass
class ReceiveReconnectGiveupEvent(PNFailureEvent):
pass
class ReceiveReconnectRetryEvent(PNEvent):
pass
class DisconnectEvent(PNEvent):
pass
class ReconnectEvent(PNEvent):
pass
bdraco-freenub-69809e8/pubnub/event_engine/models/states.py 0000664 0000000 0000000 00000045332 14645232030 0023775 0 ustar 00root root 0000000 0000000 from typing import Union
from pubnub.enums import PNStatusCategory
from pubnub.event_engine.models import effects, events
from pubnub.event_engine.models.effects import PNEffect
from pubnub.exceptions import PubNubException
class PNContext(dict):
channels: list
groups: list
region: int
timetoken: str
attempt: int
reason: PubNubException
def update(self, context):
super().update(context.__dict__)
class PNState:
_context: PNContext
def __init__(self, context: PNContext) -> None:
self._context = context
self._transitions = dict
def on(self, event: events.PNEvent, context: PNContext):
return self._transitions[event.get_name()](event, context)
def on_enter(self, context: Union[None, PNContext]):
pass
def on_exit(self):
pass
def get_context(self) -> PNContext:
return self._context
class PNTransition:
context: PNContext
state: PNState
effect: Union[None, list[PNEffect]]
def __init__(
self,
state: PNState,
context: Union[None, PNContext] = None,
effect: Union[None, list[PNEffect]] = None,
) -> None:
self.context = context
self.state = state
self.effect = effect
class UnsubscribedState(PNState):
def __init__(self, context: PNContext) -> None:
super().__init__(context)
self._context.attempt = 0
self._transitions = {
events.SubscriptionChangedEvent.__name__: self.subscription_changed,
events.SubscriptionRestoredEvent.__name__: self.subscription_restored,
}
def subscription_changed(
self, event: events.SubscriptionChangedEvent, context: PNContext
) -> PNTransition:
self._context.update(context)
self._context.channels = event.channels
self._context.groups = event.groups
self._context.timetoken = 0
return PNTransition(state=HandshakingState, context=self._context)
def subscription_restored(
self, event: events.SubscriptionRestoredEvent, context: PNContext
) -> PNTransition:
self._context.update(context)
self._context.channels = event.channels
self._context.groups = event.groups
self._context.timetoken = event.timetoken
self._context.region = event.region
return PNTransition(state=ReceivingState, context=self._context)
class HandshakingState(PNState):
def __init__(self, context: PNContext) -> None:
super().__init__(context)
self._transitions = {
events.HandshakeFailureEvent.__name__: self.reconnecting,
events.DisconnectEvent.__name__: self.disconnect,
events.HandshakeSuccessEvent.__name__: self.handshaking_success,
events.SubscriptionRestoredEvent.__name__: self.subscription_restored,
events.SubscriptionChangedEvent.__name__: self.subscription_changed,
}
def on_enter(self, context: Union[None, PNContext]):
self._context.update(context)
super().on_enter(self._context)
return effects.HandshakeEffect(self._context.channels, self._context.groups)
def on_exit(self):
super().on_exit()
return effects.CancelHandshakeEffect()
def subscription_changed(
self, event: events.SubscriptionChangedEvent, context: PNContext
) -> PNTransition:
self._context.update(context)
self._context.channels = event.channels
self._context.groups = event.groups
self._context.timetoken = 0
return PNTransition(state=HandshakingState, context=self._context)
def subscription_restored(
self, event: events.SubscriptionRestoredEvent, context: PNContext
) -> PNTransition:
self._context.update(context)
self._context.channels = event.channels
self._context.groups = event.groups
self._context.timetoken = event.timetoken
self._context.region = event.region
return PNTransition(state=ReceivingState, context=self._context)
def reconnecting(
self, event: events.HandshakeFailureEvent, context: PNContext
) -> PNTransition:
self._context.update(context)
self._context.attempt = event.attempt
self._context.reason = event.reason
return PNTransition(state=HandshakeReconnectingState, context=self._context)
def disconnect(
self, event: events.DisconnectEvent, context: PNContext
) -> PNTransition:
self._context.update(context)
self._context.timetoken = 0
return PNTransition(state=HandshakeStoppedState, context=self._context)
def handshaking_success(
self, event: events.HandshakeSuccessEvent, context: PNContext
) -> PNTransition:
self._context.update(context)
self._context.timetoken = event.timetoken
self._context.region = event.region
return PNTransition(
state=ReceivingState,
context=self._context,
effect=effects.EmitStatusEffect(PNStatusCategory.PNConnectedCategory),
)
class HandshakeReconnectingState(PNState):
def __init__(self, context: PNContext) -> None:
super().__init__(context)
self._transitions = {
events.DisconnectEvent.__name__: self.disconnect,
events.HandshakeReconnectGiveupEvent.__name__: self.give_up,
events.HandshakeReconnectSuccessEvent.__name__: self.success,
events.SubscriptionRestoredEvent.__name__: self.subscription_restored,
events.HandshakeReconnectFailureEvent.__name__: self.handshake_reconnect,
events.SubscriptionChangedEvent.__name__: self.subscription_changed,
}
def on_enter(self, context: Union[None, PNContext]):
self._context.update(context)
super().on_enter(self._context)
return effects.HandshakeReconnectEffect(
self._context.channels,
self._context.groups,
attempts=self._context.attempt,
reason=self._context.reason,
)
def on_exit(self):
super().on_exit()
return effects.CancelHandshakeReconnectEffect()
def disconnect(
self, event: events.DisconnectEvent, context: PNContext
) -> PNTransition:
self._context.update(context)
return PNTransition(state=HandshakeStoppedState, context=self._context)
def subscription_changed(
self, event: events.SubscriptionChangedEvent, context: PNContext
) -> PNTransition:
self._context.update(context)
self._context.channels = event.channels
self._context.groups = event.groups
self._context.timetoken = 0
return PNTransition(state=HandshakeReconnectingState, context=self._context)
def handshake_reconnect(
self, event: events.HandshakeReconnectFailureEvent, context: PNContext
) -> PNTransition:
self._context.update(context)
self._context.attempt = event.attempt + 1
self._context.reason = event.reason
return PNTransition(state=HandshakeReconnectingState, context=self._context)
def give_up(
self, event: events.HandshakeReconnectGiveupEvent, context: PNContext
) -> PNTransition:
self._context.update(context)
self._context.attempt = event.attempt
self._context.reason = event.reason
return PNTransition(state=HandshakeFailedState, context=self._context)
def subscription_restored(
self, event: events.SubscriptionRestoredEvent, context: PNContext
) -> PNTransition:
self._context.update(context)
self._context.channels = event.channels
self._context.groups = event.groups
self._context.timetoken = event.timetoken
self._context.region = event.region
return PNTransition(state=ReceivingState, context=self._context)
def success(
self, event: events.HandshakeReconnectSuccessEvent, context: PNContext
) -> PNTransition:
self._context.update(context)
self._context.timetoken = event.timetoken
self._context.region = event.region
return PNTransition(
state=ReceivingState,
context=self._context,
effect=effects.EmitStatusEffect(
PNStatusCategory.PNConnectedCategory,
),
)
class HandshakeFailedState(PNState):
def __init__(self, context: PNContext) -> None:
super().__init__(context)
self._transitions = {
events.SubscriptionChangedEvent.__name__: self.subscription_changed,
events.ReconnectEvent.__name__: self.reconnect,
events.SubscriptionRestoredEvent.__name__: self.subscription_restored,
}
def subscription_changed(
self, event: events.SubscriptionChangedEvent, context: PNContext
) -> PNTransition:
self._context.update(context)
self._context.channels = event.channels
self._context.groups = event.groups
self._context.timetoken = 0
return PNTransition(state=HandshakingState, context=self._context)
def reconnect(
self, event: events.ReconnectEvent, context: PNContext
) -> PNTransition:
self._context.update(context)
return PNTransition(state=HandshakingState, context=self._context)
def subscription_restored(
self, event: events.SubscriptionRestoredEvent, context: PNContext
) -> PNTransition:
self._context.update(context)
self._context.channels = event.channels
self._context.groups = event.groups
self._context.timetoken = event.timetoken
self._context.region = event.region
return PNTransition(state=ReceivingState, context=self._context)
class HandshakeStoppedState(PNState):
def __init__(self, context: PNContext) -> None:
super().__init__(context)
self._context.attempt = 0
self._transitions = {events.ReconnectEvent.__name__: self.reconnect}
def reconnect(
self, event: events.ReconnectEvent, context: PNContext
) -> PNTransition:
self._context.update(context)
return PNTransition(state=HandshakeReconnectingState, context=self._context)
class ReceivingState(PNState):
def __init__(self, context: PNContext) -> None:
super().__init__(context)
self._context.attempt = 0
self._transitions = {
events.SubscriptionChangedEvent.__name__: self.subscription_changed,
events.SubscriptionRestoredEvent.__name__: self.subscription_restored,
events.ReceiveSuccessEvent.__name__: self.receiving_success,
events.ReceiveFailureEvent.__name__: self.receiving_failure,
events.DisconnectEvent.__name__: self.disconnect,
events.ReconnectEvent.__name__: self.reconnect,
}
def on_enter(self, context: Union[None, PNContext]):
super().on_enter(context)
return effects.ReceiveMessagesEffect(
context.channels,
context.groups,
timetoken=self._context.timetoken,
region=self._context.region,
)
def on_exit(self):
super().on_exit()
return effects.CancelReceiveMessagesEffect()
def subscription_changed(
self, event: events.SubscriptionChangedEvent, context: PNContext
) -> PNTransition:
self._context.update(context)
self._context.channels = event.channels
self._context.groups = event.groups
self._context.timetoken = 0
return PNTransition(state=self.__class__, context=self._context)
def subscription_restored(
self, event: events.SubscriptionRestoredEvent, context: PNContext
) -> PNTransition:
self._context.update(context)
self._context.channels = event.channels
self._context.groups = event.groups
self._context.timetoken = event.timetoken
self._context.region = event.region
return PNTransition(state=self.__class__, context=self._context)
def receiving_success(
self, event: events.ReceiveSuccessEvent, context: PNContext
) -> PNTransition:
self._context.update(context)
self._context.timetoken = event.timetoken
self._context.region = event.region
return PNTransition(
state=self.__class__,
context=self._context,
effect=[
effects.EmitMessagesEffect(messages=event.messages),
effects.EmitStatusEffect(PNStatusCategory.PNConnectedCategory),
],
)
def receiving_failure(
self, event: events.ReceiveFailureEvent, context: PNContext
) -> PNTransition:
self._context.update(context)
self._context.reason = event.reason
return PNTransition(state=ReceiveReconnectingState, context=self._context)
def disconnect(
self, event: events.DisconnectEvent, context: PNContext
) -> PNTransition:
self._context.update(context)
return PNTransition(
state=ReceiveStoppedState,
context=self._context,
effect=effects.EmitStatusEffect(PNStatusCategory.PNDisconnectedCategory),
)
def reconnect(
self, event: events.ReconnectEvent, context: PNContext
) -> PNTransition:
self._context.update(context)
return PNTransition(state=ReceivingState, context=self._context)
class ReceiveReconnectingState(PNState):
def __init__(self, context: PNContext) -> None:
super().__init__(context)
self._transitions = {
events.ReceiveReconnectFailureEvent.__name__: self.reconnect_failure,
events.SubscriptionChangedEvent.__name__: self.subscription_changed,
events.DisconnectEvent.__name__: self.disconnect,
events.ReceiveReconnectGiveupEvent.__name__: self.give_up,
events.ReceiveReconnectSuccessEvent.__name__: self.reconnect_success,
events.SubscriptionRestoredEvent.__name__: self.subscription_restored,
}
def on_enter(self, context: Union[None, PNContext]):
self._context.update(context)
super().on_enter(self._context)
return effects.ReceiveReconnectEffect(
self._context.channels,
self._context.groups,
self._context.timetoken,
self._context.region,
self._context.attempt,
self._context.reason,
)
def on_exit(self):
super().on_exit()
return effects.CancelReceiveReconnectEffect()
def reconnect_failure(
self, event: events.ReceiveReconnectFailureEvent, context: PNContext
) -> PNTransition:
self._context.update(context)
self._context.attempt = event.attempt + 1
self._context.reason = event.reason
return PNTransition(state=ReceiveReconnectingState, context=self._context)
def subscription_changed(
self, event: events.SubscriptionChangedEvent, context: PNContext
) -> PNTransition:
self._context.update(context)
self._context.channels = event.channels
self._context.groups = event.groups
self._context.timetoken = 0
return PNTransition(state=ReceiveReconnectingState, context=self._context)
def disconnect(
self, event: events.DisconnectEvent, context: PNContext
) -> PNTransition:
self._context.update(context)
return PNTransition(state=ReceiveStoppedState, context=self._context)
def give_up(
self, event: events.ReceiveReconnectGiveupEvent, context: PNContext
) -> PNTransition:
self._context.update(context)
self._context.reason = event.reason
self._context.attempt = event.attempt
return PNTransition(
state=ReceiveFailedState,
context=self._context,
effect=effects.EmitStatusEffect(PNStatusCategory.PNDisconnectedCategory),
)
def reconnect_success(
self, event: events.ReceiveReconnectSuccessEvent, context: PNContext
) -> PNTransition:
self._context.update(context)
self._context.timetoken = event.timetoken
self._context.region = event.region
return PNTransition(
state=ReceivingState,
context=self._context,
effect=[
effects.EmitMessagesEffect(event.messages),
effects.EmitStatusEffect(PNStatusCategory.PNConnectedCategory),
],
)
def subscription_restored(
self, event: events.SubscriptionRestoredEvent, context: PNContext
) -> PNTransition:
self._context.update(context)
self._context.channels = event.channels
self._context.groups = event.groups
self._context.timetoken = event.timetoken
self._context.region = event.region
return PNTransition(state=ReceiveReconnectingState, context=self._context)
class ReceiveFailedState(PNState):
def __init__(self, context: PNContext) -> None:
super().__init__(context)
self._transitions = {
events.ReceiveReconnectRetryEvent.__name__: self.reconnect_retry,
events.SubscriptionChangedEvent.__name__: self.subscription_changed,
events.SubscriptionRestoredEvent.__name__: self.subscription_restored,
events.ReconnectEvent.__name__: self.reconnect,
}
def reconnect_retry(
self, event: events.ReceiveReconnectRetryEvent, context: PNContext
) -> PNTransition:
self._context.update(context)
return PNTransition(state=ReceiveReconnectingState, context=self._context)
def subscription_changed(
self, event: events.SubscriptionChangedEvent, context: PNContext
) -> PNTransition:
self._context.update(context)
self._context.channels = event.channels
self._context.groups = event.groups
self._context.timetoken = 0
return PNTransition(state=ReceivingState, context=self._context)
def reconnect(
self, event: events.ReconnectEvent, context: PNContext
) -> PNTransition:
self._context.update(context)
return PNTransition(state=ReceivingState, context=self._context)
def subscription_restored(
self, event: events.SubscriptionRestoredEvent, context: PNContext
) -> PNTransition:
self._context.update(context)
self._context.channels = event.channels
self._context.groups = event.groups
self._context.timetoken = event.timetoken
self._context.region = event.region
return PNTransition(state=ReceivingState, context=self._context)
class ReceiveStoppedState(PNState):
def __init__(self, context: PNContext) -> None:
super().__init__(context)
self._context.attempt = 0
self._transitions = {events.ReconnectEvent.__name__: self.reconnect}
def reconnect(
self, event: events.ReconnectEvent, context: PNContext
) -> PNTransition:
self._context.update(context)
return PNTransition(state=ReceiveReconnectingState, context=self._context)
bdraco-freenub-69809e8/pubnub/event_engine/statemachine.py 0000664 0000000 0000000 00000007623 14645232030 0023655 0 ustar 00root root 0000000 0000000 """StateMachine."""
import logging
from typing import Optional
from pubnub.event_engine.dispatcher import Dispatcher
from pubnub.event_engine.models import effects, events, states
class StateMachine:
_current_state: states.PNState
_context: states.PNContext
_effect_list: list[effects.PNEffect]
_enabled: bool
def __init__(
self,
initial_state: states.PNState,
dispatcher_class: Optional[Dispatcher] = None,
) -> None:
self._context = states.PNContext()
self._current_state = initial_state(self._context)
self._listeners = {}
self._effect_list = []
if dispatcher_class is None:
dispatcher_class = Dispatcher
self._dispatcher = dispatcher_class(self)
self._enabled = True
def get_state_name(self):
return self._current_state.__class__.__name__
def get_context(self) -> states.PNContext:
return self._current_state._context
def get_dispatcher(self) -> Dispatcher:
return self._dispatcher
def trigger(self, event: events.PNEvent) -> states.PNTransition:
logging.debug(
f"Triggered {event.__class__.__name__}({event.__dict__}) on {self.get_state_name()}"
)
if not self._enabled:
logging.error("EventEngine is not enabled")
return False
if event.get_name() in self._current_state._transitions:
self._effect_list.clear()
effect = self._current_state.on_exit()
logging.debug(f"On exit effect: {effect.__class__.__name__}")
if effect:
self._effect_list.append(effect)
transition: states.PNTransition = self._current_state.on(
event, self._context
)
self._current_state = transition.state(self._current_state.get_context())
self._context = transition.context
if transition.effect:
if isinstance(transition.effect, list):
logging.debug("unpacking list")
for effect in transition.effect:
logging.debug(f"Transition effect: {effect.__class__.__name__}")
self._effect_list.append(effect)
else:
logging.debug(
f"Transition effect: {transition.effect.__class__.__name__}"
)
self._effect_list.append(transition.effect)
effect = self._current_state.on_enter(self._context)
if effect:
logging.debug(f"On enter effect: {effect.__class__.__name__}")
self._effect_list.append(effect)
else:
# we're ignoring events unhandled
logging.debug(
f"unhandled event?? {event.__class__.__name__} in {self._current_state.__class__.__name__}"
)
self.stop()
self.dispatch_effects()
def dispatch_effects(self):
for effect in self._effect_list:
logging.debug(f"dispatching {effect.__class__.__name__} {id(effect)}")
self._dispatcher.dispatch_effect(effect)
self._effect_list.clear()
def stop(self):
self._enabled = False
if __name__ == "__main__":
machine = StateMachine(states.UnsubscribedState)
logging.debug(f"machine initialized. Current state: {machine.get_state_name()}")
effect = machine.trigger(
events.SubscriptionChangedEvent(channels=["fail"], groups=[])
)
machine.add_listener(
effects.PNEffect,
lambda x: logging.debug(f"Catch All Logger: {effect.__dict__}"),
)
machine.add_listener(
effects.EmitMessagesEffect,
)
effect = machine.trigger(events.DisconnectEvent())
logging.debug(
f"SubscriptionChangedEvent triggered with channels=[`fail`]. Curr state: {machine.get_state_name()}"
)
logging.debug(f"effect queue: {machine._effect_list}")
bdraco-freenub-69809e8/pubnub/exceptions.py 0000664 0000000 0000000 00000001225 14645232030 0020713 0 ustar 00root root 0000000 0000000 class PubNubException(Exception):
def __init__(self, errormsg="", status_code=0, pn_error=None, status=None):
self._errormsg = errormsg
self._status_code = status_code
self._pn_error = pn_error
self.status = status
if len(str(errormsg)) > 0 and int(status_code) > 0:
msg = str(pn_error) + " (" + str(status_code) + "): " + str(errormsg)
elif len(str(errormsg)) > 0:
msg = str(pn_error) + ": " + str(errormsg)
else:
msg = str(pn_error)
super().__init__(msg)
@property
def _status(self):
raise DeprecationWarning
return self.status
bdraco-freenub-69809e8/pubnub/features.py 0000664 0000000 0000000 00000000772 14645232030 0020356 0 ustar 00root root 0000000 0000000 from os import getenv
from pubnub.exceptions import PubNubException
flags = {"PN_ENABLE_ENTITIES": getenv("PN_ENABLE_ENTITIES", False)}
def feature_flag(flag):
def not_implemented(*args, **kwargs):
raise PubNubException(errormsg="This feature is not enabled")
def inner(method):
if flag not in flags.keys():
raise PubNubException(errormsg="Flag not supported")
if not flags[flag]:
return not_implemented
return method
return inner
bdraco-freenub-69809e8/pubnub/managers.py 0000664 0000000 0000000 00000046746 14645232030 0020350 0 ustar 00root root 0000000 0000000 import base64
import copy
import logging
import math
import time
from abc import ABCMeta, abstractmethod
from cbor2 import loads
from . import utils
from .callbacks import ReconnectionCallback, SubscribeCallback
from .dtos import SubscribeOperation, UnsubscribeOperation
from .enums import PNOperationType, PNReconnectionPolicy, PNStatusCategory
from .errors import PNERR_INVALID_ACCESS_TOKEN
from .exceptions import PubNubException
from .models.consumer.common import PNStatus
from .models.server.subscribe import SubscribeEnvelope
from .models.subscription_item import SubscriptionItem
logger = logging.getLogger("pubnub")
class PublishSequenceManager:
def __init__(self, provided_max_sequence):
self.max_sequence = provided_max_sequence
self.next_sequence = 0
@abstractmethod
def get_next_sequence(self):
if self.max_sequence == self.next_sequence:
self.next_sequence = 1
else:
self.next_sequence += 1
return self.next_sequence
class BasePathManager:
MAX_SUBDOMAIN = 20
DEFAULT_SUBDOMAIN = "pubsub"
DEFAULT_BASE_PATH = "pubnub.com"
def __init__(self, initial_config):
self.config = initial_config
self._current_subdomain = 1
def get_base_path(self):
if self.config.origin:
return self.config.origin
else:
return f"{BasePathManager.DEFAULT_SUBDOMAIN}.{BasePathManager.DEFAULT_BASE_PATH}"
class ReconnectionManager:
INTERVAL = 3
MINEXPONENTIALBACKOFF = 1
MAXEXPONENTIALBACKOFF = 32
def __init__(self, pubnub):
self._pubnub = pubnub
self._callback = None
self._timer = None
self._timer_interval = None
self._connection_errors = 1
def set_reconnection_listener(self, reconnection_callback):
assert isinstance(reconnection_callback, ReconnectionCallback)
self._callback = reconnection_callback
def _recalculate_interval(self):
if self._pubnub.config.reconnect_policy == PNReconnectionPolicy.EXPONENTIAL:
self._timer_interval = int(math.pow(2, self._connection_errors) - 1)
if self._timer_interval > self.MAXEXPONENTIALBACKOFF:
self._timer_interval = self.MINEXPONENTIALBACKOFF
self._connection_errors = 1
logger.debug(
"timerInterval > MAXEXPONENTIALBACKOFF at: %s"
% utils.datetime_now()
)
elif self._timer_interval < 1:
self._timer_interval = self.MINEXPONENTIALBACKOFF
logger.debug(
"timerInterval = %d at: %s"
% (self._timer_interval, utils.datetime_now())
)
else:
self._timer_interval = self.INTERVAL
@abstractmethod
def start_polling(self):
pass
def _stop_heartbeat_timer(self):
if self._timer is not None:
self._timer.stop()
self._timer = None
class StateManager:
def __init__(self):
self._channels = {}
self._groups = {}
self._presence_channels = {}
self._presence_groups = {}
def is_empty(self):
return (
len(self._channels) == 0
and len(self._groups) == 0
and len(self._presence_channels) == 0
and len(self._presence_groups) == 0
)
def subscribed_to_the_only_channel(self):
return (
len(self._channels) == 1
and len(self._groups) == 0
and len(self._presence_channels) == 0
and len(self._presence_groups) == 0
)
def prepare_channel_list(self, include_presence):
return StateManager._prepare_membership_list(
self._channels, self._presence_channels, include_presence
)
def prepare_channel_group_list(self, include_presence):
return StateManager._prepare_membership_list(
self._groups, self._presence_groups, include_presence
)
def adapt_subscribe_builder(self, subscribe_operation):
for channel in subscribe_operation.channels:
self._channels[channel] = SubscriptionItem(name=channel)
if subscribe_operation.presence_enabled:
self._presence_channels[channel] = SubscriptionItem(name=channel)
for group in subscribe_operation.channel_groups:
self._groups[group] = SubscriptionItem(name=group)
if subscribe_operation.presence_enabled:
self._presence_groups[group] = SubscriptionItem(name=group)
def adapt_unsubscribe_builder(self, unsubscribe_operation):
for channel in unsubscribe_operation.channels:
self._channels.pop(channel, None)
if channel in self._presence_channels:
self._presence_channels.pop(channel, None)
for group in unsubscribe_operation.channel_groups:
self._groups.pop(group)
if group in self._presence_groups:
self._presence_groups.pop(group)
def adapt_state_builder(self, state_operation):
for channel in state_operation.channels:
subscribed_channel = self._channels.get(channel)
if subscribed_channel is not None:
subscribed_channel.state = state_operation.state
for group in state_operation.channel_groups:
subscribed_group = self._channels.get(group)
if subscribed_group is not None:
subscribed_group.state = state_operation.state
def state_payload(self):
state = {}
for channel in self._channels.values():
if channel.state is not None:
state[channel.name] = channel.state
for group in self._groups.values():
if group.state is not None:
state[group.name] = group.state
return state
@staticmethod
def _prepare_membership_list(data_storage, presence_storage, include_presence):
response = []
for item in data_storage.values():
response.append(item.name)
if include_presence:
for item in presence_storage.values():
response.append(item.name + "-pnpres")
return response
class ListenerManager:
def __init__(self, pubnub_instance):
self._pubnub = pubnub_instance
self._listeners = []
def add_listener(self, listener):
assert isinstance(listener, SubscribeCallback)
self._listeners.append(listener)
def remove_listener(self, listener):
assert isinstance(listener, SubscribeCallback)
self._listeners.remove(listener)
def announce_status(self, status):
for callback in self._listeners:
callback.status(self._pubnub, status)
def announce_message(self, message):
for callback in self._listeners:
callback.message(self._pubnub, message)
def announce_signal(self, signal):
for callback in self._listeners:
callback.signal(self._pubnub, signal)
def announce_channel(self, channel):
for callback in self._listeners:
callback.channel(self._pubnub, channel)
def announce_uuid(self, uuid):
for callback in self._listeners:
callback.uuid(self._pubnub, uuid)
def announce_membership(self, membership):
for callback in self._listeners:
callback.membership(self._pubnub, membership)
def announce_message_action(self, message_action):
for callback in self._listeners:
callback.message_action(self._pubnub, message_action)
def announce_presence(self, presence):
for callback in self._listeners:
callback.presence(self._pubnub, presence)
def announce_file_message(self, file_message):
for callback in self._listeners:
callback.file(self._pubnub, file_message)
class SubscriptionManager:
__metaclass__ = ABCMeta
HEARTBEAT_INTERVAL_MULTIPLIER = 1000
def __init__(self, pubnub_instance):
self._pubnub = pubnub_instance
self._subscription_status_announced = False
self._subscription_state = StateManager()
self._listener_manager = ListenerManager(self._pubnub)
self._timetoken = 0
self._region = None
self._should_stop = False
self._subscribe_request_task = None
self._heartbeat_call = None
@abstractmethod
def _start_worker(self):
pass
@abstractmethod
def _set_consumer_event(self):
pass
@abstractmethod
def _message_queue_put(self, message):
pass
@abstractmethod
def _start_subscribe_loop(self):
pass
@abstractmethod
def _stop_subscribe_loop(self):
pass
@abstractmethod
def _stop_heartbeat_timer(self):
pass
@abstractmethod
def _perform_heartbeat_loop(self):
pass
@abstractmethod
def _send_leave(self, unsubscribe_operation):
pass
def add_listener(self, listener):
self._listener_manager.add_listener(listener)
def remove_listener(self, listener):
self._listener_manager.remove_listener(listener)
def get_subscribed_channels(self):
return self._subscription_state.prepare_channel_list(False)
def get_subscribed_channel_groups(self):
return self._subscription_state.prepare_channel_group_list(False)
def unsubscribe_all(self):
self.adapt_unsubscribe_builder(
UnsubscribeOperation(
channels=self._subscription_state.prepare_channel_list(False),
channel_groups=self._subscription_state.prepare_channel_group_list(
False
),
)
)
def adapt_subscribe_builder(self, subscribe_operation):
assert isinstance(subscribe_operation, SubscribeOperation)
self._subscription_state.adapt_subscribe_builder(subscribe_operation)
self._subscription_status_announced = False
if subscribe_operation.timetoken is not None:
self._timetoken = subscribe_operation.timetoken
self.reconnect()
def adapt_unsubscribe_builder(self, unsubscribe_operation):
assert isinstance(unsubscribe_operation, UnsubscribeOperation)
self._subscription_state.adapt_unsubscribe_builder(unsubscribe_operation)
if not self._pubnub.config.suppress_leave_events:
self._send_leave(unsubscribe_operation)
if self._subscription_state.is_empty():
self._region = None
self._timetoken = 0
self.reconnect()
def adapt_state_builder(self, state_operation):
self._subscription_state.adapt_state_builder(state_operation)
self.reconnect()
@abstractmethod
def reconnect(self):
pass
def stop(self):
self._should_stop = True
self._stop_subscribe_loop()
self._stop_heartbeat_timer()
self._set_consumer_event()
def _handle_endpoint_call(self, raw_result, status):
assert isinstance(status, PNStatus)
if not self._subscription_status_announced:
pn_status = PNStatus()
pn_status.category = PNStatusCategory.PNConnectedCategory
pn_status.status_code = status.status_code
pn_status.auth_key = status.auth_key
pn_status.operation = status.operation
pn_status.client_request = status.client_request
pn_status.origin = status.origin
pn_status.tls_enabled = status.tls_enabled
pn_status.affected_channels = status.affected_channels
pn_status.affected_groups = status.affected_groups
self._subscription_status_announced = True
self._listener_manager.announce_status(pn_status)
result = SubscribeEnvelope.from_json(raw_result)
only_channel = self._subscription_state.subscribed_to_the_only_channel()
if result.messages is not None and len(result.messages) > 0:
for message in result.messages:
if only_channel:
message.only_channel_subscription = True
self._message_queue_put(message)
self._timetoken = int(result.metadata.timetoken)
self._region = int(result.metadata.region)
# TODO: make abstract
def _register_heartbeat_timer(self):
self._stop_heartbeat_timer()
class TelemetryManager:
TIMESTAMP_DIVIDER = 1000
MAXIMUM_LATENCY_DATA_AGE = 60
CLEAN_UP_INTERVAL = 1
CLEAN_UP_INTERVAL_MULTIPLIER = 1000
def __init__(self):
self.latencies = {}
@abstractmethod
def _start_clean_up_timer(self):
pass
@abstractmethod
def _stop_clean_up_timer(self):
pass
def operation_latencies(self):
operation_latencies = {}
for endpoint_name, endpoint_latencies in self.latencies.items():
latency_key = "l_" + endpoint_name
endpoint_average_latency = self.average_latency_from_data(
endpoint_latencies
)
if endpoint_average_latency > 0:
operation_latencies[latency_key] = endpoint_average_latency
return operation_latencies
def clean_up_telemetry_data(self):
current_timestamp = time.time()
copy_latencies = copy.deepcopy(self.latencies)
for endpoint_name, endpoint_latencies in copy_latencies.items():
for latency_information in endpoint_latencies:
if (
current_timestamp - latency_information["timestamp"]
> self.MAXIMUM_LATENCY_DATA_AGE
):
self.latencies[endpoint_name].remove(latency_information)
if len(self.latencies[endpoint_name]) == 0:
del self.latencies[endpoint_name]
def store_latency(self, latency, operation_type):
if operation_type != PNOperationType.PNSubscribeOperation and latency > 0:
endpoint_name = self.endpoint_name_for_operation(operation_type)
store_timestamp = time.time()
if endpoint_name not in self.latencies:
self.latencies[endpoint_name] = []
latency_entry = {
"timestamp": store_timestamp,
"latency": latency,
}
self.latencies[endpoint_name].append(latency_entry)
@staticmethod
def average_latency_from_data(endpoint_latencies):
total_latency = 0
for latency_data in endpoint_latencies:
total_latency += latency_data["latency"]
return total_latency / len(endpoint_latencies)
@staticmethod
def endpoint_name_for_operation(operation_type):
endpoint = {
PNOperationType.PNPublishOperation: "pub",
PNOperationType.PNFireOperation: "pub",
PNOperationType.PNHistoryOperation: "hist",
PNOperationType.PNHistoryDeleteOperation: "hist",
PNOperationType.PNMessageCountOperation: "mc",
PNOperationType.PNUnsubscribeOperation: "pres",
PNOperationType.PNWhereNowOperation: "pres",
PNOperationType.PNHereNowOperation: "pres",
PNOperationType.PNGetState: "pres",
PNOperationType.PNSetStateOperation: "pres",
PNOperationType.PNHeartbeatOperation: "pres",
PNOperationType.PNAddChannelsToGroupOperation: "cg",
PNOperationType.PNRemoveChannelsFromGroupOperation: "cg",
PNOperationType.PNChannelGroupsOperation: "cg",
PNOperationType.PNChannelsForGroupOperation: "cg",
PNOperationType.PNRemoveGroupOperation: "cg",
PNOperationType.PNAddPushNotificationsOnChannelsOperation: "push",
PNOperationType.PNPushNotificationEnabledChannelsOperation: "push",
PNOperationType.PNRemoveAllPushNotificationsOperation: "push",
PNOperationType.PNRemovePushNotificationsFromChannelsOperation: "push",
PNOperationType.PNAccessManagerAudit: "pam",
PNOperationType.PNAccessManagerGrant: "pam",
PNOperationType.PNAccessManagerRevoke: "pam",
PNOperationType.PNTimeOperation: "pam",
PNOperationType.PNAccessManagerGrantToken: "pamv3",
PNOperationType.PNAccessManagerRevokeToken: "pamv3",
PNOperationType.PNSignalOperation: "sig",
PNOperationType.PNSetUuidMetadataOperation: "obj",
PNOperationType.PNGetUuidMetadataOperation: "obj",
PNOperationType.PNRemoveUuidMetadataOperation: "obj",
PNOperationType.PNGetAllUuidMetadataOperation: "obj",
PNOperationType.PNSetChannelMetadataOperation: "obj",
PNOperationType.PNGetChannelMetadataOperation: "obj",
PNOperationType.PNRemoveChannelMetadataOperation: "obj",
PNOperationType.PNGetAllChannelMetadataOperation: "obj",
PNOperationType.PNSetChannelMembersOperation: "obj",
PNOperationType.PNGetChannelMembersOperation: "obj",
PNOperationType.PNRemoveChannelMembersOperation: "obj",
PNOperationType.PNManageChannelMembersOperation: "obj",
PNOperationType.PNSetMembershipsOperation: "obj",
PNOperationType.PNGetMembershipsOperation: "obj",
PNOperationType.PNRemoveMembershipsOperation: "obj",
PNOperationType.PNManageMembershipsOperation: "obj",
PNOperationType.PNAddMessageAction: "msga",
PNOperationType.PNGetMessageActions: "msga",
PNOperationType.PNDeleteMessageAction: "msga",
PNOperationType.PNGetFilesAction: "file",
PNOperationType.PNDeleteFileOperation: "file",
PNOperationType.PNGetFileDownloadURLAction: "file",
PNOperationType.PNFetchFileUploadS3DataAction: "file",
PNOperationType.PNDownloadFileAction: "file",
PNOperationType.PNSendFileAction: "file",
}[operation_type]
return endpoint
class TokenManager:
def __init__(self):
self.token = None
def set_token(self, token):
self.token = token
def get_token(self):
return self.token
@classmethod
def parse_token(cls, token):
token = cls.unwrap_token(token)
parsed_token = {
"version": token["v"],
"timestamp": token["t"],
"ttl": token["ttl"],
"authorized_uuid": token.get("uuid"),
"resources": {},
"patterns": {},
"meta": token["meta"],
}
perm_type_name_mapping = {"res": "resources", "pat": "patterns"}
for resource_type in perm_type_name_mapping:
for resource in token[resource_type]:
if resource == "uuid":
parsed_token[perm_type_name_mapping[resource_type]]["uuids"] = (
utils.parse_pam_permissions(token[resource_type][resource])
)
elif resource == "grp":
parsed_token[perm_type_name_mapping[resource_type]]["groups"] = (
utils.parse_pam_permissions(token[resource_type][resource])
)
elif resource == "chan":
parsed_token[perm_type_name_mapping[resource_type]]["channels"] = (
utils.parse_pam_permissions(token[resource_type][resource])
)
return parsed_token
@staticmethod
def unwrap_token(token):
token = token.replace("_", "/").replace("-", "+")
byte_array = base64.b64decode(token)
try:
unwrapped_obj = loads(byte_array)
decoded_obj = utils.decode_utf8_dict(unwrapped_obj)
return decoded_obj
except Exception:
raise PubNubException(pn_error=PNERR_INVALID_ACCESS_TOKEN)
bdraco-freenub-69809e8/pubnub/models/ 0000775 0000000 0000000 00000000000 14645232030 0017443 5 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/pubnub/models/__init__.py 0000664 0000000 0000000 00000000000 14645232030 0021542 0 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/pubnub/models/consumer/ 0000775 0000000 0000000 00000000000 14645232030 0021276 5 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/pubnub/models/consumer/__init__.py 0000664 0000000 0000000 00000000000 14645232030 0023375 0 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/pubnub/models/consumer/access_manager.py 0000664 0000000 0000000 00000014163 14645232030 0024610 0 ustar 00root root 0000000 0000000 """
Possible responses of PAM request
"""
class _PAMResult:
def __init__(
self,
level,
subscribe_key,
channels,
groups,
uuids,
ttl=None,
r=None,
w=None,
m=None,
d=None,
):
self.level = level
self.subscribe_key = subscribe_key
self.channels = channels
self.groups = groups
self.uuids = uuids
self.ttl = ttl
self.read_enabled = r
self.write_enabled = w
self.manage_enabled = m
self.delete_enabled = d
@classmethod
def from_json(cls, json_input):
constructed_channels = {}
constructed_groups = {}
constructed_uuids = {}
# only extract ttl, others are to be fetched on per uuid level
r, w, m, d, g, u, j, ttl = fetch_permissions(json_input)
if "channel" in json_input:
channel_name = json_input["channel"]
constructed_auth_keys = {}
for auth_key_name, value in json_input["auths"].items():
constructed_auth_keys[auth_key_name] = PNAccessManagerKeyData.from_json(
value
)
constructed_channels[channel_name] = PNAccessManagerChannelData(
name=channel_name, auth_keys=constructed_auth_keys, ttl=ttl
)
if "channel-group" in json_input:
if isinstance(json_input["channel-group"], str):
group_name = json_input["channel-group"]
constructed_auth_keys = {}
for auth_key_name, value in json_input["auths"].items():
constructed_auth_keys[auth_key_name] = (
PNAccessManagerKeyData.from_json(value)
)
constructed_groups[group_name] = PNAccessManagerChannelGroupData(
name=group_name, auth_keys=constructed_auth_keys, ttl=ttl
)
if "channel-groups" in json_input:
if isinstance(json_input["channel-groups"], str):
group_name = json_input["channel-groups"]
constructed_auth_keys = {}
for auth_key_name, value in json_input["auths"].items():
constructed_auth_keys[auth_key_name] = (
PNAccessManagerKeyData.from_json(value)
)
constructed_groups[group_name] = PNAccessManagerChannelGroupData(
name=group_name, auth_keys=constructed_auth_keys, ttl=ttl
)
if isinstance(json_input["channel-groups"], dict):
for group_name, value in json_input["channel-groups"].items():
constructed_groups[group_name] = (
PNAccessManagerChannelGroupData.from_json(group_name, value)
)
if "channels" in json_input:
for channel_name, value in json_input["channels"].items():
constructed_channels[channel_name] = (
PNAccessManagerChannelData.from_json(channel_name, value)
)
if "uuids" in json_input:
for uuid, value in json_input["uuids"].items():
constructed_uuids[uuid] = PNAccessManagerUuidsData.from_json(
uuid, value
)
return cls(
level=json_input["level"],
subscribe_key=json_input["subscribe_key"],
channels=constructed_channels,
groups=constructed_groups,
uuids=constructed_uuids,
r=r,
w=w,
m=m,
d=d,
ttl=ttl,
)
class PNAccessManagerResult(_PAMResult):
def __str__(self):
return "Permissions are valid for %d minutes" % self.ttl or 0
class PNAccessManagerAuditResult(PNAccessManagerResult):
pass
class PNAccessManagerGrantResult(PNAccessManagerResult):
pass
class _PAMEntityData:
def __init__(
self,
name,
auth_keys=None,
r=None,
w=None,
m=None,
d=None,
g=None,
u=None,
j=None,
ttl=None,
):
self.name = name
self.auth_keys = auth_keys
self.read_enabled = r
self.write_enabled = w
self.manage_enabled = m
self.delete_enabled = d
self.get = g
self.update = u
self.join = j
self.ttl = ttl
@classmethod
def from_json(cls, name, json_input):
r, w, m, d, g, u, j, ttl = fetch_permissions(json_input)
constructed_auth_keys = {}
if "auths" in json_input:
for auth_key, value in json_input["auths"].items():
constructed_auth_keys[auth_key] = PNAccessManagerKeyData.from_json(
value
)
return cls(name, constructed_auth_keys, r, w, m, d, g, u, j, ttl)
class PNAccessManagerChannelData(_PAMEntityData):
pass
class PNAccessManagerChannelGroupData(_PAMEntityData):
pass
class PNAccessManagerUuidsData(_PAMEntityData):
pass
class PNAccessManagerKeyData:
def __init__(self, r, w, m, d, g, u, j, ttl=None):
self.read_enabled = r
self.write_enabled = w
self.manage_enabled = m
self.delete_enabled = d
self.get = g
self.update = u
self.join = j
self.ttl = ttl
@classmethod
def from_json(cls, json_input):
r, w, m, d, g, u, j, ttl = fetch_permissions(json_input)
return PNAccessManagerKeyData(r, w, m, d, g, u, j, ttl)
def fetch_permissions(json_input):
r = None
w = None
m = None
d = None
g = None
u = None
j = None
ttl = None
if "r" in json_input:
r = json_input["r"] == 1
if "w" in json_input:
w = json_input["w"] == 1
if "m" in json_input:
m = json_input["m"] == 1
if "d" in json_input:
d = json_input["d"] == 1
if "g" in json_input:
g = json_input["g"] == 1
if "u" in json_input:
u = json_input["u"] == 1
if "j" in json_input:
j = json_input["j"] == 1
if "ttl" in json_input:
ttl = json_input["ttl"]
return r, w, m, d, g, u, j, ttl
bdraco-freenub-69809e8/pubnub/models/consumer/channel_group.py 0000664 0000000 0000000 00000001030 14645232030 0024466 0 ustar 00root root 0000000 0000000 class PNChannelGroupsAddChannelResult:
def __str__(self):
return "Channel successfully added"
class PNChannelGroupsRemoveChannelResult:
def __str__(self):
return "Channel successfully removed"
class PNChannelGroupsRemoveGroupResult:
def __str__(self):
return "Group successfully removed"
class PNChannelGroupsListResult:
def __init__(self, channels):
self.channels = channels
def __str__(self):
return "Group contains following channels: %s" % ", ".join(self.channels)
bdraco-freenub-69809e8/pubnub/models/consumer/common.py 0000664 0000000 0000000 00000001041 14645232030 0023134 0 ustar 00root root 0000000 0000000 class PNStatus:
def __init__(self):
self.category = None
self.error_data = None
self.error = None
self.status_code = None
self.operation = None
self.tls_enabled = None
self.uuid = None
self.auth_key = None
self.origin = None
self.client_request = None
self.client_response = None
self.original_response = None
self.affected_channels = None
self.affected_groups = None
def is_error(self):
return bool(self.error)
bdraco-freenub-69809e8/pubnub/models/consumer/entities/ 0000775 0000000 0000000 00000000000 14645232030 0023122 5 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/pubnub/models/consumer/entities/__init__.py 0000664 0000000 0000000 00000000000 14645232030 0025221 0 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/pubnub/models/consumer/entities/membership.py 0000664 0000000 0000000 00000001620 14645232030 0025626 0 ustar 00root root 0000000 0000000 from pubnub.models.consumer.entities.result import PNEntityPageableResult
class PNMembershipsResult(PNEntityPageableResult):
_description = "Set Memberships: %s"
def __init__(self, result):
super().__init__(result)
self.status = result["status"]
def rename_channel(result):
result["space"] = result.pop("channel")
return result
def rename_uuid(result):
result["user"] = result.pop("uuid")
return result
class PNUserMembershipsResult(PNMembershipsResult):
def __init__(self, result):
super().__init__(result)
self.data = [
PNMembershipsResult.rename_channel(space) for space in result["data"]
]
class PNSpaceMembershipsResult(PNMembershipsResult):
def __init__(self, result):
super().__init__(result)
self.data = [PNMembershipsResult.rename_uuid(user) for user in result["data"]]
bdraco-freenub-69809e8/pubnub/models/consumer/entities/page.py 0000664 0000000 0000000 00000001351 14645232030 0024410 0 ustar 00root root 0000000 0000000 from abc import ABCMeta
class PNPage:
__metaclass__ = ABCMeta
def __init__(self, hash):
self._hash = str(hash)
@property
def hash(self):
return self._hash
@classmethod
def builder(cls, value):
if value is None:
return None
return cls(value)
class Next(PNPage):
def __init__(self, hash):
super().__init__(hash)
class Previous(PNPage):
def __init__(self, hash):
super().__init__(hash)
class PNPageable:
__metaclass__ = ABCMeta
def __init__(self, result):
self.total_count = result.get("totalCount", None)
self.next = Next.builder(result.get("next", None))
self.prev = Previous.builder(result.get("prev", None))
bdraco-freenub-69809e8/pubnub/models/consumer/entities/result.py 0000664 0000000 0000000 00000000671 14645232030 0025016 0 ustar 00root root 0000000 0000000 from pubnub.models.consumer.objects_v2.page import PNPageable
class PNEntityResult:
def __init__(self, result):
self.data = result["data"]
self.status = result["status"]
def __str__(self):
return self._description % self.data
class PNEntityPageableResult(PNEntityResult, PNPageable):
def __init__(self, result):
PNEntityResult.__init__(self, result)
PNPageable.__init__(self, result)
bdraco-freenub-69809e8/pubnub/models/consumer/entities/space.py 0000664 0000000 0000000 00000002120 14645232030 0024562 0 ustar 00root root 0000000 0000000 from typing import Optional
from pubnub.models.consumer.entities.result import (
PNEntityPageableResult,
PNEntityResult,
)
class PNCreateSpaceResult(PNEntityResult):
_description = "Create Space: %s"
class PNUpdateSpaceResult(PNEntityResult):
_description = "Update Space: %s"
class PNFetchSpaceResult(PNEntityResult):
_description = "Fetch Space: %s"
class PNRemoveSpaceResult(PNEntityResult):
_description = "Remove Space: %s"
class PNFetchSpacesResult(PNEntityPageableResult):
_description = "Fetch Spaces: %s"
class PNSpaceResult(PNEntityResult):
def __str__(self):
return f"Space {self.event} event with data: {self.data}"
class Space:
space_id: str
custom: Optional[dict]
def __init__(self, space_id=None, **kwargs):
self.space_id = space_id
if "custom" in kwargs.keys():
self.custom = kwargs["custom"]
def to_payload_dict(self):
result = {"channel": {"id": str(self.space_id)}}
if "custom" in self.__dict__.keys():
result["custom"] = self.custom
return result
bdraco-freenub-69809e8/pubnub/models/consumer/entities/user.py 0000664 0000000 0000000 00000002073 14645232030 0024454 0 ustar 00root root 0000000 0000000 from typing import Optional
from pubnub.models.consumer.entities.result import (
PNEntityPageableResult,
PNEntityResult,
)
class PNCreateUserResult(PNEntityResult):
_description = "Create User: %s"
class PNUpdateUserResult(PNEntityResult):
_description = "Update User: %s"
class PNFetchUserResult(PNEntityResult):
_description = "Fetch User: %s"
class PNRemoveUserResult(PNEntityResult):
_description = "Remove User: %s"
class PNFetchUsersResult(PNEntityPageableResult):
_description = "Fetch Users: %s"
class PNUserResult(PNEntityResult):
def __str__(self):
return f"UUID {self.event} event with data: {self.data}"
class User:
user_id: str
custom: Optional[dict]
def __init__(self, user_id=None, **kwargs):
self.user_id = user_id
if "custom" in kwargs.keys():
self.custom = kwargs["custom"]
def to_payload_dict(self):
result = {"uuid": {"id": str(self.user_id)}}
if "custom" in self.__dict__.keys():
result["custom"] = self.custom
return result
bdraco-freenub-69809e8/pubnub/models/consumer/file.py 0000664 0000000 0000000 00000003250 14645232030 0022567 0 ustar 00root root 0000000 0000000 class PNGetFilesResult:
def __init__(self, result):
self.data = result["data"]
self.count = result.get("count", None)
self.next = result.get("next", None)
self.prev = result.get("prev", None)
def __str__(self):
return "Get files success with data: %s" % self.data
class PNDeleteFileResult:
def __init__(self, result):
self.status = result["status"]
def __str__(self):
return "Delete files success with status: %s" % self.status
class PNGetFileDownloadURLResult:
def __init__(self, result, data=None):
self.file_url = result.headers["Location"]
def __str__(self):
return "Get file URL success with status: %s" % self.status
class PNFetchFileUploadS3DataResult:
def __init__(self, result):
self.name = result["data"]["name"]
self.file_id = result["data"]["id"]
self.data = result["file_upload_request"]
def __str__(self):
return "Fetch file upload S3 data success with status: %s" % self.status
class PNDownloadFileResult:
def __init__(self, result):
self.data = result
def __str__(self):
return "Downloading file success with status: %s" % self.status
class PNSendFileResult:
def __init__(self, result, file_upload_data):
self.name = file_upload_data.result.name
self.file_id = file_upload_data.result.file_id
def __str__(self):
return "Sending file success with status: %s" % self.status
class PNPublishFileMessageResult:
def __init__(self, result):
self.timestamp = result[2]
def __str__(self):
return "Sending file notification success with status: %s" % self.status
bdraco-freenub-69809e8/pubnub/models/consumer/history.py 0000664 0000000 0000000 00000007276 14645232030 0023365 0 ustar 00root root 0000000 0000000 class PNHistoryResult:
def __init__(self, messages, start_timetoken, end_timetoken):
self.messages = messages
self.start_timetoken = start_timetoken
self.end_timetoken = end_timetoken
def __str__(self):
return "History result for range %d..%d" % (
self.start_timetoken,
self.end_timetoken,
)
@classmethod
def from_json(
cls,
json_input,
crypto,
include_timetoken=False,
include_meta=False,
cipher=None,
):
start_timetoken = json_input[1]
end_timetoken = json_input[2]
raw_items = json_input[0]
messages = []
for item in raw_items:
if (
(include_timetoken or include_meta)
and isinstance(item, dict)
and "message" in item
):
message = PNHistoryItemResult(item["message"], crypto)
if include_timetoken and "timetoken" in item:
message.timetoken = item["timetoken"]
if include_meta and "meta" in item:
message.meta = item["meta"]
else:
message = PNHistoryItemResult(item, crypto)
if cipher is not None:
message.decrypt(cipher)
messages.append(message)
return PNHistoryResult(
messages=messages,
start_timetoken=start_timetoken,
end_timetoken=end_timetoken,
)
class PNHistoryItemResult:
def __init__(self, entry, crypto, timetoken=None, meta=None):
self.timetoken = timetoken
self.meta = meta
self.entry = entry
self.crypto = crypto
def __str__(self):
return f"History item with tt: {self.timetoken} and content: {self.entry}"
def decrypt(self, cipher_key):
self.entry = self.crypto.decrypt(cipher_key, self.entry)
class PNFetchMessagesResult:
def __init__(self, channels, start_timetoken, end_timetoken):
self.channels = channels
self.start_timetoken = start_timetoken
self.end_timetoken = end_timetoken
def __str__(self):
return "Fetch messages result for range %d..%d" % (
self.start_timetoken,
self.end_timetoken,
)
@classmethod
def from_json(
cls,
json_input,
include_message_actions=False,
start_timetoken=None,
end_timetoken=None,
):
channels = {}
for key, entry in json_input["channels"].items():
channels[key] = []
for item in entry:
message = PNFetchMessageItem(item["message"], item["timetoken"])
if "uuid" in item:
message.uuid = item["uuid"]
if "message_type" in item:
message.message_type = item["message_type"]
if "meta" in item:
message.meta = item["meta"]
if include_message_actions:
if "actions" in item:
message.actions = item["actions"]
else:
message.actions = {}
channels[key].append(message)
return PNFetchMessagesResult(
channels=channels,
start_timetoken=start_timetoken,
end_timetoken=end_timetoken,
)
class PNFetchMessageItem:
def __init__(self, message, timetoken, meta=None, actions=None):
self.message = message
self.meta = meta
self.timetoken = timetoken
self.actions = actions
def __str__(self):
return (
f"Fetch message item with tt: {self.timetoken} and content: {self.message}"
)
bdraco-freenub-69809e8/pubnub/models/consumer/message_actions.py 0000664 0000000 0000000 00000003003 14645232030 0025010 0 ustar 00root root 0000000 0000000 class PNMessageAction:
def __init__(self, message_action=None):
if message_action is not None:
self.type = message_action["type"]
self.value = message_action["value"]
self.message_timetoken = message_action["messageTimetoken"]
self.uuid = message_action["uuid"]
self.action_timetoken = message_action["actionTimetoken"]
else:
self.type = None
self.value = None
self.message_timetoken = None
self.uuid = None
self.action_timetoken = None
def __str__(self):
return f"Message action with tt: {self.action_timetoken} for uuid {self.uuid} with value {self.value} "
class PNGetMessageActionsResult:
def __init__(self, result):
"""
Representation of get message actions server response
:param result: result of get message actions operation
"""
self._result = result
self.actions = result["actions"]
def __str__(self):
return "Get message actions success"
class PNAddMessageActionResult(PNMessageAction):
def __init__(self, message_action):
super().__init__(message_action)
class PNRemoveMessageActionResult:
def __init__(self, result):
"""
s
Representation of remove message actions server response
:param result: result of remove message actions operation
"""
self._result = result
def __str__(self):
return "Remove message actions success"
bdraco-freenub-69809e8/pubnub/models/consumer/message_count.py 0000664 0000000 0000000 00000000545 14645232030 0024510 0 ustar 00root root 0000000 0000000 class PNMessageCountResult:
def __init__(self, result):
"""
Representation of message count server response
:param result: result of message count operation
"""
self._result = result
self.channels = result["channels"]
def __str__(self):
return f"Message count for channels: {self.channels}"
bdraco-freenub-69809e8/pubnub/models/consumer/objects_v2/ 0000775 0000000 0000000 00000000000 14645232030 0023336 5 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/pubnub/models/consumer/objects_v2/__init__.py 0000664 0000000 0000000 00000000000 14645232030 0025435 0 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/pubnub/models/consumer/objects_v2/channel.py 0000664 0000000 0000000 00000002310 14645232030 0025314 0 ustar 00root root 0000000 0000000 from pubnub.models.consumer.objects_v2.page import PNPageable
class PNSetChannelMetadataResult:
def __init__(self, result):
self.data = result["data"]
self.status = result["status"]
def __str__(self):
return "Set Channel metatdata: %s" % self.data
class PNGetChannelMetadataResult:
def __init__(self, result):
self.data = result["data"]
self.status = result["status"]
def __str__(self):
return "Get Channel metatdata: %s" % self.data
class PNRemoveChannelMetadataResult:
def __init__(self, result):
self.data = result["data"]
self.status = result["status"]
def __str__(self):
return "Get Channel metatdata: %s" % self.data
class PNGetAllChannelMetadataResult(PNPageable):
def __init__(self, result):
PNPageable.__init__(self, result)
self.data = result["data"]
self.status = result["status"]
def __str__(self):
return "Get all Channel metatdata: %s" % self.data
class PNChannelMetadataResult:
def __init__(self, event, data):
self.data = data
self.event = event
def __str__(self):
return f"Channel {self.event} event with data: {self.data}"
bdraco-freenub-69809e8/pubnub/models/consumer/objects_v2/channel_members.py 0000664 0000000 0000000 00000003625 14645232030 0027040 0 ustar 00root root 0000000 0000000 from abc import ABCMeta, abstractmethod
from pubnub.models.consumer.objects_v2.page import PNPageable
class PNUUID:
__metaclass__ = ABCMeta
def __init__(self, uuid):
self._uuid = uuid
@staticmethod
def uuid(uuid):
return JustUUID(uuid)
@staticmethod
def uuid_with_custom(uuid, custom):
return UUIDWithCustom(uuid, custom)
@abstractmethod
def to_payload_dict(self):
return None
class JustUUID(PNUUID):
def to_payload_dict(self):
return {"uuid": {"id": str(self._uuid)}}
class UUIDWithCustom(PNUUID):
def __init__(self, uuid, custom):
PNUUID.__init__(self, uuid)
self._custom = custom
def to_payload_dict(self):
return {"uuid": {"id": str(self._uuid)}, "custom": dict(self._custom)}
class PNSetChannelMembersResult(PNPageable):
def __init__(self, result):
PNPageable.__init__(self, result)
self.data = result["data"]
self.status = result["status"]
def __str__(self):
return "Set Channel Members metatdata: %s" % self.data
class PNGetChannelMembersResult(PNPageable):
def __init__(self, result):
PNPageable.__init__(self, result)
self.data = result["data"]
self.status = result["status"]
def __str__(self):
return "Get Channel Members metatdata: %s" % self.data
class PNRemoveChannelMembersResult(PNPageable):
def __init__(self, result):
PNPageable.__init__(self, result)
self.data = result["data"]
self.status = result["status"]
def __str__(self):
return "Remove Channel Members metatdata: %s" % self.data
class PNManageChannelMembersResult(PNPageable):
def __init__(self, result):
PNPageable.__init__(self, result)
self.data = result["data"]
self.status = result["status"]
def __str__(self):
return "Manage Channel Members metatdata: %s" % self.data
bdraco-freenub-69809e8/pubnub/models/consumer/objects_v2/memberships.py 0000664 0000000 0000000 00000004424 14645232030 0026232 0 ustar 00root root 0000000 0000000 from abc import ABCMeta, abstractmethod
from pubnub.models.consumer.objects_v2.page import PNPageable
class PNChannelMembership:
__metaclass__ = ABCMeta
def __init__(self, channel):
self._channel = channel
@staticmethod
def channel(channel):
return JustChannel(channel)
@staticmethod
def channel_with_custom(channel, custom):
return ChannelWithCustom(channel, custom)
@abstractmethod
def to_payload_dict(self):
return None
class JustChannel(PNChannelMembership):
def __init__(self, channel):
PNChannelMembership.__init__(self, channel)
def to_payload_dict(self):
return {"channel": {"id": str(self._channel)}}
class ChannelWithCustom(PNChannelMembership):
def __init__(self, channel, custom):
PNChannelMembership.__init__(self, channel)
self._custom = custom
def to_payload_dict(self):
return {"channel": {"id": str(self._channel)}, "custom": dict(self._custom)}
class PNSetMembershipsResult(PNPageable):
def __init__(self, result):
PNPageable.__init__(self, result)
self.data = result["data"]
self.status = result["status"]
def __str__(self):
return "Set Memberships metatdata: %s" % self.data
class PNGetMembershipsResult(PNPageable):
def __init__(self, result):
PNPageable.__init__(self, result)
self.data = result["data"]
self.status = result["status"]
def __str__(self):
return "Get Memberships metatdata: %s" % self.data
class PNRemoveMembershipsResult(PNPageable):
def __init__(self, result):
PNPageable.__init__(self, result)
self.data = result["data"]
self.status = result["status"]
def __str__(self):
return "Remove Memberships metatdata: %s" % self.data
class PNManageMembershipsResult(PNPageable):
def __init__(self, result):
PNPageable.__init__(self, result)
self.data = result["data"]
self.status = result["status"]
def __str__(self):
return "Manage Channel Members metatdata: %s" % self.data
class PNMembershipResult:
def __init__(self, event, data):
self.data = data
self.event = event
def __str__(self):
return f"Membership {self.event} event with data: {self.data}"
bdraco-freenub-69809e8/pubnub/models/consumer/objects_v2/page.py 0000664 0000000 0000000 00000001375 14645232030 0024632 0 ustar 00root root 0000000 0000000 from abc import ABCMeta
class PNPage:
__metaclass__ = ABCMeta
def __init__(self, hash):
self._hash = str(hash)
@property
def hash(self):
return self._hash
class Next(PNPage):
def __init__(self, hash):
PNPage.__init__(self, hash)
class Previous(PNPage):
def __init__(self, hash):
PNPage.__init__(self, hash)
class PNPageable:
__metaclass__ = ABCMeta
def __init__(self, result):
self.total_count = result.get("totalCount", None)
if result.get("next", None):
self.next = Next(result["next"])
else:
self.next = None
if result.get("prev", None):
self.prev = Previous(result["prev"])
else:
self.prev = None
bdraco-freenub-69809e8/pubnub/models/consumer/objects_v2/sort.py 0000664 0000000 0000000 00000002054 14645232030 0024700 0 ustar 00root root 0000000 0000000 from enum import Enum
class PNSortKeyValue(Enum):
ID = 1
NAME = 2
UPDATED = 3
class PNSortDirection(Enum):
ASC = 1
DESC = 2
class PNSortKey:
def __init__(self, sort_key_value, direction):
self._sort_key_value = sort_key_value
self._direction = direction
@staticmethod
def asc(sort_key_value):
return PNSortKey(sort_key_value, PNSortDirection.ASC)
@staticmethod
def desc(sort_key_value):
return PNSortKey(sort_key_value, PNSortDirection.DESC)
def key_str(self):
if self._sort_key_value == PNSortKeyValue.ID:
return "id"
elif self._sort_key_value == PNSortKeyValue.NAME:
return "name"
elif self._sort_key_value == PNSortKeyValue.UPDATED:
return "updated"
else:
raise ValueError()
def dir_str(self):
if self._direction == PNSortDirection.ASC:
return "asc"
elif self._direction == PNSortDirection.DESC:
return "desc"
else:
raise ValueError()
bdraco-freenub-69809e8/pubnub/models/consumer/objects_v2/uuid.py 0000664 0000000 0000000 00000002252 14645232030 0024657 0 ustar 00root root 0000000 0000000 from pubnub.models.consumer.objects_v2.page import PNPageable
class PNSetUUIDMetadataResult:
def __init__(self, result):
self.data = result["data"]
self.status = result["status"]
def __str__(self):
return "Set UUID metatdata: %s" % self.data
class PNGetUUIDMetadataResult:
def __init__(self, result):
self.data = result["data"]
self.status = result["status"]
def __str__(self):
return "Get UUID metatdata: %s" % self.data
class PNRemoveUUIDMetadataResult:
def __init__(self, result):
self.data = result["data"]
self.status = result["status"]
def __str__(self):
return "Get UUID metatdata: %s" % self.data
class PNGetAllUUIDMetadataResult(PNPageable):
def __init__(self, result):
PNPageable.__init__(self, result)
self.data = result["data"]
self.status = result["status"]
def __str__(self):
return "Get all UUID metatdata: %s" % self.data
class PNUUIDMetadataResult:
def __init__(self, event, data):
self.data = data
self.event = event
def __str__(self):
return f"UUID {self.event} event with data: {self.data}"
bdraco-freenub-69809e8/pubnub/models/consumer/pn_error_data.py 0000664 0000000 0000000 00000000352 14645232030 0024467 0 ustar 00root root 0000000 0000000 class PNErrorData:
def __init__(self, information, exception):
assert isinstance(information, str)
assert isinstance(exception, Exception)
self.information = information
self.exception = exception
bdraco-freenub-69809e8/pubnub/models/consumer/presence.py 0000664 0000000 0000000 00000012735 14645232030 0023464 0 ustar 00root root 0000000 0000000 class PNHereNowResult:
def __init__(self, total_channels, total_occupancy, channels):
assert isinstance(total_channels, int)
assert isinstance(total_occupancy, int)
self.total_channels = total_channels
self.total_occupancy = total_occupancy
self.channels = channels
def __str__(self):
return "HereNow Result total occupancy: %d, total channels: %d" % (
self.total_occupancy,
self.total_channels,
)
@classmethod
def from_json(cls, envelope, channel_names):
# multiple
if "payload" in envelope and isinstance(envelope["payload"], dict):
json_input = envelope["payload"]
channels = []
if len(json_input["channels"]) > 0:
for channel_name, raw_data in json_input["channels"].items():
channels.append(
PNHereNowChannelData.from_json(channel_name, raw_data)
)
return PNHereNowResult(
total_channels=int(json_input["total_channels"]),
total_occupancy=int(json_input["total_occupancy"]),
channels=channels,
)
elif len(channel_names) == 1:
return PNHereNowResult(
total_channels=1,
total_occupancy=int(json_input["total_occupancy"]),
channels=[PNHereNowChannelData(channel_names[0], 0, [])],
)
else:
return PNHereNowResult(
total_channels=int(json_input["total_channels"]),
total_occupancy=int(json_input["total_occupancy"]),
channels={},
)
# empty
elif "occupancy" in envelope and int(envelope["occupancy"]) == 0:
return PNHereNowResult(
total_channels=1,
total_occupancy=int(envelope["occupancy"]),
channels=[PNHereNowChannelData(channel_names[0], 0, [])],
)
# single
elif "uuids" in envelope and isinstance(envelope["uuids"], list):
occupants = []
for user in envelope["uuids"]:
if isinstance(user, str):
occupants.append(PNHereNowOccupantsData(user, None))
else:
state = user["state"] if "state" in user else None
occupants.append(PNHereNowOccupantsData(user["uuid"], state))
return PNHereNowResult(
total_channels=1,
total_occupancy=int(envelope["occupancy"]),
channels=[
PNHereNowChannelData(
channel_name=channel_names[0],
occupancy=envelope["occupancy"],
occupants=occupants,
)
],
)
else:
return PNHereNowResult(
total_channels=1,
total_occupancy=int(envelope["occupancy"]),
channels=[
PNHereNowChannelData(
channel_name=channel_names[0],
occupancy=envelope["occupancy"],
occupants=[],
)
],
)
class PNHereNowChannelData:
def __init__(self, channel_name, occupancy, occupants):
self.channel_name = channel_name
self.occupancy = occupancy
self.occupants = occupants
def __str__(self):
return "HereNow Channel Data for channel '%s': occupancy: %d, occupants: %d" % (
self.channel_name,
self.occupancy,
self.occupants,
)
@classmethod
def from_json(cls, name, json_input):
if "uuids" in json_input:
occupants = []
for user in json_input["uuids"]:
if isinstance(user, dict) and len(user) > 0:
if "state" in user:
occupants.append(
PNHereNowOccupantsData(user["uuid"], user["state"])
)
else:
occupants.append(PNHereNowOccupantsData(user["uuid"], None))
else:
occupants.append(PNHereNowOccupantsData(user, None))
else:
occupants = None
return PNHereNowChannelData(
channel_name=name,
occupancy=int(json_input["occupancy"]),
occupants=occupants,
)
class PNHereNowOccupantsData:
def __init__(self, uuid, state):
self.uuid = uuid
self.state = state
def __str__(self):
return f"HereNow Occupants Data for '{self.uuid}': {self.state}"
class PNWhereNowResult:
def __init__(self, channels):
assert isinstance(channels, (list, tuple))
self.channels = channels
def __str__(self):
return "User is currently subscribed to %s" % ", ".join(self.channels)
@classmethod
def from_json(cls, json_input):
return PNWhereNowResult(json_input["payload"]["channels"])
class PNSetStateResult:
def __init__(self, state):
assert isinstance(state, dict)
self.state = state
def __str__(self):
return "New state %s successfully set" % self.state
class PNGetStateResult:
def __init__(self, channels):
assert isinstance(channels, dict)
self.channels = channels
def __str__(self):
return "Current state is %s" % self.channels
bdraco-freenub-69809e8/pubnub/models/consumer/pubsub.py 0000664 0000000 0000000 00000007012 14645232030 0023150 0 ustar 00root root 0000000 0000000 from pubnub.models.consumer.message_actions import PNMessageAction
class PNMessageResult:
def __init__(
self,
message,
subscription,
channel,
timetoken,
user_metadata=None,
publisher=None,
):
if subscription is not None:
assert isinstance(subscription, str)
if channel is not None:
assert isinstance(channel, str)
if publisher is not None:
assert isinstance(publisher, str)
assert isinstance(timetoken, int)
if user_metadata is not None:
assert isinstance(user_metadata, object)
self.message = message
# DEPRECATED: subscribed_channel and actual_channel properties are deprecated
# self.subscribed_channel = subscribed_channel <= now known as subscription
# self.actual_channel = actual_channel <= now known as channel
self.channel = channel
self.subscription = subscription
self.timetoken = timetoken
self.user_metadata = user_metadata
self.publisher = publisher
class PNSignalMessageResult(PNMessageResult):
pass
class PNFileMessageResult(PNMessageResult):
def __init__(
self,
message,
subscription,
channel,
timetoken,
publisher,
file_url,
file_id,
file_name,
):
super().__init__(message, subscription, channel, timetoken, publisher=publisher)
self.file_url = file_url
self.file_id = file_id
self.file_name = file_name
class PNPresenceEventResult:
def __init__(
self,
event,
uuid,
timestamp,
occupancy,
subscription,
channel,
timetoken,
state,
join,
leave,
timeout,
user_metadata=None,
):
assert isinstance(event, str)
assert isinstance(timestamp, int)
assert isinstance(occupancy, int)
assert isinstance(channel, str)
assert isinstance(timetoken, int)
if user_metadata is not None:
assert isinstance(user_metadata, object)
if state is not None:
assert isinstance(state, dict)
self.event = event
self.uuid = uuid
self.timestamp = timestamp
self.occupancy = occupancy
self.state = state
self.join = join
self.leave = leave
self.timeout = timeout
# DEPRECATED: subscribed_channel and actual_channel properties are deprecated
# self.subscribed_channel = subscribed_channel <= now known as subscription
# self.actual_channel = actual_channel <= now known as channel
self.subscription = subscription
self.channel = channel
self.timetoken = timetoken
self.user_metadata = user_metadata
class PNMessageActionResult(PNMessageAction):
def __init__(self, result):
super().__init__(result)
class PNPublishResult:
def __init__(self, envelope, timetoken):
"""
Representation of publish server response
:param timetoken: of publish operation
"""
self.timetoken = timetoken
def __str__(self):
return "Publish success with timetoken %s" % self.timetoken
class PNFireResult:
def __init__(self, envelope, timetoken):
"""
Representation of fire server response
:param timetoken: of fire operation
"""
self.timetoken = timetoken
def __str__(self):
return "Fire success with timetoken %s" % self.timetoken
bdraco-freenub-69809e8/pubnub/models/consumer/push.py 0000664 0000000 0000000 00000001057 14645232030 0022632 0 ustar 00root root 0000000 0000000 class PNPushAddChannelResult:
def __str__(self):
return "Channel successfully added"
class PNPushRemoveChannelResult:
def __str__(self):
return "Channel successfully removed"
class PNPushRemoveAllChannelsResult:
def __str__(self):
return "All channels successfully removed"
class PNPushListProvisionsResult:
def __init__(self, channels):
self.channels = channels
def __str__(self):
return "Push notification enabled on following channels: %s" % ", ".join(
self.channels
)
bdraco-freenub-69809e8/pubnub/models/consumer/signal.py 0000664 0000000 0000000 00000000516 14645232030 0023127 0 ustar 00root root 0000000 0000000 class PNSignalResult:
def __init__(self, result):
"""
Representation of signal server response
:param result: result of signal operation
"""
self.timetoken = result[2]
self._result = result
def __str__(self):
return "Signal success with timetoken %s" % self.timetoken
bdraco-freenub-69809e8/pubnub/models/consumer/time.py 0000664 0000000 0000000 00000001034 14645232030 0022604 0 ustar 00root root 0000000 0000000 from datetime import date
class PNTimeResponse:
MULTIPLIER = 10000000
def __init__(self, server_response):
assert isinstance(server_response, list)
self.server_response = server_response
self.value_as_string = str(server_response[0])
self.value_as_int = server_response[0]
def __int__(self):
return self.value_as_int
def __str__(self):
return self.value_as_string
def date_time(self):
return date.fromtimestamp(self.value_as_int / PNTimeResponse.MULTIPLIER)
bdraco-freenub-69809e8/pubnub/models/consumer/v3/ 0000775 0000000 0000000 00000000000 14645232030 0021626 5 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/pubnub/models/consumer/v3/__init__.py 0000664 0000000 0000000 00000000000 14645232030 0023725 0 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/pubnub/models/consumer/v3/access_manager.py 0000664 0000000 0000000 00000001105 14645232030 0025130 0 ustar 00root root 0000000 0000000 """
Possible responses of PAMv3 request
"""
class _PAMv3Result:
def __init__(self, token):
self.token = token
@classmethod
def from_json(cls, json_input):
return cls(token=json_input["token"])
class PNGrantTokenResult(_PAMv3Result):
def __str__(self):
return "Grant token: %s" % (self.token)
def get_token(self):
return self.token
class PNRevokeTokenResult:
def __init__(self, result):
self.status = result["status"]
def __str__(self):
return "Revoke token success with status: %s" % self.status
bdraco-freenub-69809e8/pubnub/models/consumer/v3/channel.py 0000664 0000000 0000000 00000001666 14645232030 0023621 0 ustar 00root root 0000000 0000000 from pubnub.models.consumer.v3.pn_resource import PNResource
class Channel(PNResource):
def __init__(self, resource_name=None, resource_pattern=None):
super().__init__(resource_name, resource_pattern)
@staticmethod
def id(channel_id):
channel = Channel(resource_name=channel_id)
return channel
@staticmethod
def pattern(channel_pattern):
channel = Channel(resource_pattern=channel_pattern)
return channel
def read(self):
self._read = True
return self
def manage(self):
self._manage = True
return self
def write(self):
self._write = True
return self
def delete(self):
self._delete = True
return self
def get(self):
self._get = True
return self
def update(self):
self._update = True
return self
def join(self):
self._join = True
return self
bdraco-freenub-69809e8/pubnub/models/consumer/v3/group.py 0000664 0000000 0000000 00000001111 14645232030 0023326 0 ustar 00root root 0000000 0000000 from pubnub.models.consumer.v3.pn_resource import PNResource
class Group(PNResource):
def __init__(self, resource_name=None, resource_pattern=None):
super().__init__(resource_name, resource_pattern)
@staticmethod
def id(group_id):
group = Group(resource_name=group_id)
return group
@staticmethod
def pattern(group_pattern):
group = Group(resource_pattern=group_pattern)
return group
def read(self):
self._read = True
return self
def manage(self):
self._manage = True
return self
bdraco-freenub-69809e8/pubnub/models/consumer/v3/pn_resource.py 0000664 0000000 0000000 00000002025 14645232030 0024523 0 ustar 00root root 0000000 0000000 class PNResource:
def __init__(self, resource_name=None, resource_pattern=None):
self._resource_name = resource_name
self._resource_pattern = resource_pattern
self._read = False
self._write = False
self._create = False
self._manage = False
self._delete = False
self._get = False
self._update = False
self._join = False
def is_pattern_resource(self):
return self._resource_pattern is not None
def get_id(self):
if self.is_pattern_resource():
return self._resource_pattern
return self._resource_name
def is_read(self):
return self._read
def is_write(self):
return self._write
def is_create(self):
return self._create
def is_manage(self):
return self._manage
def is_delete(self):
return self._delete
def is_get(self):
return self._get
def is_update(self):
return self._update
def is_join(self):
return self._join
bdraco-freenub-69809e8/pubnub/models/consumer/v3/space.py 0000664 0000000 0000000 00000001747 14645232030 0023304 0 ustar 00root root 0000000 0000000 from pubnub.models.consumer.v3.pn_resource import PNResource
class Space(PNResource):
def __init__(self, resource_name=None, resource_pattern=None):
super().__init__(resource_name, resource_pattern)
@staticmethod
def id(space_id):
space = Space(resource_name=space_id)
return space
@staticmethod
def pattern(space_pattern):
space = Space(resource_pattern=space_pattern)
return space
def read(self):
self._read = True
return self
def write(self):
self._write = True
return self
def create(self):
self._create = True
return self
def manage(self):
self._manage = True
return self
def delete(self):
self._delete = True
return self
def get(self):
self._get = True
return self
def update(self):
self._update = True
return self
def join(self):
self._join = True
return self
bdraco-freenub-69809e8/pubnub/models/consumer/v3/user.py 0000664 0000000 0000000 00000001734 14645232030 0023163 0 ustar 00root root 0000000 0000000 from pubnub.models.consumer.v3.pn_resource import PNResource
class User(PNResource):
def __init__(self, resource_name=None, resource_pattern=None):
super().__init__(resource_name, resource_pattern)
@staticmethod
def id(user_id):
user = User(resource_name=user_id)
return user
@staticmethod
def pattern(user_pattern):
user = User(resource_pattern=user_pattern)
return user
def read(self):
self._read = True
return self
def write(self):
self._write = True
return self
def create(self):
self._create = True
return self
def manage(self):
self._manage = True
return self
def delete(self):
self._delete = True
return self
def get(self):
self._get = True
return self
def update(self):
self._update = True
return self
def join(self):
self._join = True
return self
bdraco-freenub-69809e8/pubnub/models/consumer/v3/uuid.py 0000664 0000000 0000000 00000001203 14645232030 0023142 0 ustar 00root root 0000000 0000000 from pubnub.models.consumer.v3.pn_resource import PNResource
class UUID(PNResource):
def __init__(self, resource_name=None, resource_pattern=None):
super().__init__(resource_name, resource_pattern)
@staticmethod
def id(user_id):
user = UUID(resource_name=user_id)
return user
@staticmethod
def pattern(user_pattern):
user = UUID(resource_pattern=user_pattern)
return user
def delete(self):
self._delete = True
return self
def get(self):
self._get = True
return self
def update(self):
self._update = True
return self
bdraco-freenub-69809e8/pubnub/models/server/ 0000775 0000000 0000000 00000000000 14645232030 0020751 5 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/pubnub/models/server/__init__.py 0000664 0000000 0000000 00000000000 14645232030 0023050 0 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/pubnub/models/server/subscribe.py 0000664 0000000 0000000 00000006602 14645232030 0023310 0 ustar 00root root 0000000 0000000 class SubscribeEnvelope:
def __init__(self, messages=None, metadata=None):
assert isinstance(messages, (list, None))
assert isinstance(metadata, (SubscribeMetadata, None))
self.messages = messages
self.metadata = metadata
@classmethod
def from_json(cls, json_input):
messages = []
for raw_message in json_input["m"]:
messages.append(SubscribeMessage.from_json(raw_message))
metadata = SubscribeMetadata.from_json(json_input["t"])
return SubscribeEnvelope(messages, metadata)
class SubscribeMessage:
def __init__(self):
self.shard = None
self.subscription_match = None
self.channel = None
self.payload = None
self.flags = None
self.issuing_client_id = None
self.subscribe_key = None
self.origination_timetoken = None
self.publish_metadata = None
self.only_channel_subscription = False
self.type = 0
@classmethod
def from_json(cls, json_input):
message = SubscribeMessage()
if "a" in json_input:
message.shard = json_input["a"]
if "b" in json_input:
message.subscription_match = json_input["b"]
message.channel = json_input["c"]
message.payload = json_input["d"]
message.flags = json_input["f"]
if "i" in json_input:
message.issuing_client_id = json_input["i"]
message.subscribe_key = json_input["k"]
if "o" in json_input:
message.origination_timetoken = json_input["o"]
message.publish_metadata = PublishMetadata.from_json(json_input["p"])
if "e" in json_input:
message.type = json_input["e"]
return message
class SubscribeMetadata:
def __init__(self, timetoken=None, region=None):
self.timetoken = timetoken
self.region = region
@classmethod
def from_json(cls, json_input):
assert isinstance(json_input, dict)
assert "r" in json_input
assert "t" in json_input
return SubscribeMetadata(json_input["t"], json_input["r"])
class PresenceEnvelope:
def __init__(self, action, uuid, occupancy, timestamp, data=None):
assert isinstance(action, str)
assert isinstance(occupancy, int)
assert isinstance(timestamp, int)
if data is not None:
assert isinstance(data, dict)
self.action = action
self.uuid = uuid
self.occupancy = occupancy
self.timestamp = timestamp
self.data = data
@classmethod
def extract_value(cls, json, key):
if key in json:
return json[key]
else:
return None
@classmethod
def from_json_payload(cls, json):
return PresenceEnvelope(
action=cls.extract_value(json, "action"),
uuid=cls.extract_value(json, "uuid"),
occupancy=cls.extract_value(json, "occupancy"),
timestamp=cls.extract_value(json, "timestamp"),
data=cls.extract_value(json, "data"),
)
class PublishMetadata:
def __init__(self, publish_timetoken=None, region=None):
self.publish_timetoken = publish_timetoken
self.region = region
@classmethod
def from_json(cls, json_input):
assert "r" in json_input
assert "t" in json_input
return PublishMetadata(int(json_input["t"]), int(json_input["r"]))
bdraco-freenub-69809e8/pubnub/models/subscription_item.py 0000664 0000000 0000000 00000000254 14645232030 0023560 0 ustar 00root root 0000000 0000000 class SubscriptionItem:
def __init__(self, name=None, state=None):
self.name = name
self.state = state
def __str__(self):
return self.name
bdraco-freenub-69809e8/pubnub/pnconfiguration.py 0000664 0000000 0000000 00000011275 14645232030 0021745 0 ustar 00root root 0000000 0000000 from Cryptodome.Cipher import AES
from pubnub.enums import PNHeartbeatNotificationOptions, PNReconnectionPolicy
from pubnub.exceptions import PubNubException
class PNConfiguration:
DEFAULT_PRESENCE_TIMEOUT = 300
DEFAULT_HEARTBEAT_INTERVAL = 280
ALLOWED_AES_MODES = [AES.MODE_CBC, AES.MODE_GCM]
RECONNECTION_INTERVAL = 3
RECONNECTION_MIN_EXPONENTIAL_BACKOFF = 1
RECONNECTION_MAX_EXPONENTIAL_BACKOFF = 32
def __init__(self):
# TODO: add validation
self._uuid = None
self.origin = "ps.pndsn.com"
self.ssl = True
self.non_subscribe_request_timeout = 10
self.subscribe_request_timeout = 310
self.connect_timeout = 10
self.subscribe_key = None
self.publish_key = None
self.secret_key = None
self.cipher_key = None
self._cipher_mode = AES.MODE_CBC
self._fallback_cipher_mode = None
self.auth_key = None
self.filter_expression = None
self.enable_subscribe = True
self.crypto_instance = None
self.file_crypto_instance = None
self.log_verbosity = False
self.enable_presence_heartbeat = False
self.heartbeat_notification_options = PNHeartbeatNotificationOptions.FAILURES
self.reconnect_policy = PNReconnectionPolicy.NONE
self.maximum_reconnection_retries = -1 # -1 means unlimited/ 0 means no retries
self.daemon = False
self.use_random_initialization_vector = True
self.suppress_leave_events = False
self.should_compress = False
self.heartbeat_default_values = True
self._presence_timeout = PNConfiguration.DEFAULT_PRESENCE_TIMEOUT
self._heartbeat_interval = PNConfiguration.DEFAULT_HEARTBEAT_INTERVAL
def validate(self):
PNConfiguration.validate_not_empty_string(self.uuid)
def validate_not_empty_string(value: str):
assert (
value and isinstance(value, str) and value.strip() != ""
), "UUID missing or invalid type"
def scheme(self):
if self.ssl:
return "https"
else:
return "http"
def scheme_extended(self):
return self.scheme() + "://"
def scheme_and_host(self):
return self.scheme_extended() + self.origin
def set_presence_timeout_with_custom_interval(self, timeout, interval):
self.heartbeat_default_values = False
self._presence_timeout = timeout
self._heartbeat_interval = interval
def set_presence_timeout(self, timeout):
self.set_presence_timeout_with_custom_interval(timeout, (timeout / 2) - 1)
@property
def cipher_mode(self):
return self._cipher_mode
@cipher_mode.setter
def cipher_mode(self, cipher_mode):
if cipher_mode not in self.ALLOWED_AES_MODES:
raise PubNubException("Cipher mode not supported")
if cipher_mode is not self._cipher_mode:
self._cipher_mode = cipher_mode
self.crypto_instance = None
@property
def fallback_cipher_mode(self):
return self._fallback_cipher_mode
@fallback_cipher_mode.setter
def fallback_cipher_mode(self, fallback_cipher_mode):
if fallback_cipher_mode not in self.ALLOWED_AES_MODES:
raise PubNubException("Cipher mode not supported")
if fallback_cipher_mode is not self._fallback_cipher_mode:
self._fallback_cipher_mode = fallback_cipher_mode
self.crypto_instance = None
@property
def crypto(self):
if self.crypto_instance is None:
self._init_cryptodome()
return self.crypto_instance
def _init_cryptodome(self):
from .crypto import PubNubCryptodome
self.crypto_instance = PubNubCryptodome(self)
def _init_file_crypto(self):
from .crypto import PubNubFileCrypto
self.file_crypto_instance = PubNubFileCrypto(self)
@property
def file_crypto(self):
if not self.file_crypto_instance:
self._init_file_crypto()
return self.file_crypto_instance
@property
def port(self):
return 443 if self.ssl == "https" else 80
@property
def presence_timeout(self):
return self._presence_timeout
@property
def heartbeat_interval(self):
return self._heartbeat_interval
# TODO: set log level
# TODO: set log level
@property
def uuid(self):
return self._uuid
@uuid.setter
def uuid(self, uuid):
PNConfiguration.validate_not_empty_string(uuid)
self._uuid = uuid
@property
def user_id(self):
return self._uuid
@user_id.setter
def user_id(self, user_id):
PNConfiguration.validate_not_empty_string(user_id)
self._uuid = user_id
bdraco-freenub-69809e8/pubnub/pubnub.py 0000664 0000000 0000000 00000040761 14645232030 0020035 0 ustar 00root root 0000000 0000000 import copy
import logging
import threading
from queue import Empty, Queue
from threading import Event
from . import utils
from .callbacks import ReconnectionCallback, SubscribeCallback
from .endpoints.presence.heartbeat import Heartbeat
from .endpoints.presence.leave import Leave
from .endpoints.pubsub.subscribe import Subscribe
from .enums import (
PNHeartbeatNotificationOptions,
PNOperationType,
PNReconnectionPolicy,
PNStatusCategory,
)
from .managers import (
PublishSequenceManager,
ReconnectionManager,
SubscriptionManager,
TelemetryManager,
)
from .models.consumer.common import PNStatus
from .pnconfiguration import PNConfiguration
from .pubnub_core import PubNubCore
from .request_handlers.base import BaseRequestHandler
from .request_handlers.requests_handler import RequestsRequestHandler
from .structures import PlatformOptions
from .workers import SubscribeMessageWorker
logger = logging.getLogger("pubnub")
class PubNub(PubNubCore):
"""PubNub Python API"""
def __init__(self, config):
assert isinstance(config, PNConfiguration)
PubNubCore.__init__(self, config)
self._request_handler = RequestsRequestHandler(self)
if self.config.enable_subscribe:
self._subscription_manager = NativeSubscriptionManager(self)
self._publish_sequence_manager = PublishSequenceManager(PubNubCore.MAX_SEQUENCE)
self._telemetry_manager = NativeTelemetryManager()
def sdk_platform(self):
return ""
def set_request_handler(self, handler):
assert isinstance(handler, BaseRequestHandler)
self._request_handler = handler
def request_sync(self, endpoint_call_options):
platform_options = PlatformOptions(self.headers, self.config)
self.merge_in_params(endpoint_call_options)
if self.config.log_verbosity:
print(endpoint_call_options)
return self._request_handler.sync_request(
platform_options, endpoint_call_options
)
def request_async(
self, endpoint_name, endpoint_call_options, callback, cancellation_event
):
platform_options = PlatformOptions(self.headers, self.config)
self.merge_in_params(endpoint_call_options)
if self.config.log_verbosity:
print(endpoint_call_options)
return self._request_handler.async_request(
endpoint_name,
platform_options,
endpoint_call_options,
callback,
cancellation_event,
)
def merge_in_params(self, options):
params_to_merge_in = {}
if options.operation_type == PNOperationType.PNPublishOperation:
params_to_merge_in["seqn"] = (
self._publish_sequence_manager.get_next_sequence()
)
options.merge_params_in(params_to_merge_in)
def stop(self):
if self._subscription_manager is not None:
self._subscription_manager.stop()
else:
raise Exception("Subscription manager is not enabled for this instance")
def request_deferred(self, options_func):
raise NotImplementedError
def request_future(self, *args, **kwargs):
raise NotImplementedError
class NativeReconnectionManager(ReconnectionManager):
def __init__(self, pubnub):
super().__init__(pubnub)
def _register_heartbeat_timer(self):
self.stop_heartbeat_timer()
self._recalculate_interval()
self._timer = threading.Timer(self._timer_interval, self._call_time)
self._timer.daemon = True
self._timer.start()
def _call_time(self):
self._pubnub.time().pn_async(self._call_time_callback)
def _call_time_callback(self, resp, status):
if not status.is_error():
self._connection_errors = 1
self.stop_heartbeat_timer()
self._callback.on_reconnect()
logger.debug(
"reconnection manager stop due success time endpoint call: %s"
% utils.datetime_now()
)
elif self._pubnub.config.reconnect_policy == PNReconnectionPolicy.EXPONENTIAL:
logger.debug("reconnect interval increment at: %s" % utils.datetime_now())
self.stop_heartbeat_timer()
self._connection_errors += 1
self._register_heartbeat_timer()
elif self._pubnub.config.reconnect_policy == PNReconnectionPolicy.LINEAR:
self.stop_heartbeat_timer()
self._connection_errors += 1
self._register_heartbeat_timer()
def start_polling(self):
if self._pubnub.config.reconnect_policy == PNReconnectionPolicy.NONE:
logger.warning(
"reconnection policy is disabled, please handle reconnection manually."
)
return
logger.debug("reconnection manager start at: %s" % utils.datetime_now())
self._register_heartbeat_timer()
def stop_heartbeat_timer(self):
if self._timer is not None:
self._timer.cancel()
class NativePublishSequenceManager(PublishSequenceManager):
def __init__(self, provided_max_sequence):
super().__init__(provided_max_sequence)
self._lock = threading.Lock()
def get_next_sequence(self):
with self._lock:
if self.max_sequence == self.next_sequence:
self.next_sequence = 1
else:
self.next_sequence += 1
return self.next_sequence
class NativeSubscriptionManager(SubscriptionManager):
def __init__(self, pubnub_instance):
subscription_manager = self
self._message_queue = Queue()
self._consumer_event = threading.Event()
self._subscribe_call = None
self._heartbeat_periodic_callback = None
self._reconnection_manager = NativeReconnectionManager(pubnub_instance)
super().__init__(pubnub_instance)
self._start_worker()
class NativeReconnectionCallback(ReconnectionCallback):
def on_reconnect(self):
subscription_manager.reconnect()
pn_status = PNStatus()
pn_status.category = PNStatusCategory.PNReconnectedCategory
pn_status.error = False
subscription_manager._subscription_status_announced = True
subscription_manager._listener_manager.announce_status(pn_status)
self._reconnection_listener = NativeReconnectionCallback()
self._reconnection_manager.set_reconnection_listener(
self._reconnection_listener
)
def _send_leave(self, unsubscribe_operation):
def leave_callback(result, status):
self._listener_manager.announce_status(status)
Leave(self._pubnub).channels(unsubscribe_operation.channels).channel_groups(
unsubscribe_operation.channel_groups
).pn_async(leave_callback)
def _register_heartbeat_timer(self):
super()._register_heartbeat_timer()
self._perform_heartbeat_loop()
self._heartbeat_periodic_callback = NativePeriodicCallback(
self._perform_heartbeat_loop, self._pubnub.config.heartbeat_interval
)
if not self._should_stop:
self._heartbeat_periodic_callback.start()
def _perform_heartbeat_loop(self):
if self._heartbeat_call is not None:
# TODO: cancel call
pass
state_payload = self._subscription_state.state_payload()
presence_channels = self._subscription_state.prepare_channel_list(False)
presence_groups = self._subscription_state.prepare_channel_group_list(False)
if len(presence_channels) == 0 and len(presence_groups) == 0:
return
def heartbeat_callback(raw_result, status):
heartbeat_verbosity = self._pubnub.config.heartbeat_notification_options
if status.is_error():
if heartbeat_verbosity in (
PNHeartbeatNotificationOptions.ALL,
PNHeartbeatNotificationOptions.FAILURES,
):
self._listener_manager.announce_status(status)
else:
if heartbeat_verbosity == PNHeartbeatNotificationOptions.ALL:
self._listener_manager.announce_status(status)
try:
(
Heartbeat(self._pubnub)
.channels(presence_channels)
.channel_groups(presence_groups)
.state(state_payload)
.pn_async(heartbeat_callback)
)
except Exception as e:
logger.error("Heartbeat request failed: %s" % e)
def _stop_heartbeat_timer(self):
if self._heartbeat_periodic_callback is not None:
self._heartbeat_periodic_callback.stop()
def _set_consumer_event(self):
self._consumer_event.set()
self._message_queue_put(None)
def _message_queue_put(self, message):
self._message_queue.put(message)
def reconnect(self):
self._should_stop = False
self._start_subscribe_loop()
# Check the instance flag to determine if we want to perform the presence heartbeat
# This is False by default
if self._pubnub.config.enable_presence_heartbeat is True:
self._register_heartbeat_timer()
def disconnect(self):
self._should_stop = True
self._stop_heartbeat_timer()
self._stop_subscribe_loop()
def _start_worker(self):
consumer = NativeSubscribeMessageWorker(
self._pubnub,
self._listener_manager,
self._message_queue,
self._consumer_event,
)
self._consumer_thread = threading.Thread(
target=consumer.run, name="SubscribeMessageWorker"
)
self._consumer_thread.daemon = True
self._consumer_thread.start()
def _start_subscribe_loop(self):
self._stop_subscribe_loop()
combined_channels = self._subscription_state.prepare_channel_list(True)
combined_groups = self._subscription_state.prepare_channel_group_list(True)
if len(combined_channels) == 0 and len(combined_groups) == 0:
return
def callback(raw_result, status):
"""SubscribeEndpoint callback"""
if status.is_error():
if status and status.category == PNStatusCategory.PNCancelledCategory:
return
if (
status.category is PNStatusCategory.PNTimeoutCategory
and not self._should_stop
):
self._start_subscribe_loop()
return
logger.error(
"Exception in subscribe loop: %s" % str(status.error_data.exception)
)
if (
status
and status.category == PNStatusCategory.PNAccessDeniedCategory
):
status.operation = PNOperationType.PNUnsubscribeOperation
self._listener_manager.announce_status(status)
self.unsubscribe_all()
self.disconnect()
return
self._listener_manager.announce_status(status)
self._reconnection_manager.start_polling()
self.disconnect()
else:
self._handle_endpoint_call(raw_result, status)
self._start_subscribe_loop()
try:
self._subscribe_call = (
Subscribe(self._pubnub)
.channels(combined_channels)
.channel_groups(combined_groups)
.timetoken(self._timetoken)
.region(self._region)
.filter_expression(self._pubnub.config.filter_expression)
.pn_async(callback)
)
except Exception as e:
logger.error("Subscribe request failed: %s" % e)
def _stop_subscribe_loop(self):
sc = self._subscribe_call
if sc is not None and not sc.is_executed and not sc.is_canceled:
sc.cancel()
class NativePeriodicCallback:
def __init__(self, callback, callback_time):
self._callback = callback
self._callback_time = callback_time
self._running = False
self._timeout = None
def start(self):
self._running = True
self._schedule_next()
def stop(self):
self._running = False
if self._timeout is not None:
self._timeout.cancel()
self._timeout = None
def _run(self):
if not self._running:
return
try:
self._callback()
except Exception:
# TODO: handle the exception
pass
finally:
self._schedule_next()
def _schedule_next(self):
self._timeout = threading.Timer(self._callback_time, self._run)
self._timer.daemon = True
self._timeout.start()
class NativeSubscribeMessageWorker(SubscribeMessageWorker):
def _take_message(self):
while not self._event.is_set():
try:
# TODO: get rid of 1s timeout
msg = self._queue.get(True, 1)
if msg is not None:
self._process_incoming_payload(msg)
self._queue.task_done()
except Empty:
continue
except Exception as e:
# TODO: move to finally
self._queue.task_done()
self._event.set()
logger.error("take message interrupted: %s" % str(e))
raise
class SubscribeListener(SubscribeCallback):
def __init__(self):
self.connected = False
self.connected_event = Event()
self.disconnected_event = Event()
self.presence_queue = Queue()
self.message_queue = Queue()
self.channel_queue = Queue()
self.uuid_queue = Queue()
self.membership_queue = Queue()
def status(self, pubnub, status):
if utils.is_subscribed_event(status) and not self.connected_event.is_set():
self.connected_event.set()
elif (
utils.is_unsubscribed_event(status) and not self.disconnected_event.is_set()
):
self.disconnected_event.set()
def message(self, pubnub, message):
self.message_queue.put(message)
def presence(self, pubnub, presence):
self.presence_queue.put(presence)
def wait_for_connect(self):
if not self.connected_event.is_set():
self.connected_event.wait()
else:
raise Exception("the instance is already connected")
def channel(self, pubnub, channel):
self.channel_queue.put(channel)
def uuid(self, pubnub, uuid):
self.uuid_queue.put(uuid)
def membership(self, pubnub, membership):
self.membership_queue.put(membership)
def wait_for_disconnect(self):
if not self.disconnected_event.is_set():
self.disconnected_event.wait()
else:
raise Exception("the instance is already disconnected")
def wait_for_message_on(self, *channel_names):
channel_names = list(channel_names)
while True:
env = self.message_queue.get()
self.message_queue.task_done()
if env.channel in channel_names:
return env
else:
continue
def wait_for_presence_on(self, *channel_names):
channel_names = list(channel_names)
while True:
env = self.presence_queue.get()
self.presence_queue.task_done()
if env.channel in channel_names:
return env
else:
continue
class NonSubscribeListener:
def __init__(self):
self.result = None
self.status = None
self.done_event = Event()
def callback(self, result, status):
self.result = result
self.status = status
self.done_event.set()
def pn_await(self, timeout=5):
"""Returns False if a timeout happened, otherwise True"""
return self.done_event.wait(timeout)
def await_result(self, timeout=5):
self.pn_await(timeout)
return self.result
def await_result_and_reset(self, timeout=5):
self.pn_await(timeout)
cp = copy.copy(self.result)
self.reset()
return cp
def reset(self):
self.result = None
self.status = None
self.done_event.clear()
class NativeTelemetryManager(TelemetryManager):
def store_latency(self, latency, operation_type):
super().store_latency(latency, operation_type)
self.clean_up_telemetry_data()
bdraco-freenub-69809e8/pubnub/pubnub_asyncio.py 0000664 0000000 0000000 00000065541 14645232030 0021565 0 ustar 00root root 0000000 0000000 import asyncio
import json
import logging
import math
import time
import urllib
from asyncio import Event, Queue, Semaphore
import aiohttp
from yarl import URL
from pubnub import utils
from pubnub.callbacks import ReconnectionCallback, SubscribeCallback
from pubnub.dtos import SubscribeOperation, UnsubscribeOperation
from pubnub.endpoints.presence.heartbeat import Heartbeat
from pubnub.endpoints.presence.leave import Leave
from pubnub.endpoints.pubsub.subscribe import Subscribe
from pubnub.enums import (
PNHeartbeatNotificationOptions,
PNOperationType,
PNReconnectionPolicy,
PNStatusCategory,
)
from pubnub.errors import (
PNERR_CLIENT_ERROR,
PNERR_CLIENT_TIMEOUT,
PNERR_JSON_DECODING_FAILED,
PNERR_REQUEST_CANCELLED,
PNERR_SERVER_ERROR,
)
from pubnub.event_engine.models import events, states
from pubnub.event_engine.statemachine import StateMachine
from pubnub.managers import (
PublishSequenceManager,
ReconnectionManager,
SubscriptionManager,
TelemetryManager,
)
from pubnub.models.consumer.common import PNStatus
from pubnub.pubnub_core import PubNubCore
from pubnub.structures import RequestOptions, ResponseInfo
from pubnub.workers import SubscribeMessageWorker
from .exceptions import PubNubException
logger = logging.getLogger("pubnub")
class PubNubAsyncio(PubNubCore):
"""
PubNub Python SDK for asyncio framework
"""
def __init__(self, config, custom_event_loop=None, subscription_manager=None):
super().__init__(config)
self.event_loop = custom_event_loop or asyncio.get_event_loop()
self._connector = None
self._session = None
self._connector = aiohttp.TCPConnector(verify_ssl=True, loop=self.event_loop)
if not subscription_manager:
subscription_manager = AsyncioSubscriptionManager
if self.config.enable_subscribe:
self._subscription_manager = subscription_manager(self)
self._publish_sequence_manager = AsyncioPublishSequenceManager(
self.event_loop, PubNubCore.MAX_SEQUENCE
)
self._telemetry_manager = AsyncioTelemetryManager()
def __del__(self):
pass
if self.event_loop.is_running():
self.event_loop.create_task(self.close_session())
async def close_pending_tasks(self, tasks):
await asyncio.gather(*tasks)
await asyncio.sleep(0.1)
async def create_session(self):
self._session = aiohttp.ClientSession(
loop=self.event_loop,
timeout=aiohttp.ClientTimeout(connect=self.config.connect_timeout),
connector=self._connector,
)
async def close_session(self):
if self._session is not None:
await self._session.close()
async def set_connector(self, cn):
await self._session.close()
self._connector = cn
self._session = aiohttp.ClientSession(
loop=self.event_loop,
timeout=aiohttp.ClientTimeout(connect=self.config.connect_timeout),
connector=self._connector,
)
async def stop(self):
await self.close_session()
if self._subscription_manager:
self._subscription_manager.stop()
def sdk_platform(self):
return "-Asyncio"
def request_sync(self, *args):
raise NotImplementedError
def request_deferred(self, *args):
raise NotImplementedError
async def request_result(self, options_func, cancellation_event):
envelope = await self._request_helper(options_func, cancellation_event)
return envelope.result
async def request_future(self, options_func, cancellation_event):
try:
res = await self._request_helper(options_func, cancellation_event)
return res
except PubNubException as e:
return PubNubAsyncioException(result=None, status=e.status)
except asyncio.TimeoutError:
return PubNubAsyncioException(
result=None,
status=options_func().create_status(
PNStatusCategory.PNTimeoutCategory,
None,
None,
exception=PubNubException(pn_error=PNERR_CLIENT_TIMEOUT),
),
)
except asyncio.CancelledError:
return PubNubAsyncioException(
result=None,
status=options_func().create_status(
PNStatusCategory.PNCancelledCategory,
None,
None,
exception=PubNubException(pn_error=PNERR_REQUEST_CANCELLED),
),
)
except Exception as e:
return PubNubAsyncioException(
result=None,
status=options_func().create_status(
PNStatusCategory.PNUnknownCategory, None, None, e
),
)
async def _request_helper(self, options_func, cancellation_event):
"""
Query string should be provided as a manually serialized and encoded string.
:param options_func:
:param cancellation_event:
:return:
"""
if cancellation_event is not None:
assert isinstance(cancellation_event, Event)
options = options_func()
assert isinstance(options, RequestOptions)
create_response = options.create_response
create_status = options.create_status
create_exception = options.create_exception
params_to_merge_in = {}
if options.operation_type == PNOperationType.PNPublishOperation:
params_to_merge_in[
"seqn"
] = await self._publish_sequence_manager.get_next_sequence()
options.merge_params_in(params_to_merge_in)
if options.use_base_path:
url = utils.build_url(
self.config.scheme(),
self.base_origin,
options.path,
options.query_string,
)
else:
url = utils.build_url(
scheme="", origin="", path=options.path, params=options.query_string
)
url = URL(url, encoded=True)
logger.debug(f"{options.method_string} {url} {options.data}")
if options.request_headers:
request_headers = {**self.headers, **options.request_headers}
else:
request_headers = self.headers
try:
if not self._session:
await self.create_session()
start_timestamp = time.time()
response = await asyncio.wait_for(
self._session.request(
options.method_string,
url,
headers=request_headers,
data=options.data if options.data else None,
allow_redirects=options.allow_redirects,
),
options.request_timeout,
)
except (asyncio.TimeoutError, asyncio.CancelledError):
raise
except Exception as e:
logger.error("session.request exception: %s" % str(e))
raise
if not options.non_json_response:
body = await response.text()
else:
if isinstance(response.content, bytes):
body = (
response.content
) # TODO: simplify this logic within the v5 release
else:
body = await response.read()
if cancellation_event is not None and cancellation_event.is_set():
return
response_info = None
status_category = PNStatusCategory.PNUnknownCategory
if response:
request_url = urllib.parse.urlparse(str(response.url))
query = urllib.parse.parse_qs(request_url.query)
uuid = None
auth_key = None
if "uuid" in query and len(query["uuid"]) > 0:
uuid = query["uuid"][0]
if "auth_key" in query and len(query["auth_key"]) > 0:
auth_key = query["auth_key"][0]
response_info = ResponseInfo(
status_code=response.status,
tls_enabled="https" == request_url.scheme,
origin=request_url.hostname,
uuid=uuid,
auth_key=auth_key,
client_request=None,
client_response=response,
)
# if body is not None and len(body) > 0 and not options.non_json_response:
if body is not None and len(body) > 0:
if options.non_json_response:
data = body
else:
try:
data = json.loads(body)
except ValueError:
if response.status == 599 and len(body) > 0:
data = body
else:
raise
except TypeError:
try:
data = json.loads(body.decode("utf-8"))
except ValueError:
raise create_exception(
category=status_category,
response=response,
response_info=response_info,
exception=PubNubException(
pn_error=PNERR_JSON_DECODING_FAILED,
errormsg="json decode error",
),
)
else:
data = "N/A"
logger.debug(data)
if response.status not in (200, 307, 204):
if response.status >= 500:
err = PNERR_SERVER_ERROR
else:
err = PNERR_CLIENT_ERROR
if response.status == 403:
status_category = PNStatusCategory.PNAccessDeniedCategory
if response.status == 400:
status_category = PNStatusCategory.PNBadRequestCategory
raise create_exception(
category=status_category,
response=data,
response_info=response_info,
exception=PubNubException(
errormsg=data, pn_error=err, status_code=response.status
),
)
else:
self._telemetry_manager.store_latency(
time.time() - start_timestamp, options.operation_type
)
return AsyncioEnvelope(
result=create_response(data)
if not options.non_json_response
else create_response(response, data),
status=create_status(
PNStatusCategory.PNAcknowledgmentCategory, data, response_info, None
),
)
class AsyncioReconnectionManager(ReconnectionManager):
def __init__(self, pubnub):
self._task = None
super().__init__(pubnub)
async def _register_heartbeat_timer(self):
while True:
self._recalculate_interval()
await asyncio.sleep(self._timer_interval)
logger.debug("reconnect loop at: %s" % utils.datetime_now())
try:
await self._pubnub.time().future()
self._connection_errors = 1
self._callback.on_reconnect()
break
except Exception:
if (
self._pubnub.config.reconnect_policy
== PNReconnectionPolicy.EXPONENTIAL
):
logger.debug(
"reconnect interval increment at: %s" % utils.datetime_now()
)
self._connection_errors += 1
def start_polling(self):
if self._pubnub.config.reconnect_policy == PNReconnectionPolicy.NONE:
logger.warning(
"reconnection policy is disabled, please handle reconnection manually."
)
return
self._task = asyncio.ensure_future(self._register_heartbeat_timer())
def stop_polling(self):
if self._task is not None and not self._task.cancelled():
self._task.cancel()
class AsyncioPublishSequenceManager(PublishSequenceManager):
def __init__(self, ioloop, provided_max_sequence):
super().__init__(provided_max_sequence)
self._lock = asyncio.Lock()
self._event_loop = ioloop
async def get_next_sequence(self):
async with self._lock:
if self.max_sequence == self.next_sequence:
self.next_sequence = 1
else:
self.next_sequence += 1
return self.next_sequence
class AsyncioSubscriptionManager(SubscriptionManager):
def __init__(self, pubnub_instance):
subscription_manager = self
self._message_worker = None
self._message_queue = Queue()
self._subscription_lock = Semaphore(1)
self._subscribe_loop_task = None
self._heartbeat_periodic_callback = None
self._reconnection_manager = AsyncioReconnectionManager(pubnub_instance)
super().__init__(pubnub_instance)
self._start_worker()
class AsyncioReconnectionCallback(ReconnectionCallback):
def on_reconnect(self):
subscription_manager.reconnect()
pn_status = PNStatus()
pn_status.category = PNStatusCategory.PNReconnectedCategory
pn_status.error = False
subscription_manager._subscription_status_announced = True
subscription_manager._listener_manager.announce_status(pn_status)
self._reconnection_listener = AsyncioReconnectionCallback()
self._reconnection_manager.set_reconnection_listener(
self._reconnection_listener
)
def _set_consumer_event(self):
if not self._message_worker.cancelled():
self._message_worker.cancel()
def _message_queue_put(self, message):
self._message_queue.put_nowait(message)
def _start_worker(self):
consumer = AsyncioSubscribeMessageWorker(
self._pubnub, self._listener_manager, self._message_queue, None
)
self._message_worker = asyncio.ensure_future(
consumer.run(), loop=self._pubnub.event_loop
)
def reconnect(self):
# TODO: method is synchronized in Java
self._should_stop = False
self._subscribe_loop_task = asyncio.ensure_future(self._start_subscribe_loop())
self._register_heartbeat_timer()
def disconnect(self):
# TODO: method is synchronized in Java
self._should_stop = True
self._stop_heartbeat_timer()
self._stop_subscribe_loop()
def stop(self):
super().stop()
self._reconnection_manager.stop_polling()
if self._subscribe_loop_task and not self._subscribe_loop_task.cancelled():
self._subscribe_loop_task.cancel()
async def _start_subscribe_loop(self):
self._stop_subscribe_loop()
await self._subscription_lock.acquire()
combined_channels = self._subscription_state.prepare_channel_list(True)
combined_groups = self._subscription_state.prepare_channel_group_list(True)
if len(combined_channels) == 0 and len(combined_groups) == 0:
self._subscription_lock.release()
return
self._subscribe_request_task = asyncio.ensure_future(
Subscribe(self._pubnub)
.channels(combined_channels)
.channel_groups(combined_groups)
.timetoken(self._timetoken)
.region(self._region)
.filter_expression(self._pubnub.config.filter_expression)
.future()
)
e = await self._subscribe_request_task
if self._subscribe_request_task.cancelled():
self._subscription_lock.release()
return
if e.is_error():
if e.status and e.status.category == PNStatusCategory.PNCancelledCategory:
self._subscription_lock.release()
return
if e.status and e.status.category == PNStatusCategory.PNTimeoutCategory:
asyncio.ensure_future(self._start_subscribe_loop())
self._subscription_lock.release()
return
logger.error("Exception in subscribe loop: %s" % str(e))
if (
e.status
and e.status.category == PNStatusCategory.PNAccessDeniedCategory
):
e.status.operation = PNOperationType.PNUnsubscribeOperation
# TODO: raise error
self._listener_manager.announce_status(e.status)
self._reconnection_manager.start_polling()
self._subscription_lock.release()
self.disconnect()
return
else:
self._handle_endpoint_call(e.result, e.status)
self._subscription_lock.release()
self._subscribe_loop_task = asyncio.ensure_future(
self._start_subscribe_loop()
)
self._subscription_lock.release()
def _stop_subscribe_loop(self):
if (
self._subscribe_request_task is not None
and not self._subscribe_request_task.cancelled()
):
self._subscribe_request_task.cancel()
def _stop_heartbeat_timer(self):
if self._heartbeat_periodic_callback is not None:
self._heartbeat_periodic_callback.stop()
def _register_heartbeat_timer(self):
super()._register_heartbeat_timer()
self._heartbeat_periodic_callback = AsyncioPeriodicCallback(
self._perform_heartbeat_loop,
self._pubnub.config.heartbeat_interval * 1000,
self._pubnub.event_loop,
)
if not self._should_stop:
self._heartbeat_periodic_callback.start()
async def _perform_heartbeat_loop(self):
if self._heartbeat_call is not None:
# TODO: cancel call
pass
cancellation_event = Event()
state_payload = self._subscription_state.state_payload()
presence_channels = self._subscription_state.prepare_channel_list(False)
presence_groups = self._subscription_state.prepare_channel_group_list(False)
if len(presence_channels) == 0 and len(presence_groups) == 0:
return
try:
heartbeat_call = (
Heartbeat(self._pubnub)
.channels(presence_channels)
.channel_groups(presence_groups)
.state(state_payload)
.cancellation_event(cancellation_event)
.future()
)
envelope = await heartbeat_call
heartbeat_verbosity = self._pubnub.config.heartbeat_notification_options
if envelope.status.is_error():
if heartbeat_verbosity in (
PNHeartbeatNotificationOptions.ALL,
PNHeartbeatNotificationOptions.FAILURES,
):
self._listener_manager.announce_status(envelope.status)
else:
if heartbeat_verbosity == PNHeartbeatNotificationOptions.ALL:
self._listener_manager.announce_status(envelope.status)
except PubNubAsyncioException:
pass
# TODO: check correctness
# if e.status is not None and e.status.category == PNStatusCategory.PNTimeoutCategory:
# self._start_subscribe_loop()
# else:
# self._listener_manager.announce_status(e.status)
finally:
cancellation_event.set()
def _send_leave(self, unsubscribe_operation):
asyncio.ensure_future(self._send_leave_helper(unsubscribe_operation))
async def _send_leave_helper(self, unsubscribe_operation):
envelope = (
await Leave(self._pubnub)
.channels(unsubscribe_operation.channels)
.channel_groups(unsubscribe_operation.channel_groups)
.future()
)
self._listener_manager.announce_status(envelope.status)
class EventEngineSubscriptionManager(SubscriptionManager):
event_engine: StateMachine
loop: asyncio.AbstractEventLoop
def __init__(self, pubnub_instance):
self.event_engine = StateMachine(states.UnsubscribedState)
self.event_engine.get_dispatcher().set_pn(pubnub_instance)
self.loop = asyncio.new_event_loop()
super().__init__(pubnub_instance)
def stop(self):
self.event_engine.stop()
def adapt_subscribe_builder(self, subscribe_operation: SubscribeOperation):
if not isinstance(subscribe_operation, SubscribeOperation):
raise PubNubException("Invalid Subscribe Operation")
if subscribe_operation.timetoken:
subscription_event = events.SubscriptionRestoredEvent(
channels=subscribe_operation.channels,
groups=subscribe_operation.channel_groups,
timetoken=subscribe_operation.timetoken,
)
else:
subscription_event = events.SubscriptionChangedEvent(
channels=subscribe_operation.channels,
groups=subscribe_operation.channel_groups,
)
self.event_engine.trigger(subscription_event)
def adapt_unsubscribe_builder(self, unsubscribe_operation):
if not isinstance(unsubscribe_operation, UnsubscribeOperation):
raise PubNubException("Invalid Unsubscribe Operation")
event = events.SubscriptionChangedEvent(None, None)
self.event_engine.trigger(event)
class AsyncioSubscribeMessageWorker(SubscribeMessageWorker):
async def run(self):
await self._take_message()
async def _take_message(self):
while True:
try:
msg = await self._queue.get()
if msg is not None:
self._process_incoming_payload(msg)
self._queue.task_done()
except asyncio.CancelledError:
logger.debug("Message Worker cancelled")
break
except Exception as e:
logger.error("take message interrupted: %s" % str(e))
raise
class AsyncioPeriodicCallback:
def __init__(self, callback, callback_time, event_loop):
self._callback = callback
self._callback_time = callback_time
self._event_loop = event_loop
self._next_timeout = None
self._running = False
self._timeout = None
def start(self):
self._running = True
self._next_timeout = self._event_loop.time()
self._schedule_next()
def stop(self):
self._running = False
if self._timeout is not None:
self._timeout.cancel()
self._timeout = None
def _run(self):
if not self._running:
return
try:
asyncio.ensure_future(self._callback())
except Exception:
raise
finally:
self._schedule_next()
def _schedule_next(self):
current_time = self._event_loop.time()
if self._next_timeout <= current_time:
callback_time_sec = self._callback_time / 1000.0
self._next_timeout += (
math.floor((current_time - self._next_timeout) / callback_time_sec) + 1
) * callback_time_sec
self._timeout = self._event_loop.call_at(self._next_timeout, self._run)
class AsyncioEnvelope:
def __init__(self, result, status):
self.result = result
self.status = status
@staticmethod
def is_error():
return False
class PubNubAsyncioException(Exception):
def __init__(self, result, status):
self.result = result
self.status = status
def __str__(self):
return str(self.status.error_data.exception)
@staticmethod
def is_error():
return True
def value(self):
return self.status.error_data.exception
class SubscribeListener(SubscribeCallback):
def __init__(self):
self.connected = False
self.connected_event = Event()
self.disconnected_event = Event()
self.presence_queue = Queue()
self.message_queue = Queue()
self.error_queue = Queue()
def status(self, pubnub, status):
if utils.is_subscribed_event(status) and not self.connected_event.is_set():
self.connected_event.set()
elif (
utils.is_unsubscribed_event(status) and not self.disconnected_event.is_set()
):
self.disconnected_event.set()
elif status.is_error():
self.error_queue.put_nowait(status.error_data.exception)
def message(self, pubnub, message):
self.message_queue.put_nowait(message)
def presence(self, pubnub, presence):
self.presence_queue.put_nowait(presence)
async def _wait_for(self, coro):
scc_task = asyncio.ensure_future(coro)
err_task = asyncio.ensure_future(self.error_queue.get())
await asyncio.wait([scc_task, err_task], return_when=asyncio.FIRST_COMPLETED)
if err_task.done() and not scc_task.done():
if not scc_task.cancelled():
scc_task.cancel()
raise err_task.result()
else:
if not err_task.cancelled():
err_task.cancel()
return scc_task.result()
async def wait_for_connect(self):
if not self.connected_event.is_set():
await self._wait_for(self.connected_event.wait())
else:
raise Exception("instance is already connected")
async def wait_for_disconnect(self):
if not self.disconnected_event.is_set():
await self._wait_for(self.disconnected_event.wait())
else:
raise Exception("instance is already disconnected")
async def wait_for_message_on(self, *channel_names):
channel_names = list(channel_names)
while True:
try:
env = await self._wait_for(self.message_queue.get())
if env.channel in channel_names:
return env
else:
continue
finally:
self.message_queue.task_done()
async def wait_for_presence_on(self, *channel_names):
channel_names = list(channel_names)
while True:
try:
env = await self._wait_for(self.presence_queue.get())
if env.channel in channel_names:
return env
else:
continue
finally:
self.presence_queue.task_done()
class AsyncioTelemetryManager(TelemetryManager):
def __init__(self):
TelemetryManager.__init__(self)
self.loop = asyncio.get_event_loop()
self._schedule_next_cleanup()
def _schedule_next_cleanup(self):
self._timer = self.loop.call_later(
self.CLEAN_UP_INTERVAL * self.CLEAN_UP_INTERVAL_MULTIPLIER / 1000,
self._clean_up_schedule_next,
)
def _clean_up_schedule_next(self):
self.clean_up_telemetry_data()
self._schedule_next_cleanup()
def _stop_clean_up_timer(self):
self._timer.cancel()
bdraco-freenub-69809e8/pubnub/pubnub_core.py 0000664 0000000 0000000 00000050463 14645232030 0021045 0 ustar 00root root 0000000 0000000 import logging
import time
from abc import ABCMeta, abstractmethod
from pubnub.endpoints.entities.membership.add_memberships import (
AddSpaceMembers,
AddUserSpaces,
)
from pubnub.endpoints.entities.membership.fetch_memberships import (
FetchSpaceMemberships,
FetchUserMemberships,
)
from pubnub.endpoints.entities.membership.remove_memberships import (
RemoveSpaceMembers,
RemoveUserSpaces,
)
from pubnub.endpoints.entities.membership.update_memberships import (
UpdateSpaceMembers,
UpdateUserSpaces,
)
from pubnub.endpoints.entities.space.create_space import CreateSpace
from pubnub.endpoints.entities.space.fetch_space import FetchSpace
from pubnub.endpoints.entities.space.fetch_spaces import FetchSpaces
from pubnub.endpoints.entities.space.remove_space import RemoveSpace
from pubnub.endpoints.entities.space.update_space import UpdateSpace
from pubnub.endpoints.entities.user.create_user import CreateUser
from pubnub.endpoints.entities.user.fetch_user import FetchUser
from pubnub.endpoints.entities.user.fetch_users import FetchUsers
from pubnub.endpoints.entities.user.remove_user import RemoveUser
from pubnub.endpoints.entities.user.update_user import UpdateUser
from pubnub.errors import PNERR_MISUSE_OF_USER_AND_SPACE, PNERR_USER_SPACE_PAIRS_MISSING
from pubnub.exceptions import PubNubException
from pubnub.features import feature_flag
from .builders import SubscribeBuilder, UnsubscribeBuilder
from .endpoints.access.audit import Audit
from .endpoints.access.grant import Grant
from .endpoints.access.grant_token import GrantToken
from .endpoints.access.revoke_token import RevokeToken
from .endpoints.channel_groups.add_channel_to_channel_group import (
AddChannelToChannelGroup,
)
from .endpoints.channel_groups.list_channels_in_channel_group import (
ListChannelsInChannelGroup,
)
from .endpoints.channel_groups.remove_channel_from_channel_group import (
RemoveChannelFromChannelGroup,
)
from .endpoints.channel_groups.remove_channel_group import RemoveChannelGroup
from .endpoints.fetch_messages import FetchMessages
from .endpoints.file_operations.delete_file import DeleteFile
from .endpoints.file_operations.download_file import DownloadFileNative
from .endpoints.file_operations.fetch_upload_details import FetchFileUploadS3Data
from .endpoints.file_operations.get_file_url import GetFileDownloadUrl
from .endpoints.file_operations.list_files import ListFiles
from .endpoints.file_operations.publish_file_message import PublishFileMessage
from .endpoints.file_operations.send_file import SendFileNative
from .endpoints.history import History
from .endpoints.history_delete import HistoryDelete
from .endpoints.message_actions.add_message_action import AddMessageAction
from .endpoints.message_actions.get_message_actions import GetMessageActions
from .endpoints.message_actions.remove_message_action import RemoveMessageAction
from .endpoints.message_count import MessageCount
from .endpoints.objects_v2.channel.get_all_channels import GetAllChannels
from .endpoints.objects_v2.channel.get_channel import GetChannel
from .endpoints.objects_v2.channel.remove_channel import RemoveChannel
from .endpoints.objects_v2.channel.set_channel import SetChannel
from .endpoints.objects_v2.members.get_channel_members import GetChannelMembers
from .endpoints.objects_v2.members.manage_channel_members import ManageChannelMembers
from .endpoints.objects_v2.members.remove_channel_members import RemoveChannelMembers
from .endpoints.objects_v2.members.set_channel_members import SetChannelMembers
from .endpoints.objects_v2.memberships.get_memberships import GetMemberships
from .endpoints.objects_v2.memberships.manage_memberships import ManageMemberships
from .endpoints.objects_v2.memberships.remove_memberships import RemoveMemberships
from .endpoints.objects_v2.memberships.set_memberships import SetMemberships
from .endpoints.objects_v2.uuid.get_all_uuid import GetAllUuid
from .endpoints.objects_v2.uuid.get_uuid import GetUuid
from .endpoints.objects_v2.uuid.remove_uuid import RemoveUuid
from .endpoints.objects_v2.uuid.set_uuid import SetUuid
from .endpoints.presence.get_state import GetState
from .endpoints.presence.heartbeat import Heartbeat
from .endpoints.presence.here_now import HereNow
from .endpoints.presence.set_state import SetState
from .endpoints.presence.where_now import WhereNow
from .endpoints.pubsub.fire import Fire
from .endpoints.pubsub.publish import Publish
from .endpoints.push.add_channels_to_push import AddChannelsToPush
from .endpoints.push.list_push_provisions import ListPushProvisions
from .endpoints.push.remove_channels_from_push import RemoveChannelsFromPush
from .endpoints.push.remove_device import RemoveDeviceFromPush
from .endpoints.signal import Signal
from .endpoints.time import Time
from .managers import BasePathManager, TelemetryManager, TokenManager
logger = logging.getLogger("pubnub")
class PubNubCore:
"""A base class for PubNub Python API implementations"""
SDK_VERSION = "7.2.0"
SDK_NAME = "PubNub-Python"
TIMESTAMP_DIVIDER = 1000
MAX_SEQUENCE = 65535
__metaclass__ = ABCMeta
_plugins = []
def __init__(self, config):
self.config = config
self.config.validate()
self.headers = {"User-Agent": self.sdk_name}
self._subscription_manager = None
self._publish_sequence_manager = None
self._telemetry_manager = TelemetryManager()
self._base_path_manager = BasePathManager(config)
self._token_manager = TokenManager()
@property
def base_origin(self):
return self._base_path_manager.get_base_path()
@property
def sdk_name(self):
return f"{PubNubCore.SDK_NAME}{self.sdk_platform()}/{PubNubCore.SDK_VERSION}"
@abstractmethod
def sdk_platform(self):
pass
@property
def uuid(self):
return self.config.uuid
def add_listener(self, listener):
self._validate_subscribe_manager_enabled()
return self._subscription_manager.add_listener(listener)
def remove_listener(self, listener):
self._validate_subscribe_manager_enabled()
return self._subscription_manager.remove_listener(listener)
def get_subscribed_channels(self):
self._validate_subscribe_manager_enabled()
return self._subscription_manager.get_subscribed_channels()
def get_subscribed_channel_groups(self):
self._validate_subscribe_manager_enabled()
return self._subscription_manager.get_subscribed_channel_groups()
def add_channel_to_channel_group(self):
return AddChannelToChannelGroup(self)
def remove_channel_from_channel_group(self):
return RemoveChannelFromChannelGroup(self)
def list_channels_in_channel_group(self):
return ListChannelsInChannelGroup(self)
def remove_channel_group(self):
return RemoveChannelGroup(self)
def subscribe(self):
return SubscribeBuilder(self._subscription_manager)
def unsubscribe(self):
return UnsubscribeBuilder(self._subscription_manager)
def unsubscribe_all(self):
return self._subscription_manager.unsubscribe_all()
def reconnect(self):
return self._subscription_manager.reconnect()
def heartbeat(self):
return Heartbeat(self)
def set_state(self):
return SetState(self, self._subscription_manager)
def get_state(self):
return GetState(self)
def here_now(self):
return HereNow(self)
def where_now(self):
return WhereNow(self)
def publish(self):
return Publish(self)
def grant(self):
return Grant(self)
def grant_token(self):
return GrantToken(self)
def revoke_token(self, token):
return RevokeToken(self, token)
def audit(self):
return Audit(self)
# Push Related methods
def list_push_channels(self):
return ListPushProvisions(self)
def add_channels_to_push(self):
return AddChannelsToPush(self)
def remove_channels_from_push(self):
return RemoveChannelsFromPush(self)
def remove_device_from_push(self):
return RemoveDeviceFromPush(self)
def history(self):
return History(self)
def message_counts(self):
return MessageCount(self)
def fire(self):
return Fire(self)
def signal(self):
return Signal(self)
def set_uuid_metadata(self):
return SetUuid(self)
def get_uuid_metadata(self):
return GetUuid(self)
def remove_uuid_metadata(self):
return RemoveUuid(self)
def get_all_uuid_metadata(self):
return GetAllUuid(self)
def set_channel_metadata(self):
return SetChannel(self)
def get_channel_metadata(self):
return GetChannel(self)
def remove_channel_metadata(self):
return RemoveChannel(self)
def get_all_channel_metadata(self):
return GetAllChannels(self)
def set_channel_members(self):
return SetChannelMembers(self)
def get_channel_members(self):
return GetChannelMembers(self)
def remove_channel_members(self):
return RemoveChannelMembers(self)
def manage_channel_members(self):
return ManageChannelMembers(self)
def set_memberships(self):
return SetMemberships(self)
def get_memberships(self):
return GetMemberships(self)
def manage_memberships(self):
return ManageMemberships(self)
def fetch_messages(self):
return FetchMessages(self)
def add_message_action(self):
return AddMessageAction(self)
def get_message_actions(self):
return GetMessageActions(self)
def remove_message_action(self):
return RemoveMessageAction(self)
def time(self):
return Time(self)
def delete_messages(self):
return HistoryDelete(self)
def parse_token(self, token):
return self._token_manager.parse_token(token)
def set_token(self, token):
self._token_manager.set_token(token)
def _get_token(self):
return self._token_manager.get_token()
def send_file(self):
if not self.sdk_platform():
return SendFileNative(self)
elif "Asyncio" in self.sdk_platform():
from .endpoints.file_operations.send_file_asyncio import AsyncioSendFile
return AsyncioSendFile(self)
else:
raise NotImplementedError
def download_file(self):
if not self.sdk_platform():
return DownloadFileNative(self)
elif "Asyncio" in self.sdk_platform():
from .endpoints.file_operations.download_file_asyncio import (
DownloadFileAsyncio,
)
return DownloadFileAsyncio(self)
else:
raise NotImplementedError
def list_files(self):
return ListFiles(self)
def get_file_url(self):
return GetFileDownloadUrl(self)
def delete_file(self):
return DeleteFile(self)
def _fetch_file_upload_s3_data(self):
return FetchFileUploadS3Data(self)
def publish_file_message(self):
return PublishFileMessage(self)
def decrypt(self, cipher_key, file):
return self.config.file_crypto.decrypt(cipher_key, file)
def encrypt(self, cipher_key, file):
return self.config.file_crypto.encrypt(cipher_key, file)
@staticmethod
def timestamp():
return int(time.time())
def _validate_subscribe_manager_enabled(self):
if self._subscription_manager is None:
raise Exception("Subscription manager is not enabled for this instance")
""" Entities code -- all of methods bellow should be decorated with pubnub.features.feature_flag """
@feature_flag("PN_ENABLE_ENTITIES")
def create_space(
self,
space_id,
name=None,
description=None,
custom=None,
space_type=None,
space_status=None,
sync=None,
):
space = CreateSpace(self).space_id(space_id)
if name is not None:
space.set_name(name)
if description is not None:
space.description(description)
if custom is not None:
space.custom(custom)
if space_status is not None:
space.space_status(space_status)
if space_type is not None:
space.space_type(space_type)
if sync:
return space.sync()
return space
@feature_flag("PN_ENABLE_ENTITIES")
def update_space(
self,
space_id,
name=None,
description=None,
custom=None,
space_type=None,
space_status=None,
sync=None,
):
space = UpdateSpace(self).space_id(space_id)
if name is not None:
space.set_name(name)
if description is not None:
space.description(description)
if custom is not None:
space.custom(custom)
if space_status is not None:
space.space_status(space_status)
if space_type is not None:
space.space_type(space_type)
if sync:
return space.sync()
return space
@feature_flag("PN_ENABLE_ENTITIES")
def remove_space(self, space_id, sync=None):
remove_space = RemoveSpace(self).space_id(space_id)
if sync:
return remove_space.sync()
return remove_space
@feature_flag("PN_ENABLE_ENTITIES")
def fetch_space(self, space_id, include_custom=None, sync=None):
space = FetchSpace(self).space_id(space_id)
if include_custom is not None:
space.include_custom(include_custom)
if sync:
return space.sync()
return space
@feature_flag("PN_ENABLE_ENTITIES")
def fetch_spaces(
self,
limit=None,
page=None,
filter=None,
sort=None,
include_total_count=None,
include_custom=None,
sync=None,
):
spaces = FetchSpaces(self)
if limit is not None:
spaces.limit(limit)
if page is not None:
spaces.page(page)
if filter is not None:
spaces.filter(filter)
if sort is not None:
spaces.sort(sort)
if include_total_count is not None:
spaces.include_total_count(include_total_count)
if include_custom is not None:
spaces.include_custom(include_custom)
if sync:
return spaces.sync()
return spaces
@feature_flag("PN_ENABLE_ENTITIES")
def create_user(
self,
user_id,
name=None,
email=None,
custom=None,
user_type=None,
user_status=None,
sync=None,
):
user = CreateUser(self).user_id(user_id)
if name is not None:
user.set_name(name)
if email is not None:
user.email(email)
if custom is not None:
user.custom(custom)
if user_status is not None:
user.user_status(user_status)
if user_type is not None:
user.user_type(user_type)
if sync:
return user.sync()
return user
@feature_flag("PN_ENABLE_ENTITIES")
def update_user(
self,
user_id,
name=None,
email=None,
custom=None,
user_type=None,
user_status=None,
sync=None,
):
user = UpdateUser(self).user_id(user_id)
if name is not None:
user.set_name(name)
if email is not None:
user.email(email)
if custom is not None:
user.custom(custom)
if user_status is not None:
user.user_status(user_status)
if user_type is not None:
user.user_type(user_type)
if sync:
return user.sync()
return user
@feature_flag("PN_ENABLE_ENTITIES")
def remove_user(self, user_id, sync=None):
user = RemoveUser(self).user_id(user_id)
if sync:
return user.sync()
return user
@feature_flag("PN_ENABLE_ENTITIES")
def fetch_user(self, user_id, include_custom=None, sync=None):
user = FetchUser(self).user_id(user_id)
if include_custom is not None:
user.include_custom(include_custom)
if sync:
return user.sync()
return user
@feature_flag("PN_ENABLE_ENTITIES")
def fetch_users(
self,
limit=None,
page=None,
filter=None,
sort=None,
include_total_count=None,
include_custom=None,
sync=None,
):
users = FetchUsers(self)
if limit is not None:
users.limit(limit)
if page is not None:
users.page(page)
if filter is not None:
users.filter(filter)
if sort is not None:
users.sort(sort)
if include_total_count is not None:
users.include_total_count(include_total_count)
if include_custom is not None:
users.include_custom(include_custom)
if sync:
return users.sync()
return users
@feature_flag("PN_ENABLE_ENTITIES")
def add_memberships(
self,
user_id: str = None,
users: list = None,
space_id: str = None,
spaces: list = None,
sync=None,
):
if user_id and space_id:
raise (PubNubException(pn_error=PNERR_MISUSE_OF_USER_AND_SPACE))
if user_id and spaces:
membership = AddUserSpaces(self).user_id(user_id).spaces(spaces)
elif space_id and users:
membership = AddSpaceMembers(self).space_id(space_id).users(users)
else:
raise (PubNubException(pn_error=PNERR_USER_SPACE_PAIRS_MISSING))
if sync:
return membership.sync()
return membership
@feature_flag("PN_ENABLE_ENTITIES")
def update_memberships(
self,
user_id: str = None,
users: list = None,
space_id: str = None,
spaces: list = None,
sync=None,
):
if user_id and space_id:
raise (PubNubException(pn_error=PNERR_MISUSE_OF_USER_AND_SPACE))
if user_id and spaces:
membership = UpdateUserSpaces(self).user_id(user_id).spaces(spaces)
elif space_id and users:
membership = UpdateSpaceMembers(self).space_id(space_id).users(users)
else:
raise (PubNubException(pn_error=PNERR_USER_SPACE_PAIRS_MISSING))
if sync:
return membership.sync()
return membership
def remove_memberships(self, **kwargs):
if len(kwargs) == 0:
return RemoveMemberships(self)
if "user_id" in kwargs.keys() and "space_id" in kwargs.keys():
raise (PubNubException(pn_error=PNERR_MISUSE_OF_USER_AND_SPACE))
if kwargs["user_id"] and kwargs["spaces"]:
membership = (
RemoveUserSpaces(self)
.user_id(kwargs["user_id"])
.spaces(kwargs["spaces"])
)
elif kwargs["space_id"] and kwargs["users"]:
membership = (
RemoveSpaceMembers(self)
.space_id(kwargs["space_id"])
.users(kwargs["users"])
)
else:
raise (PubNubException(pn_error=PNERR_USER_SPACE_PAIRS_MISSING))
if kwargs["sync"]:
return membership.sync()
return membership
@feature_flag("PN_ENABLE_ENTITIES")
def fetch_memberships(
self,
user_id: str = None,
space_id: str = None,
limit=None,
page=None,
filter=None,
sort=None,
include_total_count=None,
include_custom=None,
sync=None,
):
if user_id and space_id:
raise (PubNubException(pn_error=PNERR_MISUSE_OF_USER_AND_SPACE))
if user_id:
memberships = FetchUserMemberships(self).user_id(user_id)
elif space_id:
memberships = FetchSpaceMemberships(self).space_id(space_id)
else:
raise (PubNubException(pn_error=PNERR_USER_SPACE_PAIRS_MISSING))
if limit:
memberships.limit(limit)
if page:
memberships.page(page)
if filter:
memberships.filter(filter)
if sort:
memberships.sort(sort)
if include_total_count:
memberships.include_total_count(include_total_count)
if include_custom:
memberships.include_custom(include_custom)
if sync:
return memberships.sync()
return memberships
bdraco-freenub-69809e8/pubnub/request_handlers/ 0000775 0000000 0000000 00000000000 14645232030 0021530 5 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/pubnub/request_handlers/__init__.py 0000664 0000000 0000000 00000000000 14645232030 0023627 0 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/pubnub/request_handlers/base.py 0000664 0000000 0000000 00000000623 14645232030 0023015 0 ustar 00root root 0000000 0000000 from abc import ABCMeta, abstractmethod
class BaseRequestHandler:
__metaclass__ = ABCMeta
@abstractmethod
def sync_request(self, platform_options, endpoint_call_options):
pass
@abstractmethod
def async_request(
self,
endpoint_name,
platform_options,
endpoint_call_options,
callback,
cancellation_event,
):
pass
bdraco-freenub-69809e8/pubnub/request_handlers/requests_handler.py 0000664 0000000 0000000 00000027633 14645232030 0025465 0 ustar 00root root 0000000 0000000 import json # noqa # pylint: disable=W0611
import logging
import threading
import urllib
import requests
from requests import Session
from requests.adapters import HTTPAdapter
from pubnub import utils
from pubnub.enums import PNStatusCategory
from pubnub.errors import (
PNERR_CLIENT_ERROR,
PNERR_CLIENT_TIMEOUT,
PNERR_CONNECTION_ERROR,
PNERR_HTTP_ERROR,
PNERR_SERVER_ERROR,
PNERR_TOO_MANY_REDIRECTS_ERROR,
PNERR_UNKNOWN_ERROR,
)
from pubnub.exceptions import PubNubException
from pubnub.request_handlers.base import BaseRequestHandler
from pubnub.structures import Envelope, PlatformOptions, RequestOptions, ResponseInfo
try:
from json.decoder import JSONDecodeError
except ImportError:
JSONDecodeError = ValueError
logger = logging.getLogger("pubnub")
class RequestsRequestHandler(BaseRequestHandler):
"""PubNub Python SDK Native requests handler based on `requests` HTTP library."""
ENDPOINT_THREAD_COUNTER = 0
def __init__(self, pubnub):
self.session = Session()
self.session.mount(
"http://%s" % pubnub.config.origin,
HTTPAdapter(max_retries=1, pool_maxsize=500),
)
self.session.mount(
"https://%s" % pubnub.config.origin,
HTTPAdapter(max_retries=1, pool_maxsize=500),
)
self.session.mount(
"http://%s/v2/subscribe" % pubnub.config.origin,
HTTPAdapter(pool_maxsize=500),
)
self.session.mount(
"https://%s/v2/subscribe" % pubnub.config.origin,
HTTPAdapter(pool_maxsize=500),
)
self.pubnub = pubnub
def sync_request(self, platform_options, endpoint_call_options):
return self._build_envelope(platform_options, endpoint_call_options)
def async_request(
self,
endpoint_name,
platform_options,
endpoint_call_options,
callback,
cancellation_event,
):
call = Call()
if cancellation_event is None:
cancellation_event = threading.Event()
def callback_to_invoke_in_separate_thread():
try:
envelope = self._build_envelope(platform_options, endpoint_call_options)
if cancellation_event is not None and cancellation_event.is_set():
# Since there are no way to affect on ongoing request it's response will
# be just ignored on cancel call
return
callback(envelope)
except PubNubException as e:
logger.error("Async request PubNubException. %s" % str(e))
callback(
Envelope(
result=None,
status=endpoint_call_options.create_status(
category=PNStatusCategory.PNBadRequestCategory,
response=None,
response_info=None,
exception=e,
),
)
)
except Exception as e:
logger.error("Async request Exception. %s" % str(e))
callback(
Envelope(
result=None,
status=endpoint_call_options.create_status(
category=PNStatusCategory.PNInternalExceptionCategory,
response=None,
response_info=None,
exception=e,
),
)
)
finally:
call.executed_cb()
self.execute_callback_in_separate_thread(
callback_to_invoke_in_separate_thread,
endpoint_name,
call,
cancellation_event,
)
def execute_callback_in_separate_thread(
self,
callback_to_invoke_in_another_thread,
operation_name,
call_obj,
cancellation_event,
):
client = AsyncHTTPClient(callback_to_invoke_in_another_thread)
thread = threading.Thread(
target=client.run,
name="Thread-%s-%d"
% (operation_name, ++RequestsRequestHandler.ENDPOINT_THREAD_COUNTER),
)
thread.daemon = self.pubnub.config.daemon
thread.start()
call_obj.thread = thread
call_obj.cancellation_event = cancellation_event
return call_obj
def async_file_based_operation(
self, func, callback, operation_name, cancellation_event=None
):
call = Call()
if cancellation_event is None:
cancellation_event = threading.Event()
def callback_to_invoke_in_separate_thread():
try:
envelope = func()
callback(envelope.result, envelope.status)
except Exception as e:
logger.error("Async file upload request Exception. %s" % str(e))
callback(Envelope(result=None, status=e))
finally:
call.executed_cb()
self.execute_callback_in_separate_thread(
callback_to_invoke_in_separate_thread,
operation_name,
call,
cancellation_event,
)
return call
def _build_envelope(self, p_options, e_options):
"""A wrapper for _invoke_url to separate request logic"""
status_category = PNStatusCategory.PNUnknownCategory
response_info = None
url_base_path = self.pubnub.base_origin if e_options.use_base_path else None
try:
res = self._invoke_request(p_options, e_options, url_base_path)
except PubNubException as e:
if e._pn_error is PNERR_CONNECTION_ERROR:
status_category = PNStatusCategory.PNUnexpectedDisconnectCategory
elif e._pn_error is PNERR_CLIENT_TIMEOUT:
status_category = PNStatusCategory.PNTimeoutCategory
return Envelope(
result=None,
status=e_options.create_status(
category=status_category,
response=None,
response_info=response_info,
exception=e,
),
)
if res is not None:
url = urllib.parse.urlparse(res.url)
query = urllib.parse.parse_qs(url.query)
uuid = None
auth_key = None
if "uuid" in query and len(query["uuid"]) > 0:
uuid = query["uuid"][0]
if "auth_key" in query and len(query["auth_key"]) > 0:
auth_key = query["auth_key"][0]
response_info = ResponseInfo(
status_code=res.status_code,
tls_enabled="https" == url.scheme,
origin=url.hostname,
uuid=uuid,
auth_key=auth_key,
client_request=res.request,
)
if not res.ok:
if res.status_code == 403:
status_category = PNStatusCategory.PNAccessDeniedCategory
if res.status_code == 400:
status_category = PNStatusCategory.PNBadRequestCategory
if res.text is None:
text = "N/A"
else:
text = res.text
if res.status_code >= 500:
err = PNERR_SERVER_ERROR
else:
err = PNERR_CLIENT_ERROR
try:
response = res.json()
except JSONDecodeError:
response = None
return Envelope(
result=None,
status=e_options.create_status(
category=status_category,
response=response,
response_info=response_info,
exception=PubNubException(
pn_error=err, errormsg=text, status_code=res.status_code
),
),
)
else:
if e_options.non_json_response:
response = res
else:
response = res.json()
return Envelope(
result=e_options.create_response(response),
status=e_options.create_status(
category=PNStatusCategory.PNAcknowledgmentCategory,
response=response,
response_info=response_info,
exception=None,
),
)
def _invoke_request(self, p_options, e_options, base_origin):
assert isinstance(p_options, PlatformOptions)
assert isinstance(e_options, RequestOptions)
if base_origin:
url = p_options.pn_config.scheme() + "://" + base_origin + e_options.path
else:
url = e_options.path
if e_options.request_headers:
request_headers = {**p_options.headers, **e_options.request_headers}
else:
request_headers = p_options.headers
args = {
"method": e_options.method_string,
"headers": request_headers,
"url": url,
"params": e_options.query_string,
"timeout": (e_options.connect_timeout, e_options.request_timeout),
"allow_redirects": e_options.allow_redirects,
}
if e_options.is_post() or e_options.is_patch():
args["data"] = e_options.data
args["files"] = e_options.files
logger.debug(
"%s %s %s"
% (
e_options.method_string,
utils.build_url(
p_options.pn_config.scheme(),
base_origin,
e_options.path,
e_options.query_string,
),
e_options.data,
)
)
else:
logger.debug(
"%s %s"
% (
e_options.method_string,
utils.build_url(
p_options.pn_config.scheme(),
base_origin,
e_options.path,
e_options.query_string,
),
)
)
try:
res = self.session.request(**args)
logger.debug("GOT %s" % res.text)
except requests.exceptions.ConnectionError as e:
raise PubNubException(pn_error=PNERR_CONNECTION_ERROR, errormsg=str(e))
except requests.exceptions.HTTPError as e:
raise PubNubException(pn_error=PNERR_HTTP_ERROR, errormsg=str(e))
except requests.exceptions.Timeout as e:
raise PubNubException(pn_error=PNERR_CLIENT_TIMEOUT, errormsg=str(e))
except requests.exceptions.TooManyRedirects as e:
raise PubNubException(
pn_error=PNERR_TOO_MANY_REDIRECTS_ERROR, errormsg=str(e)
)
except Exception as e:
raise PubNubException(pn_error=PNERR_UNKNOWN_ERROR, errormsg=str(e))
return res
class AsyncHTTPClient:
"""A wrapper for threaded calls"""
def __init__(self, callback_to_invoke):
self._callback_to_invoke = callback_to_invoke
def run(self):
self._callback_to_invoke()
class Call:
"""
A platform dependent representation of async PubNub method call
"""
def __init__(self):
self.thread = None
self.cancellation_event = None
self.is_executed = False
self.is_canceled = False
def cancel(self):
"""
Set Event flag to stop thread on timeout. This will not stop thread immediately, it will stopped
only after ongoing request will be finished
:return: nothing
"""
if self.cancellation_event is not None:
self.cancellation_event.set()
self.is_canceled = True
def join(self):
if isinstance(self.thread, threading.Thread):
self.thread.join()
def executed_cb(self):
self.is_executed = True
bdraco-freenub-69809e8/pubnub/structures.py 0000664 0000000 0000000 00000006237 14645232030 0020765 0 ustar 00root root 0000000 0000000 from .enums import HttpMethod
class RequestOptions:
def __init__(
self,
path,
params_callback,
method,
request_timeout,
connect_timeout,
create_response,
create_status,
create_exception,
operation_type,
data=None,
sort_arguments=False,
allow_redirects=True,
use_base_path=None,
files=None,
request_headers=None,
non_json_response=False,
):
assert len(path) > 0
assert callable(params_callback)
assert isinstance(method, int)
assert isinstance(request_timeout, int)
assert isinstance(connect_timeout, int)
if not (
method is HttpMethod.GET
or method is HttpMethod.POST
or method is HttpMethod.DELETE
or method is HttpMethod.PATCH
):
raise AssertionError()
self.params = None
self.path = path
self.params_callback = params_callback
self._method = method
self.request_timeout = request_timeout
self.connect_timeout = connect_timeout
# TODO: rename 'data' => 'body'
self.data = data
self.files = files
self.body = data
self.sort_params = sort_arguments
self.create_response = create_response
self.create_status = create_status
self.create_exception = create_exception
self.operation_type = operation_type
self.allow_redirects = allow_redirects
self.use_base_path = use_base_path
self.request_headers = request_headers
self.non_json_response = non_json_response
def merge_params_in(self, params_to_merge_in):
self.params = self.params_callback(params_to_merge_in)
@property
def method_string(self):
return HttpMethod.string(self._method)
def is_post(self):
return self._method is HttpMethod.POST
def is_patch(self):
return self._method is HttpMethod.PATCH
def query_list(self):
"""All query keys and values should be already encoded inside a build_params() method"""
s = []
for k, v in self.params.items():
s.append(str(k) + "=" + str(v))
if self.sort_params:
return sorted(s)
else:
return s
@property
def query_string(self):
return str("&".join(self.query_list()))
def __str__(self):
return f"path: {self.path}, qs: {self.query_string}"
class PlatformOptions:
def __init__(self, headers, pn_config):
self.headers = headers
self.pn_config = pn_config
class ResponseInfo:
def __init__(
self,
status_code,
tls_enabled,
origin,
uuid,
auth_key,
client_request,
client_response=None,
):
self.status_code = status_code
self.tls_enabled = tls_enabled
self.origin = origin
self.uuid = uuid
self.auth_key = auth_key
self.client_request = client_request
self.client_response = client_response
class Envelope:
def __init__(self, result, status):
self.result = result
self.status = status
bdraco-freenub-69809e8/pubnub/utils.py 0000664 0000000 0000000 00000020304 14645232030 0017671 0 ustar 00root root 0000000 0000000 import datetime
import hmac
import json
import threading
import urllib
import uuid as u
from hashlib import sha256
from .enums import (
HttpMethod,
PAMPermissions,
PNOperationType,
PNPushType,
PNStatusCategory,
)
from .errors import PNERR_JSON_NOT_SERIALIZABLE
from .exceptions import PubNubException
from .models.consumer.common import PNStatus
def get_data_for_user(data):
try:
if "message" in data and "payload" in data:
return {"message": data["message"], "payload": data["payload"]}
else:
return data
except TypeError:
return data
def write_value_as_string(data):
try:
return json.dumps(data)
except TypeError:
raise PubNubException(pn_error=PNERR_JSON_NOT_SERIALIZABLE)
def url_encode(data):
return urllib.parse.quote(data, safe="~").replace("+", "%2B")
def url_write(data):
"""Just wraps url_encode(write_value_as_string())"""
return url_encode(write_value_as_string(data))
def uuid():
return str(u.uuid4())
def split_items(items_string):
if len(items_string) == 0:
return []
else:
return items_string.split(",")
def join_items(items_list):
return ",".join(items_list)
def join_items_and_encode(items_list):
return ",".join(url_encode(x) for x in items_list)
def join_channels(items_list):
if len(items_list) == 0:
return ","
else:
return join_items_and_encode(items_list)
def extend_list(existing_items, new_items):
if isinstance(new_items, str):
existing_items.extend(split_items(new_items))
else:
existing_items.extend(new_items)
def build_url(scheme, origin, path, params={}):
return urllib.parse.urlunsplit((scheme, origin, path, params, ""))
def synchronized(func):
func.__lock__ = threading.Lock()
def synced_func(*args, **kws):
with func.__lock__:
return func(*args, **kws)
return synced_func
def is_subscribed_event(status):
assert isinstance(status, PNStatus)
return status.category == PNStatusCategory.PNConnectedCategory
def is_unsubscribed_event(status):
assert isinstance(status, PNStatus)
return (
status.category == PNStatusCategory.PNAcknowledgmentCategory
and status.operation == PNOperationType.PNUnsubscribeOperation
)
def prepare_pam_arguments(unsorted_params):
sorted_keys = sorted(unsorted_params)
stringified_arguments = ""
i = 0
for key in sorted_keys:
if i != 0:
stringified_arguments += "&"
stringified_arguments += key + "=" + pam_encode(str(unsorted_params[key]))
i += 1
return stringified_arguments
def pam_encode(s_url):
# !'()*~
encoded = url_encode(s_url)
if encoded is not None:
encoded = (
encoded.replace("*", "%2A")
.replace("!", "%21")
.replace("'", "%27")
.replace("(", "%28")
.replace(")", "%29")
.replace("[", "%5B")
.replace("]", "%5D")
.replace("~", "%7E")
)
return encoded
def sign_sha256(secret, sign_input):
from base64 import urlsafe_b64encode
sign = urlsafe_b64encode(
hmac.new(secret.encode("utf-8"), sign_input.encode("utf-8"), sha256).digest()
)
return sign.decode("utf-8")
def push_type_to_string(push_type):
if push_type == PNPushType.APNS:
return "apns"
elif push_type == PNPushType.GCM:
return "gcm"
elif push_type == PNPushType.MPNS:
return "mpns"
else:
return ""
def strip_right(text, suffix):
if not text.endswith(suffix):
return text
return text[: len(text) - len(suffix)]
def datetime_now():
return datetime.datetime.now().strftime("%I:%M%p on %B %d, %Y")
def sign_request(endpoint, pn, custom_params, method, body):
custom_params["timestamp"] = str(pn.timestamp())
request_url = endpoint.get_path()
encoded_query_string = prepare_pam_arguments(custom_params)
is_v2_signature = not (
request_url.startswith("/publish") and method == HttpMethod.POST
)
signed_input = ""
if not is_v2_signature:
signed_input += pn.config.subscribe_key + "\n"
signed_input += pn.config.publish_key + "\n"
signed_input += request_url + "\n"
signed_input += encoded_query_string
else:
signed_input += HttpMethod.string(method).upper() + "\n"
signed_input += pn.config.publish_key + "\n"
signed_input += request_url + "\n"
signed_input += encoded_query_string + "\n"
if body is not None:
signed_input += body
signature = sign_sha256(pn.config.secret_key, signed_input)
if is_v2_signature:
signature = signature.rstrip("=")
signature = "v2." + signature
custom_params["signature"] = signature
def parse_resources(resource_list, resource_set_name, resources, patterns):
if resource_list:
for pn_resource in resource_list:
resource_object = {}
if pn_resource.is_pattern_resource():
determined_object = patterns
else:
determined_object = resources
if resource_set_name in determined_object:
determined_object[resource_set_name][pn_resource.get_id()] = (
calculate_bitmask(pn_resource)
)
else:
resource_object[pn_resource.get_id()] = calculate_bitmask(pn_resource)
determined_object[resource_set_name] = resource_object
if resource_set_name not in resources:
resources[resource_set_name] = {}
if resource_set_name not in patterns:
patterns[resource_set_name] = {}
def calculate_bitmask(pn_resource):
bit_sum = 0
if pn_resource.is_read():
bit_sum += PAMPermissions.READ.value
if pn_resource.is_write():
bit_sum += PAMPermissions.WRITE.value
if pn_resource.is_manage():
bit_sum += PAMPermissions.MANAGE.value
if pn_resource.is_delete():
bit_sum += PAMPermissions.DELETE.value
if pn_resource.is_create():
bit_sum += PAMPermissions.CREATE.value
if pn_resource.is_get():
bit_sum += PAMPermissions.GET.value
if pn_resource.is_update():
bit_sum += PAMPermissions.UPDATE.value
if pn_resource.is_join():
bit_sum += PAMPermissions.JOIN.value
return bit_sum
def decode_utf8_dict(dic):
if isinstance(dic, bytes):
return dic.decode("utf-8")
elif isinstance(dic, dict):
new_dic = {}
for key in dic:
new_key = key
if isinstance(key, bytes):
new_key = key.decode("UTF-8")
if new_key == "sig" and isinstance(dic[key], bytes):
new_dic[new_key] = dic[key]
else:
new_dic[new_key] = decode_utf8_dict(dic[key])
return new_dic
elif isinstance(dic, list):
new_l = []
for e in dic:
new_l.append(decode_utf8_dict(e))
return new_l
else:
return dic
def has_permission(perms, perm):
return (perms & perm) == perm
def has_read_permission(perms):
return has_permission(perms, PAMPermissions.READ.value)
def has_write_permission(perms):
return has_permission(perms, PAMPermissions.WRITE.value)
def has_delete_permission(perms):
return has_permission(perms, PAMPermissions.DELETE.value)
def has_manage_permission(perms):
return has_permission(perms, PAMPermissions.MANAGE.value)
def has_get_permission(perms):
return has_permission(perms, PAMPermissions.GET.value)
def has_update_permission(perms):
return has_permission(perms, PAMPermissions.UPDATE.value)
def has_join_permission(perms):
return has_permission(perms, PAMPermissions.JOIN.value)
def parse_pam_permissions(resource):
new_res = {}
for res_name, perms in resource.items():
new_res[res_name] = {
"read": has_read_permission(perms),
"write": has_write_permission(perms),
"manage": has_manage_permission(perms),
"delete": has_delete_permission(perms),
"get": has_get_permission(perms),
"update": has_update_permission(perms),
"join": has_join_permission(perms),
}
return new_res
bdraco-freenub-69809e8/pubnub/workers.py 0000664 0000000 0000000 00000016773 14645232030 0020244 0 ustar 00root root 0000000 0000000 import logging
from abc import abstractmethod
from .endpoints.file_operations.get_file_url import GetFileDownloadUrl
from .enums import PNOperationType, PNStatusCategory
from .models.consumer.common import PNStatus
from .models.consumer.objects_v2.channel import PNChannelMetadataResult
from .models.consumer.objects_v2.memberships import PNMembershipResult
from .models.consumer.objects_v2.uuid import PNUUIDMetadataResult
from .models.consumer.pn_error_data import PNErrorData
from .models.consumer.pubsub import (
PNFileMessageResult,
PNMessageActionResult,
PNMessageResult,
PNPresenceEventResult,
PNSignalMessageResult,
)
from .models.server.subscribe import PresenceEnvelope, SubscribeMessage
from .utils import strip_right
logger = logging.getLogger("pubnub")
class SubscribeMessageWorker:
TYPE_MESSAGE = 0
TYPE_SIGNAL = 1
TYPE_OBJECT = 2
TYPE_MESSAGE_ACTION = 3
TYPE_FILE_MESSAGE = 4
def __init__(
self, pubnub_instance, listener_manager_instance, queue_instance, event
):
# assert isinstance(pubnub_instnace, PubNubCore)
# assert isinstance(listener_manager_instance, ListenerManager)
# assert isinstance(queue_instance, utils.Queue)
self._pubnub = pubnub_instance
self._listener_manager = listener_manager_instance
self._queue = queue_instance
self._is_running = None
self._event = event
def run(self):
self._take_message()
@abstractmethod
def _take_message(self):
pass
def _get_url_for_file_event_message(self, channel, extracted_message):
return (
GetFileDownloadUrl(self._pubnub)
.channel(channel)
.file_name(extracted_message["file"]["name"])
.file_id(extracted_message["file"]["id"])
.get_complete_url()
)
def _process_message(self, message_input):
if self._pubnub.config.cipher_key is None:
return message_input
else:
try:
return self._pubnub.config.crypto.decrypt(
self._pubnub.config.cipher_key, message_input
)
except Exception as exception:
logger.warning(
'could not decrypt message: "%s", due to error %s'
% (message_input, str(exception))
)
pn_status = PNStatus()
pn_status.category = PNStatusCategory.PNDecryptionErrorCategory
pn_status.error_data = PNErrorData(str(exception), exception)
pn_status.error = True
pn_status.operation = PNOperationType.PNSubscribeOperation
self._listener_manager.announce_status(pn_status)
return message_input
def _process_incoming_payload(self, message):
assert isinstance(message, SubscribeMessage)
channel = message.channel
subscription_match = message.subscription_match
publish_meta_data = message.publish_metadata
if channel is not None and channel == subscription_match:
subscription_match = None
if "-pnpres" in message.channel:
presence_payload = PresenceEnvelope.from_json_payload(message.payload)
stripped_presence_channel = None
stripped_presence_subscription = None
if channel is not None:
stripped_presence_channel = strip_right(channel, "-pnpres")
if subscription_match is not None:
stripped_presence_subscription = strip_right(
subscription_match, "-pnpres"
)
pn_presence_event_result = PNPresenceEventResult(
event=presence_payload.action,
channel=stripped_presence_channel,
subscription=stripped_presence_subscription,
timetoken=publish_meta_data.publish_timetoken,
occupancy=presence_payload.occupancy,
uuid=presence_payload.uuid,
timestamp=presence_payload.timestamp,
state=presence_payload.data,
join=message.payload.get("join", None),
leave=message.payload.get("leave", None),
timeout=message.payload.get("timeout", None),
)
self._listener_manager.announce_presence(pn_presence_event_result)
elif message.type == SubscribeMessageWorker.TYPE_OBJECT:
if message.payload["type"] == "channel":
channel_result = PNChannelMetadataResult(
event=message.payload["event"], data=message.payload["data"]
)
self._listener_manager.announce_channel(channel_result)
elif message.payload["type"] == "uuid":
uuid_result = PNUUIDMetadataResult(
event=message.payload["event"], data=message.payload["data"]
)
self._listener_manager.announce_uuid(uuid_result)
elif message.payload["type"] == "membership":
membership_result = PNMembershipResult(
event=message.payload["event"], data=message.payload["data"]
)
self._listener_manager.announce_membership(membership_result)
elif message.type == SubscribeMessageWorker.TYPE_FILE_MESSAGE:
extracted_message = self._process_message(message.payload)
download_url = self._get_url_for_file_event_message(
channel, extracted_message
)
pn_file_result = PNFileMessageResult(
message=extracted_message.get("message"),
channel=channel,
subscription=subscription_match,
timetoken=publish_meta_data.publish_timetoken,
publisher=message.issuing_client_id,
file_url=download_url,
file_id=extracted_message["file"]["id"],
file_name=extracted_message["file"]["name"],
)
self._listener_manager.announce_file_message(pn_file_result)
else:
extracted_message = self._process_message(message.payload)
publisher = message.issuing_client_id
if extracted_message is None:
logger.debug("unable to parse payload on #processIncomingMessages")
if message.type == SubscribeMessageWorker.TYPE_SIGNAL:
pn_signal_result = PNSignalMessageResult(
message=extracted_message,
channel=channel,
subscription=subscription_match,
timetoken=publish_meta_data.publish_timetoken,
publisher=publisher,
)
self._listener_manager.announce_signal(pn_signal_result)
elif message.type == SubscribeMessageWorker.TYPE_MESSAGE_ACTION:
message_action = extracted_message["data"]
if "uuid" not in message_action:
message_action["uuid"] = publisher
message_action_result = PNMessageActionResult(message_action)
self._listener_manager.announce_message_action(message_action_result)
else:
pn_message_result = PNMessageResult(
message=extracted_message,
channel=channel,
subscription=subscription_match,
timetoken=publish_meta_data.publish_timetoken,
publisher=publisher,
)
self._listener_manager.announce_message(pn_message_result)
bdraco-freenub-69809e8/pyproject.toml 0000664 0000000 0000000 00000006564 14645232030 0017614 0 ustar 00root root 0000000 0000000 [tool.poetry]
name = "freenub"
version = "0.1.0"
description = "This is a fork of pubnub when it still had an MIT license"
authors = ["J. Nick Koston "]
license = "MIT"
readme = "README.md"
repository = "https://github.com/bdraco/freenub"
classifiers = [
'Development Status :: 5 - Production/Stable',
'Intended Audience :: Developers',
'Programming Language :: Python',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: 3.11',
'Programming Language :: Python :: 3.12',
'Programming Language :: Python :: Implementation :: CPython',
'License :: OSI Approved :: MIT License',
'Operating System :: OS Independent',
'Topic :: Internet :: WWW/HTTP',
'Topic :: Software Development :: Libraries :: Python Modules',
]
packages = [
{ include = "pubnub" },
]
[tool.poetry.urls]
"Bug Tracker" = "https://github.com/bdraco/freenub/issues"
"Changelog" = "https://github.com/bdraco/freenub/blob/main/CHANGELOG.md"
[tool.poetry.dependencies]
python = "^3.8"
pycryptodomex = ">=3.3"
requests = ">=2.4"
cbor2 = "^5.6.4"
aiohttp = "^3.9.5"
[tool.poetry.group.dev.dependencies]
pytest = "^8.0.0"
pytest-cov = "^5.0.0"
pyyaml = "^6.0.1"
pytest-asyncio = "^0.23.7"
behave = "^1.2.6"
vcrpy = "^6.0.1"
urllib3 = "<2"
busypie = "^0.5.1"
[tool.semantic_release]
version_toml = ["pyproject.toml:tool.poetry.version"]
version_variables = [
"pubnub/__init__.py:__version__",
]
build_command = "pip install poetry && poetry build"
[tool.semantic_release.changelog]
exclude_commit_patterns = [
"chore*",
"ci*",
]
[tool.semantic_release.changelog.environment]
keep_trailing_newline = true
[tool.semantic_release.branches.main]
match = "main"
[tool.semantic_release.branches.noop]
match = "(?!main$)"
prerelease = true
[tool.pytest.ini_options]
addopts = """\
-v
-Wdefault
--cov=pubnub
--cov-report=term
--cov-report=xml
"""
[tool.coverage.run]
branch = true
[tool.coverage.report]
exclude_lines = [
"pragma: no cover",
"@overload",
"if TYPE_CHECKING",
"raise NotImplementedError",
'if __name__ == "__main__":',
]
[tool.ruff]
target-version = "py38"
line-length = 88
[tool.ruff.lint]
ignore = [
"D203", # 1 blank line required before class docstring
"D212", # Multi-line docstring summary should start at the first line
"D100", # Missing docstring in public module
"D104", # Missing docstring in public package
"D107", # Missing docstring in `__init__`
"D401", # First line of docstring should be in imperative mood
"C414",
"C413",
]
select = [
"I", # isort
]
[tool.ruff.lint.per-file-ignores]
"tests/**/*" = [
"D100",
"D101",
"D102",
"D103",
"D104",
"S101",
]
"setup.py" = ["D100"]
"conftest.py" = ["D100"]
[tool.ruff.lint.isort]
known-first-party = ["pubnub", "tests"]
[tool.mypy]
check_untyped_defs = true
disallow_any_generics = true
disallow_incomplete_defs = true
disallow_untyped_defs = true
mypy_path = "src/"
no_implicit_optional = true
show_error_codes = true
warn_unreachable = true
warn_unused_ignores = true
exclude = [
'setup.py',
]
[[tool.mypy.overrides]]
module = "tests.*"
allow_untyped_defs = true
[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
bdraco-freenub-69809e8/renovate.json 0000664 0000000 0000000 00000000101 14645232030 0017373 0 ustar 00root root 0000000 0000000 {
"extends": ["github>browniebroke/renovate-configs:python"]
}
bdraco-freenub-69809e8/requirements-dev.txt 0000664 0000000 0000000 00000000163 14645232030 0020725 0 ustar 00root root 0000000 0000000 pyyaml
pytest-cov
pycryptodomex
flake8
pytest
pytest-asyncio
aiohttp
requests
cbor2
behave
vcrpy
urllib3<2
busypie
bdraco-freenub-69809e8/scripts/ 0000775 0000000 0000000 00000000000 14645232030 0016354 5 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/scripts/install.sh 0000775 0000000 0000000 00000000071 14645232030 0020357 0 ustar 00root root 0000000 0000000 #!/usr/bin/env bash
pip install -r requirements-dev.txt
bdraco-freenub-69809e8/scripts/run-tests.py 0000775 0000000 0000000 00000001116 14645232030 0020674 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
# Don't run tests from the root repo dir.
# We want to ensure we're importing from the installed
# binary package not from the CWD.
import os
from subprocess import check_call
_dname = os.path.dirname
REPO_ROOT = _dname(_dname(os.path.abspath(__file__)))
os.chdir(os.path.join(REPO_ROOT))
tcmn = "py.test tests --cov=pubnub --ignore=tests/manual/"
fcmn = "flake8 --exclude=scripts/,src/,.cache,.git,.idea,.tox,._trial_temp/,venv/ --ignore F811,E402"
def run(command):
return check_call(command, shell=True)
run(tcmn)
# moved to separate action
# run(fcmn)
bdraco-freenub-69809e8/setup.cfg 0000664 0000000 0000000 00000000125 14645232030 0016504 0 ustar 00root root 0000000 0000000 [tool:pytest]
norecursedirs = benchmarks
[flake8]
max-line-length = 120
ignore=E402
bdraco-freenub-69809e8/templates/ 0000775 0000000 0000000 00000000000 14645232030 0016663 5 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/templates/CHANGELOG.md.j2 0000664 0000000 0000000 00000001235 14645232030 0021007 0 ustar 00root root 0000000 0000000 # Changelog
{%- for version, release in context.history.released.items() %}
## {{ version.as_tag() }} ({{ release.tagged_date.strftime("%Y-%m-%d") }})
{%- for category, commits in release["elements"].items() %}
{# Category title: Breaking, Fix, Documentation #}
### {{ category | capitalize }}
{# List actual changes in the category #}
{%- for commit in commits %}
{% if commit is not none and commit.descriptions is defined %}
- {{ commit.descriptions[0] | capitalize }} ([`{{ commit.short_hash }}`]({{ commit.hexsha | commit_hash_url }}))
{% endif %}
{%- endfor %}{# for commit #}
{%- endfor %}{# for category, commits #}
{%- endfor %}{# for version, release #}
bdraco-freenub-69809e8/tests/ 0000775 0000000 0000000 00000000000 14645232030 0016027 5 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/tests/__init__.py 0000664 0000000 0000000 00000000000 14645232030 0020126 0 ustar 00root root 0000000 0000000 bdraco-freenub-69809e8/tests/test_init.py 0000664 0000000 0000000 00000000327 14645232030 0020405 0 ustar 00root root 0000000 0000000 from Cryptodome.Cipher import AES
import pubnub
from pubnub.pnconfiguration import PNConfiguration
def test_x():
assert 1 + 1 == 2
assert PNConfiguration.ALLOWED_AES_MODES == [AES.MODE_CBC, AES.MODE_GCM]