pax_global_header00006660000000000000000000000064141534036770014524gustar00rootroot0000000000000052 comment=52632b031210162d0f243bd144a5eb90df54cd58 python-31.0.0/000077500000000000000000000000001415340367700131265ustar00rootroot00000000000000python-31.0.0/.github/000077500000000000000000000000001415340367700144665ustar00rootroot00000000000000python-31.0.0/.github/workflows/000077500000000000000000000000001415340367700165235ustar00rootroot00000000000000python-31.0.0/.github/workflows/main.yml000066400000000000000000000010261415340367700201710ustar00rootroot00000000000000on: push: branches: - master pull_request: branches: - master name: Run Tox tests jobs: tox_test: name: Tox test steps: - uses: actions/checkout@v2 - name: Run Tox tests id: test uses: fedora-python/tox-github-action@master with: tox_env: ${{ matrix.tox_env }} strategy: fail-fast: false matrix: tox_env: [py27, py36, py37, py38, py39, py310, pypy2, pypy3, pep8] # Use GitHub's Linux Docker host runs-on: ubuntu-latest python-31.0.0/.github/workflows/publish.yml000066400000000000000000000021771415340367700207230ustar00rootroot00000000000000name: Publish Python 🐍 distributions 📦 to PyPI and TestPyPI on: push jobs: build-n-publish: name: Build and publish Python 🐍 distributions 📦 to PyPI and TestPyPI runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v2 with: fetch-depth: 0 - name: Set up Python 3.9 uses: actions/setup-python@v1 with: python-version: 3.9 - name: Install pypa/build run: >- python -m pip install build --user - name: Build a binary wheel and a source tarball run: >- python -m build --sdist --wheel --outdir dist/ . - name: Publish distribution 📦 to Test PyPI uses: pypa/gh-action-pypi-publish@master with: password: ${{ secrets.TEST_PYPI_API_TOKEN }} repository_url: https://test.pypi.org/legacy/ - name: Publish distribution 📦 to PyPI if: startsWith(github.ref, 'refs/tags') uses: pypa/gh-action-pypi-publish@master with: password: ${{ secrets.PYPI_API_TOKEN }} python-31.0.0/.gitignore000066400000000000000000000002261415340367700151160ustar00rootroot00000000000000*.pyc .tox/ .stestr/ *.egg-info/ /MANIFEST /dist /docs/build /.idea /.cache /.coverage /.pytest_cache *.eggs/ *__pycache__* dist/ *.swa *.swp build/ python-31.0.0/.tito/000077500000000000000000000000001415340367700141635ustar00rootroot00000000000000python-31.0.0/.tito/packages/000077500000000000000000000000001415340367700157415ustar00rootroot00000000000000python-31.0.0/.tito/packages/.readme000066400000000000000000000002371415340367700172010ustar00rootroot00000000000000the .tito/packages directory contains metadata files named after their packages. Each file has the latest tagged version and the project's relative directory. python-31.0.0/.tito/tito.props000066400000000000000000000002231415340367700162240ustar00rootroot00000000000000[buildconfig] builder = tito.builder.Builder tagger = tito.tagger.VersionTagger changelog_do_not_remove_cherrypick = 0 changelog_format = %s (%ae) python-31.0.0/.travis.yml000066400000000000000000000053721415340367700152460ustar00rootroot00000000000000language: python sudo: false dist: trusty python: - '3.6' - '3.5' - '2.7' install: - pip install tox-travis .[devel] - pip install -r test-requirements.txt script: # Check packaging first (README format etc...) - pip install -U setuptools - pip install -U wheel - pip install -U twine - python setup.py sdist - python setup.py bdist_wheel - twine check dist/* - tox - "python -m varlink.cli -A 'python -m varlink.tests.test_orgexamplemore --varlink=$VARLINK_ADDRESS' call org.example.more.Ping '{ \"ping\": \"Ping\" }'" - PYTHONPATH=$(pwd) python ./varlink/tests/test_certification.py - PYTHONPATH=$(pwd) python ./varlink/tests/test_orgexamplemore.py - python -m varlink.tests.test_certification -A 'python -m varlink.tests.test_certification --varlink=$VARLINK_ADDRESS' --client - python -m varlink.tests.test_orgexamplemore -A 'python -m varlink.tests.test_orgexamplemore --varlink=$VARLINK_ADDRESS' --client after_success: - coveralls env: global: secure: kikKJZDrlT8NMKi/sDPGCwgyOVrJW2xB/QkXvMu2bGqq9GFYpSBQSCLO2WIx3s1ANP07cNCI6LwmsEfJgxCcFwPwj4NyeBwLvC1M3tcJcnEqA1D8BPnUZZrcDJ60p7qT6Y2MUThM08UgptNFwoEXY2H+VeAKLPEj54DzugqPGV8tN3SbrApFWrrSOegAQovbcrmIt8pN5q4+GZXl1zhQFdc+VBHpOTGidTqoIRjDSFLf7+MmFCamUH6Tad+4UoUyFYA/2747nnwB3RRJobJkDD+tmAdpXwHNzAQ9PtZVfQlEzrl9fWds1/A0TiYfwVS5ASOcu2O292OTY9ZnVVDmmkGRbFq5sn9/XwwXv0JPL7egkU967BOsrS8GU09f5cd6AG/HcgXbr8EwdYgP7qa2upc2qEO0UrjgPgNVdhgWUNducwkBwlxHNpuL9Za4vhyF8YkFiWdoRKoRjvoySYoCUsn/MqlQuxn5H+m3lS71p3RKv2FYQYF4UzYXc0IWE7mRhJqdZdS1ZGVlnN4JzjNdItuy/0ZaX+xtFU+IVONwMxi3+vw4mFtJHnng+H6MAsYE8GHf77/IeBHA+2i0hwapiQSsAk2hnFkcPHC0RBVjfjhZvKcXXM9rGGRYImRyWsY3uggF7Eh5R9WCe4Yn9pqWmV/a7F4ngshCTjTvq4K/H/k= notifications: webhooks: urls: - https://webhooks.gitter.im/e/8cfd8d5fa3c31aa3d0c3 on_success: change # options: [always|never|change] default: always on_failure: always # options: [always|never|change] default: always on_start: never # options: [always|never|change] default: always deploy: - provider: pypi user: varlink on: tags: true distributions: sdist bdist_wheel skip_existing: true password: secure: AnvsE0P1thGN9Xv3kg1kZRsb1K6fy3iqUK23LGvwes3d1CjeCoFfc7e1Q5t66/Ajb3tWWcE4oovHD8BUUQS1VWE1jcqKILhAlDgYdDsMenBWHMjVyEIcSKR5z02BBfU8Zhf/E3mCQIj+Oa6l22XHF4WLPgF4nkNWUTYACkQzUCoJg2MMtYK0CVh1VP5bgAm0Tq31/H1M1h7ik7r5OzGVPn82eVZUxndRJ3GFLuZVJLt+vlo9JrdXqhoPvquK1FSljKIHNbflw39OoUhVkOoe/eksuRkd1jbAnFlZjr3DXcauTVTTzXc1p8m0q+Zaaaw8/WlDCnzRkdnAqYxTfznXO1p/UGPJ/pzvx1mTlZEbgGjx4Q7CtbpHFldWO4bgCI5rZd213/08QtV4bA8jj3cWMpzgsYkgNC2ruaGkVCmKxTJLWupadD+V9jmeJpIaXpjRI/dZWKkMw2cHX74qqpPSHCgJ+zkzbQdpSr+0US12NBayShfp8lTO5Umm6UyxGSKgH/OgGO1SmduEqaPjOH0KV8W11zRtMRuPA7KH0soo1mDgduEzTYZc4Oq4SWf4HPRIh21eNIlLERBnGTloGfSfvP7CYXD2XtcPEGswSZnq3Dj85cUaFzcD8cLFmbShwnlEWc1s9s3PN/DCz5TQhPl9E1zkIKaZGtDFJUOaPliOnSY= python-31.0.0/LICENSE.txt000066400000000000000000000261351415340367700147600ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright {yyyy} {name of copyright owner} Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. python-31.0.0/MANIFEST.in000066400000000000000000000000361415340367700146630ustar00rootroot00000000000000include README.md LICENSE.txt python-31.0.0/Makefile000066400000000000000000000015461415340367700145740ustar00rootroot00000000000000PYTHON := $(shell python -c 'import platform;print(platform.python_version().split(".")[0])') PYTHON2 := $(shell if which python2 &>/dev/null;then which python2; elif [ "$(PYTHON)" == 2 ]; then which python; fi) PYTHON3 := $(shell if which python3 &>/dev/null;then which python3; elif [ "$(PYTHON)" == 3 ]; then which python; fi) all: build .PHONY: all build: rm -fr build python3 setup.py bdist_wheel --universal .PHONY: build clean: rm -rf dist build .PHONY: clean check: if [ -x "$(PYTHON2)" ]; then $(PYTHON2) -m unittest varlink;fi if [ -x "$(PYTHON3)" ]; then $(PYTHON3) -m unittest varlink;fi .PHONY: check docs: python3 setup.py build_sphinx --source-dir=docs/ --build-dir=docs/build --all-files .PHONY: docs docsdeploy: docs GIT_DEPLOY_DIR=$(PWD)/docs/build/html GIT_DEPLOY_BRANCH=gh-pages ./git-deploy-branch.sh -m "doc update" .PHONY: docsdeploy python-31.0.0/README.md000066400000000000000000000122211415340367700144030ustar00rootroot00000000000000[![Varlink Certified](https://img.shields.io/badge/varlink-certified-green.svg)](https://www.varlink.org/Language-Bindings) [![Build Status](https://travis-ci.org/varlink/python.svg?branch=master)](https://travis-ci.org/varlink/python) [![Coverage Status](https://coveralls.io/repos/github/varlink/python/badge.svg?branch=master)](https://coveralls.io/github/varlink/python?branch=master) [![PyPI](https://img.shields.io/pypi/v/varlink.svg)](https://pypi.org/project/varlink/) [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/varlink.svg)](https://pypi.org/project/varlink/) [![PyPI - Status](https://img.shields.io/pypi/status/varlink.svg)](https://pypi.org/project/varlink/) # python-varlink A [varlink](http://varlink.org) implementation for Python. * [GIT Repository](https://github.com/varlink/python) * [API documentation](https://varlink.github.io/python/) ## python varlink installation From pypi: ```bash $ pip3 install --user varlink ``` With Fedora 28/rawhide: ```bash $ sudo dnf install python3-varlink ``` ## Examples See the [tests](https://github.com/varlink/python-varlink/tree/master/varlink/tests) directory. ```bash $ python3 -m varlink.tests.test_orgexamplemore --varlink="unix:/tmp/test" & [1] 6434 $ python -m varlink.cli help unix:/tmp/test/org.example.more # Example Varlink service interface org.example.more # Enum, returning either start, progress or end # progress: [0-100] type State ( start: ?bool, progress: ?int, end: ?bool ) # Returns the same string method Ping(ping: string) -> (pong: string) # Dummy progress method # n: number of progress steps method TestMore(n: int) -> (state: State) # Stop serving method StopServing() -> () # Something failed in TestMore error TestMoreError (reason: string) $ python -m varlink.cli call unix:/tmp/test/org.example.more.Ping '{ "ping": "Ping"}' { "pong": "Ping" } $ fg python3 -m varlink.tests.test_orgexamplemore --varlink="unix:/tmp/test" ^C ``` ```bash $ python3 -m varlink.tests.test_orgexamplemore --client -A 'python3 -m varlink.tests.test_orgexamplemore --varlink=$VARLINK_ADDRESS' Connecting to unix:/tmp/tmppxrbqk9p/4927 Listening on /tmp/tmppxrbqk9p/4927 --- Start --- Progress: 0 Progress: 10 Progress: 20 Progress: 30 Progress: 40 Progress: 50 Progress: 60 Ping: Test Progress: 70 Ping: Test Progress: 80 Ping: Test Progress: 90 Ping: Test Progress: 100 Ping: Test --- End --- ``` ```bash $ PYTHONPATH=$(pwd) python3 ./varlink/tests/test_orgexamplemore.py Connecting to unix:/tmp/tmp7n6zc67d/5257 Listening on /tmp/tmp7n6zc67d/5257 --- Start --- Progress: 0 Progress: 10 Progress: 20 Progress: 30 Progress: 40 Progress: 50 Progress: 60 Ping: Test Progress: 70 Ping: Test Progress: 80 Ping: Test Progress: 90 Ping: Test Progress: 100 Ping: Test --- End --- ``` ```bash $ python3 -m varlink.tests.test_orgexamplemore --varlink="unix:/tmp/test" & Listening on /tmp/test [1] 6434 python3 -m varlink.tests.test_orgexamplemore --client --varlink="unix:/tmp/test" Connecting to unix:/tmp/test Ping: Test --- Start --- Progress: 0 Progress: 10 Progress: 20 Progress: 30 Progress: 40 Progress: 50 Progress: 60 Ping: Test Progress: 70 Ping: Test Progress: 80 Ping: Test Progress: 90 Ping: Test Progress: 100 Ping: Test --- End --- $ python3 -m varlink.cli call --more unix:/tmp/test/org.example.more.TestMore '{ "n": 10 }' {'state': {'start': True}} {'state': {'progress': 0}} {'state': {'progress': 10}} {'state': {'progress': 20}} {'state': {'progress': 30}} {'state': {'progress': 40}} {'state': {'progress': 50}} {'state': {'progress': 60}} {'state': {'progress': 70}} {'state': {'progress': 80}} {'state': {'progress': 90}} {'state': {'progress': 100}} {'state': {'end': True}} $ fg python3 -m varlink.tests.test_orgexamplemore --varlink="unix:/tmp/test" ^C ``` You can also start the clients and server with URLs following the [varlink URL standard](https://varlink.org/#address) with `unix:` and `tcp:`. E.g. - unix:@anonuds - unix:/run/myserver/socketfile - tcp:127.0.0.1:12345 - tcp:[::1]:12345 ### Activation Mode Activation mode starts the service to connect to and passes the socket via socket activation. The ```VARLINK_ADDRESS``` environment variable contains the varlink address URI. ```bash $ python3 -m varlink.cli --activate 'python3 -m varlink.tests.test_orgexamplemore --varlink=$VARLINK_ADDRESS' call org.example.more.Ping '{ "ping": "Ping"}' Listening on @00352 {'pong': 'Ping'} ``` ### Bridge Mode Bridge mode allows to tunnel to a remote point via stdin/stdout and call a method. Running ```varlink bridge``` allows to connect stdio to the host services via ```org.varlink.resolver``` interface resolving. ```bash # python3 -m varlink.cli -b "ssh host.example.org varlink bridge" call com.redhat.machine.GetInfo '{}' { "hostname": "host.example.org", "system": { "id": "fedora", "kernel_version": "4.18.0-0.rc5.git1.2.fc29.x86_64", "name": "Fedora", "version": "29" }, "virtualization": { "name": "none" } } ``` ### Varlink Certification Server ``` $ python3 -m varlink.tests.test_certification --varlink=tcp:127.0.0.1:12345 ``` ### Varlink Certification Client ``` $ python3 -m varlink.tests.test_certification --varlink=tcp:127.0.0.1:12345 --client ``` python-31.0.0/docs/000077500000000000000000000000001415340367700140565ustar00rootroot00000000000000python-31.0.0/docs/conf.py000066400000000000000000000116701415340367700153620ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # Configuration file for the Sphinx documentation builder. # # This file does only contain a selection of the most common options. For a # full list see the documentation: # http://www.sphinx-doc.org/en/stable/config # -- Path setup -------------------------------------------------------------- # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # # import os # import sys # sys.path.insert(0, os.path.abspath('.')) # -- Project information ----------------------------------------------------- project = u'varlink' copyright = u'2018, Harald Hoyer, Lars Karlitski' author = u'Harald Hoyer, Lars Karlitski' # The short X.Y version version = u'' # The full version, including alpha/beta/rc tags release = u'' # -- General configuration --------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. # # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.viewcode', 'sphinx.ext.githubpages', ] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # source_suffix = ['.rst', '.md'] # The master toctree document. master_doc = 'index' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. language = None # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This pattern also affects html_static_path and html_extra_path . exclude_patterns = [] # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'default' # -- Options for HTML output ------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # html_theme = 'classic' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # # html_theme_options = {} # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # Custom sidebar templates, must be a dictionary that maps document names # to template names. # # The default sidebars (for documents that don't match any pattern) are # defined by theme itself. Builtin themes are using these templates by # default: ``['localtoc.html', 'relations.html', 'sourcelink.html', # 'searchbox.html']``. # # html_sidebars = {} # -- Options for HTMLHelp output --------------------------------------------- # Output file base name for HTML help builder. htmlhelp_basename = 'varlinkdoc' # -- Options for LaTeX output ------------------------------------------------ latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). # # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. # # 'preamble': '', # Latex figure (float) alignment # # 'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ (master_doc, 'varlink.tex', u'varlink Documentation', u'Harald Hoyer\\textless{}harald@redhat.com\\textgreater{}, Lars Karlitski\\textless{}lars@karlitski.net\\textgreater{}', 'manual'), ] # -- Options for manual page output ------------------------------------------ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ (master_doc, 'varlink', u'varlink Documentation', [author], 1) ] # -- Options for Texinfo output ---------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ (master_doc, 'varlink', u'varlink Documentation', author, 'varlink', 'One line description of project.', 'Miscellaneous'), ] # -- Extension configuration ------------------------------------------------- python-31.0.0/docs/index.rst000066400000000000000000000006711415340367700157230ustar00rootroot00000000000000varlink for python ****************** .. automodule:: varlink :members: :special-members: __init__,__next__ :undoc-members: :show-inheritance: Example ####### .. automodule:: varlink.tests.test_orgexamplemore :members: Testing ####### .. automodule:: varlink.mock :members: .. toctree:: :maxdepth: 2 :caption: Contents: Indices and tables ################## * :ref:`genindex` * :ref:`modindex` * :ref:`search` python-31.0.0/git-deploy-branch.sh000077500000000000000000000144651415340367700170070ustar00rootroot00000000000000#!/usr/bin/env bash set -o errexit #abort if any command fails me=$(basename "$0") help_message="\ Usage: $me [-c FILE] [] Deploy generated files to a git branch. Options: -h, --help Show this help information. -v, --verbose Increase verbosity. Useful for debugging. -e, --allow-empty Allow deployment of an empty directory. -m, --message MESSAGE Specify the message used when committing on the deploy branch. -n, --no-hash Don't append the source commit's hash to the deploy commit's message. -c, --config-file PATH Override default & environment variables' values with those in set in the file at 'PATH'. Must be the first option specified. Variables: GIT_DEPLOY_DIR Folder path containing the files to deploy. GIT_DEPLOY_BRANCH Commit deployable files to this branch. GIT_DEPLOY_REPO Push the deploy branch to this repository. These variables have default values defined in the script. The defaults can be overridden by environment variables. Any environment variables are overridden by values set in a '.env' file (if it exists), and in turn by those set in a file specified by the '--config-file' option." parse_args() { # Set args from a local environment file. if [ -e ".env" ]; then source .env fi # Set args from file specified on the command-line. if [[ $1 = "-c" || $1 = "--config-file" ]]; then source "$2" shift 2 fi # Parse arg flags # If something is exposed as an environment variable, set/overwrite it # here. Otherwise, set/overwrite the internal variable instead. while : ; do if [[ $1 = "-h" || $1 = "--help" ]]; then echo "$help_message" return 0 elif [[ $1 = "-v" || $1 = "--verbose" ]]; then verbose=true shift elif [[ $1 = "-e" || $1 = "--allow-empty" ]]; then allow_empty=true shift elif [[ ( $1 = "-m" || $1 = "--message" ) && -n $2 ]]; then commit_message=$2 shift 2 elif [[ $1 = "-n" || $1 = "--no-hash" ]]; then GIT_DEPLOY_APPEND_HASH=false shift else break fi done # Set internal option vars from the environment and arg flags. All internal # vars should be declared here, with sane defaults if applicable. # Source directory & target branch. deploy_directory=${GIT_DEPLOY_DIR:-dist} deploy_branch=${GIT_DEPLOY_BRANCH:-gh-pages} #if no user identity is already set in the current git environment, use this: default_username=${GIT_DEPLOY_USERNAME:-deploy.sh} default_email=${GIT_DEPLOY_EMAIL:-} #repository to deploy to. must be readable and writable. repo=${GIT_DEPLOY_REPO:-origin} #append commit hash to the end of message by default append_hash=${GIT_DEPLOY_APPEND_HASH:-true} } main() { parse_args "$@" enable_expanded_output if ! git diff --exit-code --quiet --cached; then echo Aborting due to uncommitted changes in the index >&2 return 1 fi commit_title=`git log -n 1 --format="%s" HEAD` commit_hash=` git log -n 1 --format="%H" HEAD` #default commit message uses last title if a custom one is not supplied if [[ -z $commit_message ]]; then commit_message="publish: $commit_title" fi #append hash to commit message unless no hash flag was found if [ $append_hash = true ]; then commit_message="$commit_message"$'\n\n'"generated from commit $commit_hash" fi previous_branch=`git rev-parse --abbrev-ref HEAD` if [ ! -d "$deploy_directory" ]; then echo "Deploy directory '$deploy_directory' does not exist. Aborting." >&2 return 1 fi # must use short form of flag in ls for compatibility with OS X and BSD if [[ -z `ls -A "$deploy_directory" 2> /dev/null` && -z $allow_empty ]]; then echo "Deploy directory '$deploy_directory' is empty. Aborting. If you're sure you want to deploy an empty tree, use the --allow-empty / -e flag." >&2 return 1 fi if git ls-remote --exit-code $repo "refs/heads/$deploy_branch" ; then # deploy_branch exists in $repo; make sure we have the latest version disable_expanded_output git fetch --force $repo $deploy_branch:$deploy_branch enable_expanded_output fi # check if deploy_branch exists locally if git show-ref --verify --quiet "refs/heads/$deploy_branch" then incremental_deploy else initial_deploy fi restore_head } initial_deploy() { git --work-tree "$deploy_directory" checkout --orphan $deploy_branch git --work-tree "$deploy_directory" add --all commit+push } incremental_deploy() { #make deploy_branch the current branch git symbolic-ref HEAD refs/heads/$deploy_branch #put the previously committed contents of deploy_branch into the index git --work-tree "$deploy_directory" reset --mixed --quiet git --work-tree "$deploy_directory" add --all set +o errexit diff=$(git --work-tree "$deploy_directory" diff --exit-code --quiet HEAD --)$? set -o errexit case $diff in 0) echo No changes to files in $deploy_directory. Skipping commit.;; 1) commit+push;; *) echo git diff exited with code $diff. Aborting. Staying on branch $deploy_branch so you can debug. To switch back to master, use: git symbolic-ref HEAD refs/heads/master && git reset --mixed >&2 return $diff ;; esac } commit+push() { set_user_id git --work-tree "$deploy_directory" commit -m "$commit_message" disable_expanded_output #--quiet is important here to avoid outputting the repo URL, which may contain a secret token git push --quiet $repo $deploy_branch enable_expanded_output } #echo expanded commands as they are executed (for debugging) enable_expanded_output() { if [ $verbose ]; then set -o xtrace set +o verbose fi } #this is used to avoid outputting the repo URL, which may contain a secret token disable_expanded_output() { if [ $verbose ]; then set +o xtrace set -o verbose fi } set_user_id() { if [[ -z `git config user.name` ]]; then git config user.name "$default_username" fi if [[ -z `git config user.email` ]]; then git config user.email "$default_email" fi } restore_head() { if [[ $previous_branch = "HEAD" ]]; then #we weren't on any branch before, so just set HEAD back to the commit it was on git update-ref --no-deref HEAD $commit_hash $deploy_branch else git symbolic-ref HEAD refs/heads/$previous_branch fi git reset --mixed } filter() { sed -e "s|$repo|\$repo|g" } sanitize() { "$@" 2> >(filter 1>&2) | filter } [[ $1 = --source-only ]] || main "$@" python-31.0.0/newversion.sh000077500000000000000000000010141415340367700156600ustar00rootroot00000000000000#!/bin/bash set -e version="$1" if [[ -z "$version" ]]; then echo "Usage: $0 " >&2 echo "No version set" >&2 exit 1 fi sed -i -e "s/^Version.*/Version: \\t${version}/" python-varlink.spec curl -L -O https://github.com/varlink/python-varlink/archive/${version}/python-varlink-${version}.tar.gz rm -fr docs/build python3 setup.py build_sphinx --source-dir=docs/ --build-dir=docs/build --all-files GIT_DEPLOY_DIR=$(pwd)/docs/build/html GIT_DEPLOY_BRANCH=gh-pages ./git-deploy-branch.sh -m "doc update" python-31.0.0/pyproject.toml000066400000000000000000000005721415340367700160460ustar00rootroot00000000000000[build-system] requires = [ "setuptools >= 44; python_version < '3'", "setuptools >= 48; python_version > '3'", "setuptools_scm >= 4, <6; python_version < '3'", "setuptools_scm[toml]; python_version > '3'", "setuptools_scm_git_archive", "wheel >= 0.29.0", ] build-backend = 'setuptools.build_meta' [tool.setuptools_scm] local_scheme = "no-local-version"python-31.0.0/python-varlink.spec000066400000000000000000000032431415340367700167710ustar00rootroot00000000000000%if 0%{?fedora} || 0%{?rhel} >= 8 %global build_py3 1 %endif Name: python-varlink Version: 30.3.1 Release: 1%{?dist} Summary: Python implementation of Varlink License: ASL 2.0 URL: https://github.com/varlink/%{name} Source0: https://github.com/varlink/%{name}/archive/%{version}/%{name}-%{version}.tar.gz BuildArch: noarch BuildRequires: python2-devel BuildRequires: python-rpm-macros BuildRequires: python-setuptools BuildRequires: python-setuptools_scm %if 0%{?build_py3} BuildRequires: python3-devel BuildRequires: python3-rpm-macros %endif %global _description \ An python module for Varlink with client and server support. %description %_description %package -n python2-varlink Summary: %summary %{?python_provide:%python_provide python2-varlink} %if 0%{?build_py3} %package -n python3-varlink Summary: %summary %{?python_provide:%python_provide python3-varlink} %endif %description -n python2-varlink %_description %if 0%{?build_py3} %description -n python3-varlink %_description %endif %prep %autosetup -n python-%{version} %build export SETUPTOOLS_SCM_PRETEND_VERSION=%{version} %py2_build %if 0%{?build_py3} %py3_build %endif %if 0%{?build_py3} %check export SETUPTOOLS_SCM_PRETEND_VERSION=%{version} CFLAGS="%{optflags}" %{__python3} %{py_setup} %{?py_setup_args} check %endif %install export SETUPTOOLS_SCM_PRETEND_VERSION=%{version} %py2_install %if 0%{?build_py3} %py3_install %endif %files -n python2-varlink %license LICENSE.txt %doc README.md %{python2_sitelib}/* %if 0%{?build_py3} %files -n python3-varlink %license LICENSE.txt %doc README.md %{python3_sitelib}/* %endif %changelog python-31.0.0/requirements.txt000066400000000000000000000000071415340367700164070ustar00rootroot00000000000000future python-31.0.0/setup.cfg000066400000000000000000000022001415340367700147410ustar00rootroot00000000000000[metadata] name = varlink description = Python implementation of the Varlink protocol long_description = file: README.md long_description_content_type = text/markdown author = Lars Karlitski, Harald Hoyer author_email = harald@hoyer.xyz url = https://github.com/varlink/python keywords = ipc, varlink, rpc license = ASL 2.0 classifiers = Development Status :: 5 - Production/Stable Intended Audience :: Developers License :: OSI Approved :: Apache Software License Programming Language :: Python Programming Language :: Python :: 2.7 Programming Language :: Python :: 3 Programming Language :: Python :: 3.5 Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 Topic :: System :: Networking [options] zip_safe = False python_requires=>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.* include_package_data = True packages = varlink setup_requires = setuptools_scm < 6 ; python_version < "3" setuptools_scm ; python_version >= "3" [options.package_data] varlink = *.varlink [bdist_wheel] universal = 1 python-31.0.0/setup.py000066400000000000000000000005301415340367700146360ustar00rootroot00000000000000from setuptools import setup def local_scheme(_): """Enables a version format so that upload to TestPyPI is successful. For example: 2.6.2.dev8 See https://github.com/pypa/setuptools_scm/issues/342. """ return "" setup( use_scm_version={"local_scheme": "no-local-version"}, setup_requires=['setuptools_scm'], ) python-31.0.0/test-requirements.txt000066400000000000000000000000251415340367700173640ustar00rootroot00000000000000future fixtures nose2python-31.0.0/tox.ini000066400000000000000000000013471415340367700144460ustar00rootroot00000000000000# tox (https://tox.readthedocs.io/) is a tool for running tests # in multiple virtualenvs. This configuration file will run the # test suite on all supported python versions. To use it, "pip install tox" # and then run "tox" from this directory. [tox] envlist = py310,py39,py38,py37,py36,py27,pep8,pypy2,pypy3 [testenv] usedevelop = True install_command = pip install {opts} {packages} setenv = VIRTUAL_ENV={envdir} BRANCH_NAME=master CLIENT_NAME=varlink deps = -r{toxinidir}/test-requirements.txt -r{toxinidir}/requirements.txt commands = nose2 \ --coverage varlink [testenv:py27] commands = python -m unittest varlink [testenv:pypy2] commands = python -m unittest varlink [travis] python = 3.7: py37 python-31.0.0/varlink/000077500000000000000000000000001415340367700145745ustar00rootroot00000000000000python-31.0.0/varlink/__init__.py000066400000000000000000000047741415340367700167210ustar00rootroot00000000000000"""An implementation of the varlink protocol See https://www.varlink.org for more information about the varlink protocol and interface definition files. For server implementations use the :class:`varlink.Server` class. For client implementations use the :class:`varlink.Client` class. For installation and examples, see the GIT repository https://github.com/varlink/python. or the `source code <_modules/varlink/tests/test_orgexamplemore.html>`_ of :mod:`varlink.tests.test_orgexamplemore` """ import os if hasattr(os, "fork"): __all__ = ['Client', 'ClientInterfaceHandler', 'SimpleClientInterfaceHandler', 'Service', 'RequestHandler', 'Server', 'ThreadingServer', 'ForkingServer', 'InterfaceNotFound', 'MethodNotFound', 'MethodNotImplemented', 'InvalidParameter', 'ConnectionError', 'VarlinkEncoder', 'VarlinkError', 'Interface', 'Scanner', 'get_listen_fd'] from .server import ForkingServer else: __all__ = ['Client', 'ClientInterfaceHandler', 'SimpleClientInterfaceHandler', 'Service', 'RequestHandler', 'Server', 'ThreadingServer', 'InterfaceNotFound', 'MethodNotFound', 'MethodNotImplemented', 'InvalidParameter', 'ConnectionError', 'VarlinkEncoder', 'VarlinkError', 'Interface', 'Scanner', 'get_listen_fd'] from .client import (Client, ClientInterfaceHandler, SimpleClientInterfaceHandler) from .error import (VarlinkEncoder, VarlinkError, InvalidParameter, InterfaceNotFound, MethodNotImplemented, MethodNotFound, ConnectionError, BrokenPipeError) from .scanner import (Scanner, Interface) from .server import (Service, get_listen_fd, Server, ThreadingServer, RequestHandler) # There are no tests here, so don't try to run anything discovered from # introspecting the symbols (e.g. FunctionTestCase). Instead, all our # tests come from within varlink.tests. def load_tests(loader, tests, pattern): import os.path import sys from fnmatch import fnmatch if pattern == None: pattern = "test_*.py" # top level directory cached on loader instance test_dir = os.path.dirname(__file__) + "/tests" for fn in os.listdir(test_dir): if fnmatch(fn, pattern): if sys.version_info[0] == 2 and fn == "test_mocks.py": continue modname = "varlink.tests." + fn[:-3] __import__(modname) module = sys.modules[modname] tests.addTest(loader.loadTestsFromModule(module)) return tests python-31.0.0/varlink/cli.py000066400000000000000000000206751415340367700157270ustar00rootroot00000000000000# /usr/bin/env python """varlink cli tool Call with: $ python -m varlink.cli --help """ from __future__ import print_function from __future__ import unicode_literals import argparse import json import shlex import socket import sys import varlink def varlink_call(args): deli = args.METHOD.rfind(".") if deli == -1: print("No method found", file=sys.stderr) sys.exit(1) method = args.METHOD[deli + 1:] interface = args.METHOD[:deli] deli = interface.rfind("/") if deli != -1: address = interface[:deli] interface = interface[deli + 1:] else: address = None def new_client(address): if address: client = varlink.Client.new_with_address(address) else: if args.activate: client = varlink.Client.new_with_activate(shlex.split(args.activate)) elif args.bridge: client = varlink.Client.new_with_bridge(shlex.split(args.bridge)) else: client = varlink.Client.new_with_resolved_interface(interface, args.resolver) return client with new_client(address) as client: got = False try: with client.open(interface) as con: out = {'method': interface + '.' + method, 'more': args.more, 'parameters': json.loads(args.ARGUMENTS or "{}" )} con._send_message(json.dumps(out, cls=varlink.VarlinkEncoder).encode('utf-8')) more = True while more: (message, more) = con._next_varlink_message() if message: print(json.dumps(message, cls=varlink.VarlinkEncoder, indent=2, sort_keys=True)) got = True except varlink.VarlinkError as e: print(e, file=sys.stderr) except varlink.BrokenPipeError: if not got or args.more: print("Connection closed") sys.exit(1) def varlink_bridge(args): message = b'' last_interface = None con = None client = None if args.connect: client = varlink.Client.new_with_address(args.connect) con = client.open_connection() elif args.bridge: client = varlink.Client.new_with_bridge(shlex.split(args.bridge)) con = client.open_connection() elif args.activate: client = varlink.Client.new_with_activate(shlex.split(args.activate)) con = client.open_connection() else: print("No --connect or --bridge or --activate") if hasattr(sys.stdout, 'buffer'): stdout = sys.stdout.buffer else: stdout = sys.stdout if hasattr(sys.stdin, 'buffer'): stdin = sys.stdin.buffer else: stdin = sys.stdin while not sys.stdin.closed: c = stdin.read(1) if c == b'': break if c != b'\0': message += c continue req = json.loads(message.decode('utf-8')) if not args.connect and not args.activate and not args.bridge: if req['method'] == "org.varlink.service.GetInfo": req['method'] = "org.varlink.resolver.GetInfo" interface_name, _, method_name = req.get('method', '').rpartition('.') if req['method'] == "org.varlink.service.GetInterfaceDescription": resolving_interface = req['parameters']['interface'] else: resolving_interface = interface_name if not interface_name or not method_name: stdout.write(json.dumps(varlink.InterfaceNotFound(interface_name), cls=varlink.VarlinkEncoder).encode( 'utf-8') + b'\0') sys.stdout.flush() continue if last_interface != resolving_interface: if con: if hasattr(con, 'shutdown'): con.shutdown(socket.SHUT_RDWR) else: con.close() if client: client.cleanup() try: client = varlink.Client.new_with_resolved_interface(resolving_interface, resolver_address=args.resolver) except varlink.VarlinkError as e: stdout.write( json.dumps(e, cls=varlink.VarlinkEncoder).encode( 'utf-8') + b'\0') sys.stdout.flush() continue con = client.open_connection() last_interface = resolving_interface if hasattr(con, "send"): con.send(message + b'\0') else: con.write(message + b'\0') if req.get("oneway", False): continue message = b'' ret_message = b'' while True: c = con.recv(1) if c == b'': break if c != b'\0': ret_message += c continue stdout.write(ret_message + b'\0') sys.stdout.flush() ret = json.loads(ret_message.decode('utf-8')) ret_message = b'' if not ret.get('continues', False): break if ret.get('upgraded', False): raise NotImplementedError("Bridging upgraded connection not yet supported") def varlink_help(args): deli = args.INTERFACE.rfind("/") if deli != -1: address = args.INTERFACE[:deli] interface_name = args.INTERFACE[deli + 1:] client = varlink.Client.new_with_address(address) else: interface_name = args.INTERFACE if args.activate: client = varlink.Client.new_with_activate(shlex.split(args.activate)) elif args.bridge: client = varlink.Client.new_with_bridge(shlex.split(args.bridge)) else: client = varlink.Client.new_with_resolved_interface(interface_name, args.resolver) interface = client.get_interface(interface_name) del client print(interface.description) def varlink_info(args): if args.ADDRESS: client = varlink.Client.new_with_address(args.ADDRESS) elif args.bridge: client = varlink.Client.new_with_bridge(shlex.split(args.bridge)) elif args.activate: client = varlink.Client.new_with_activate(shlex.split(args.activate)) else: print("No ADDRESS or --bridge or --activate") client.get_interfaces() info = client.info del client print("Vendor:", info["vendor"]) print("Product:", info["product"]) print("Version:", info["version"]) print("URL:", info["url"]) print("Interfaces:") for i in info["interfaces"]: print(" ", i) if __name__ == '__main__': parser = argparse.ArgumentParser() subparsers = parser.add_subparsers(title="commands") parser.add_argument('-r', '--resolver', default=None, help='address of the resolver') parser.add_argument('-A', '--activate', default=None, help='Service to socket-activate and connect to. ' 'The temporary UNIX socket address is exported as $VARLINK_ADDRESS.') parser.add_argument('-b', '--bridge', default=None, help='Command to execute and connect to') parser_info = subparsers.add_parser('info', help='Print information about a service') parser_info.add_argument('ADDRESS', nargs='?') parser_info.set_defaults(func=varlink_info) parser_help = subparsers.add_parser('help', help='Print interface description or service information') parser_help.add_argument('INTERFACE') parser_help.set_defaults(func=varlink_help) parser_bridge = subparsers.add_parser('bridge', help='Bridge varlink messages to services on this machine') parser_bridge.add_argument('-c', '--connect', default=None, help='Optional varlink address to connect to, ' 'without using the resolver.') parser_bridge.set_defaults(func=varlink_bridge) parser_call = subparsers.add_parser('call', help='Call a method') parser_call.add_argument('-m', '--more', action='store_true', help='wait for multiple method returns if supported') parser_call.add_argument('METHOD') parser_call.add_argument('ARGUMENTS', nargs='?', default="") parser_call.set_defaults(func=varlink_call) args = parser.parse_args() if not hasattr(args, "func"): parser.print_usage(sys.stderr) sys.exit(1) args.func(args) python-31.0.0/varlink/client.py000066400000000000000000000543161415340367700164350ustar00rootroot00000000000000# coding=utf-8 from __future__ import print_function from __future__ import unicode_literals import json import os import shutil import signal import socket import sys import tempfile import subprocess import threading try: from builtins import next from builtins import object from builtins import open from builtins import str except ImportError: pass from .error import (VarlinkError, InterfaceNotFound, VarlinkEncoder, BrokenPipeError) from .scanner import (Interface, _Method) PY2 = sys.version_info[0] == 2 PY3 = (sys.version_info[0] >= 3) if PY2: FileNotFoundError = IOError ChildProcessError = OSError class ConnectionError(OSError): pass class ClientInterfaceHandler(object): """Base class for varlink client, which wraps varlink methods of an interface to the class""" def __init__(self, interface, namespaced=False): """Base class for varlink client, which wraps varlink methods of an interface. The object allows to talk to a varlink service, which implements the specified interface transparently by calling the methods. The call blocks until enough messages are received. For monitor calls with '_more=True' a generator object is returned. :param interface: an Interface object :param namespaced: if True, varlink methods return SimpleNamespace objects instead of dictionaries """ if not isinstance(interface, Interface): raise TypeError self._interface = interface self._namespaced = namespaced self._in_use = False for member in interface.members.values(): if isinstance(member, _Method): self._add_method(member) def close(self): """To be implemented.""" raise NotImplementedError def _send_message(self, out): """To be implemented. This should send a varlink message to the varlink service adding a trailing zero byte. """ raise NotImplementedError def _next_message(self): """To be implemented. This must be a generator yielding the next received varlink message without the trailing zero byte. """ raise NotImplementedError def _add_method(self, method): def _wrapped(*args, **kwargs): if "_more" in kwargs and kwargs.pop("_more"): return self._call_more(method.name, *args, **kwargs) else: return self._call(method.name, *args, **kwargs) if sys.version_info.major >= 3: _wrapped.__name__ = method.name else: _wrapped.__name__ = method.name.encode("latin-1") # FIXME: add comments if method.signature: if method.doc: _wrapped.__doc__ = method.doc + "\n" else: _wrapped.__doc__ = "" "\n" _wrapped.__doc__ += method.signature setattr(self, method.name, _wrapped) def _next_varlink_message(self): message = next(self._next_message()) message = json.loads(message) if not 'parameters' in message: message['parameters'] = {} if 'error' in message and message["error"] != None: self._in_use = False e = VarlinkError.new(message, self._namespaced) raise e else: return message['parameters'], ('continues' in message) and message['continues'] def _call(self, method_name, *args, **kwargs): if self._in_use: raise ConnectionError("Tried to call a varlink method, while other call still in progress") oneway = False if "_oneway" in kwargs and kwargs.pop("_oneway"): oneway = True method = self._interface.get_method(method_name) parameters = self._interface.filter_params("client.call", method.in_type, False, args, kwargs) out = {'method': self._interface.name + "." + method_name} if oneway: out['oneway'] = True if parameters: out['parameters'] = parameters self._send_message(json.dumps(out, cls=VarlinkEncoder).encode('utf-8')) if oneway: return None self._in_use = True (message, more) = self._next_varlink_message() if more: self.close() self._in_use = False raise ConnectionError("Server indicated more varlink messages") self._in_use = False if message: message = self._interface.filter_params("client.reply", method.out_type, self._namespaced, message, None) return message def _call_more(self, method_name, *args, **kwargs): if self._in_use: raise ConnectionError("Tried to call a varlink method, while other call still in progress") method = self._interface.get_method(method_name) parameters = self._interface.filter_params("client.call", method.in_type, False, args, kwargs) out = {'method': self._interface.name + "." + method_name, 'more': True, 'parameters': parameters} self._send_message(json.dumps(out, cls=VarlinkEncoder).encode('utf-8')) more = True self._in_use = True while more: (message, more) = self._next_varlink_message() if message: message = self._interface.filter_params("client.reply", method.out_type, self._namespaced, message, None) yield message self._in_use = False class SimpleClientInterfaceHandler(ClientInterfaceHandler): """A varlink client for an interface doing send/write and receive/read on a socket or file stream""" def __init__(self, interface, file_or_socket, namespaced=False): """Creates an object with the varlink methods of an interface installed. The object allows to talk to a varlink service, which implements the specified interface transparently by calling the methods. The call blocks until enough messages are received. For monitor calls with '_more=True' a generator object is returned. :param interface: an Interface object :param file_or_socket: an open socket or io stream :param namespaced: if True, varlink methods return SimpleNamespace objects instead of dictionaries """ ClientInterfaceHandler.__init__(self, interface, namespaced=namespaced) self._connection = file_or_socket if hasattr(self._connection, 'send_bytes'): self._send_bytes = True self._sendall = False elif hasattr(self._connection, 'sendall'): self._send_bytes = False self._sendall = True else: if not hasattr(self._connection, 'write'): raise TypeError self._sendall = False self._send_bytes = False if hasattr(self._connection, 'recv_bytes'): self._recv_bytes = True self._recv = False elif hasattr(self._connection, 'recv'): self._recv = True self._recv_bytes = False else: if not hasattr(self._connection, 'read'): raise TypeError self._recv = False self._recv_bytes = False self._in_buffer = b'' def __enter__(self): return self def __exit__(self, _type, _value, _traceback): self.close() def close(self): try: if hasattr(self._connection, 'shutdown'): self._connection.shutdown(socket.SHUT_RDWR) except: pass self._connection.close() def _send_message(self, out): if self._send_bytes: self._connection.send_bytes(out + b'\0') elif self._sendall: self._connection.sendall(out + b'\0') elif hasattr: self._connection.write(out + b'\0') def _next_message(self): while True: message, sep, self._in_buffer = self._in_buffer.partition(b'\0') if not sep: # No zero byte found self._in_buffer = message message = None if message: yield message.decode('utf-8') continue if self._recv_bytes: data = self._connection.recv_bytes(8192) elif self._recv: data = self._connection.recv(8192) else: data = self._connection.read(1) if len(data) == 0: raise BrokenPipeError("Disconnected") self._in_buffer += data def pipe_bridge(reader, writer): if hasattr(reader, "recv"): recv = True else: recv = False if hasattr(writer, "sendall"): sendall = True else: sendall = False while True: try: if recv: data = reader.recv(8192) else: data = reader.read(1) except: data = [] if len(data) == 0: reader.close() writer.close() return try: if sendall: writer.sendall(data) elif hasattr: writer.write(data) writer.flush() except Exception as e: reader.close() writer.close() return class Client(object): """Varlink client class. >>> with varlink.Client("unix:/run/org.example.ping") as client, client.open('org.example.ping') as connection: >>> assert connection.Ping("Test")["pong"] == "Test" If the varlink resolver is running: >>> client = varlink.Client(resolve_interface='com.redhat.logging') >>> print(client.get_interfaces()['com.redhat.logging'].get_description()) # Query and monitor the log messages of a system. interface com.redhat.logging type Entry (cursor: string, time: string, message: string, process: string, priority: string) # Monitor the log. Returns the @initial_lines most recent entries in the # first reply and then continuously replies when new entries are available. method Monitor(initial_lines: int) -> (entries: Entry[]) >>> connection = client.open("com.redhat.logging") connection now holds an object with all the varlink methods available. Do varlink method call with varlink arguments and a single varlink return structure wrapped in a namespace class: >>> ret = connection.Monitor(initial_lines=1) >>> ret namespace(entries=[namespace(cursor='s=[…]', message="req:1 'dhcp4-change' [wlp3s0][…]", priority='critical', process='nm-dispatcher', time='2018-01-29 12:19:59Z')]) >>> ret.entries[0].process 'nm-dispatcher' Do varlink method call with varlink arguments and a multiple return values in monitor mode, using the "_more" keyword: >>> for m in connection.Monitor(_more=True): >>> for e in m.entries: >>> print("%s: %s" % (e.time, e.message)) 2018-01-29 12:19:59Z: [system] Activating via systemd: service name='[…] 2018-01-29 12:19:59Z: Starting Network Manager Script Dispatcher Service... 2018-01-29 12:19:59Z: bound to 10.200.159.150 -- renewal in 1423 seconds. 2018-01-29 12:19:59Z: [system] Successfully activated service 'org.freedesktop.nm_dispatcher' 2018-01-29 12:19:59Z: Started Network Manager Script Dispatcher Service. 2018-01-29 12:19:59Z: req:1 'dhcp4-change' [wlp3s0]: new request (6 scripts) 2018-01-29 12:19:59Z: req:1 'dhcp4-change' [wlp3s0]: start running ordered scripts... "_more" is special to this python varlink binding. If "_more=True", then the method call does not return a normal namespace wrapped varlink return value, but a generator, which yields the return values and waits (blocks) for the service to return more return values in the generator's .__next__() call. """ handler = SimpleClientInterfaceHandler def __init__(self, address=None, resolve_interface=None, resolver=None): """Creates a Client object to reach the interfaces of a varlink service. For more constructors see the class constructor methods new_with_*() returning an Client object. :param address: the exact address like "unix:/run/org.varlink.resolver" :param resolve_interface: an interface name, which is resolved with the system wide resolver :param resolver: the exact address of the resolver to be used to resolve the interface name :exception ConnectionError: could not connect to the service or resolver """ self._interfaces = {} self._socket = None self._socket_fn = None self._tmpdir = None self._child_pid = 0 self._str = "Client" with open(os.path.join(os.path.dirname(__file__), 'org.varlink.service.varlink')) as f: interface = Interface(f.read()) self.add_interface(interface) if resolve_interface: self._with_interface(resolve_interface, resolver) if address: self._with_address(address) def __enter__(self): return self def __exit__(self, exc, value, tb): self.cleanup() def __str__(self): return self._str @classmethod def new_with_activate(cls, argv): """Creates a Client object to a varlink service server started via socket activation. :param argv: executable in argv[0] and parameters in argv[1:] to run the varlink service server via socket activation. """ return cls()._with_activate(argv) def _with_activate(self, argv): s = socket.socket(socket.AF_UNIX) s.setblocking(False) self._tmpdir = tempfile.mkdtemp() address = self._tmpdir + "/" + str(os.getpid()) s.bind(address) s.listen(100) self._child_pid = os.fork() if self._child_pid == 0: # child n = s.fileno() if n == 3: # without dup() the socket is closed with the python destructor n = os.dup(3) del s else: try: os.close(3) except OSError: pass os.dup2(n, 3) address = address.replace('\0', '@', 1) for i in range(1, len(argv)): argv[i] = argv[i].replace("$VARLINK_ADDRESS", "unix:" + address) os.environ["VARLINK_ADDRESS"] = "unix:" + address os.environ["LISTEN_FDS"] = "1" os.environ["LISTEN_FDNAMES"] = "varlink" os.environ["LISTEN_PID"] = str(os.getpid()) os.execvp(argv[0], argv) sys.exit(1) # parent s.close() self._with_address("unix:" + address) return self @classmethod def new_with_bridge(cls, argv): """Creates a Client object to a varlink service started via the bridge command. The bridge command like "ssh varlink bridge" is executed for every connection. This client object will do IO via stdio to the bridge command. :param argv: executable in argv[0] and parameters in argv[1:] to run the varlink service server via the bridge connection. """ return cls()._with_bridge(argv) def _with_bridge(self, argv): def new_bridge_socket(): sp = socket.socketpair() p = subprocess.Popen(argv, stdin=sp[1], stdout=sp[1], close_fds=True) sp[1].close() self._child_pid = p.pid return sp[0] def new_bridge_socket_compat(): sp = socket.socketpair() p = subprocess.Popen(" ".join(argv), shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, close_fds=True) self._child_pid = p.pid thread1 = threading.Thread(daemon=True, target=pipe_bridge, args=(sp[1], p.stdin)) thread1.start() thread2 = threading.Thread(daemon=True, target=pipe_bridge, args=(p.stdout, sp[1])) thread2.start() return sp[0] self._str = "Bridge with: '%s'" % " ".join(argv) if sys.platform == 'win32': self._socket_fn = new_bridge_socket_compat else: self._socket_fn = new_bridge_socket return self @classmethod def new_with_address(cls, address): """Creates a Client object to reach the interfaces of a varlink service. :param address: the exact address like "unix:/run/org.varlink.resolver" :exception ConnectionError: could not connect to the service or resolver """ return cls()._with_address(address) def _with_address(self, address): if address.startswith("unix:"): self._str = address address = address[5:] mode = address.find(';') if mode != -1: address = address[:mode] if address[0] == '@': address = address.replace('@', '\0', 1) def open_unix(): s = socket.socket(socket.AF_UNIX) s.setblocking(True) s.connect(address) return s self._socket_fn = open_unix elif address.startswith("tcp:"): self._str = address address = address[4:] p = address.rfind(':') if p != -1: port = address[p + 1:] address = address[:p] else: raise ConnectionError("Invalid address 'tcp:%s'" % address) address = address.replace('[', '') address = address.replace(']', '') def open_tcp(): s = socket.create_connection((address, int(port))) s.setblocking(True) return s self._socket_fn = open_tcp elif address is not None: raise ConnectionError("Invalid address '%s'" % address) return self @classmethod def new_with_resolved_interface(cls, interface, resolver_address=None): """Creates a Client object to reach the interfaces of a varlink service. :param interface: an interface name, which is resolved with the system wide resolver :param resolver_address: the exact address of the resolver to be used to resolve the interface name :exception ConnectionError: could not connect to the service or resolver """ return cls()._with_resolved_interface(interface, resolver_address) def _with_resolved_interface(self, interface, resolver_address=None): if not resolver_address: resolver_address = "unix:/run/org.varlink.resolver" if interface == 'org.varlink.resolver': self._with_address(resolver_address) else: with Client.new_with_address(resolver_address) as client, \ client.open('org.varlink.resolver') as _rc: # noinspection PyUnresolvedReferences _r = _rc.Resolve(interface) self._with_address(_r['address']) return self def cleanup(self): if hasattr(self, "_tmpdir") and self._tmpdir != None: try: shutil.rmtree(self._tmpdir) except FileNotFoundError: pass if hasattr(self, "_child_pid") and self._child_pid != 0: try: os.kill(self._child_pid, signal.SIGTERM) except OSError: pass try: os.waitpid(self._child_pid, 0) except: pass def open(self, interface_name, namespaced=False, connection=None): """Open a new connection and get a client interface handle with the varlink methods installed. :param interface_name: an interface name, which the service this client object is connected to, provides. :param namespaced: If arguments and return values are instances of SimpleNamespace rather than dictionaries. :param connection: If set, get the interface handle for an already opened connection. :exception InterfaceNotFound: if the interface is not found """ if not connection: connection = self.open_connection() if interface_name not in self._interfaces: self.get_interface(interface_name, socket_connection=connection) if interface_name not in self._interfaces: raise InterfaceNotFound(interface_name) return self.handler(self._interfaces[interface_name], connection, namespaced=namespaced) def open_connection(self): """Open a new connection and return the socket. :exception OSError: anything socket.connect() throws """ return self._socket_fn() def get_interfaces(self, socket_connection=None): """Returns the a list of Interface objects the service implements.""" if not socket_connection: socket_connection = self.open_connection() close_socket = True else: close_socket = False # noinspection PyUnresolvedReferences _service = self.handler(self._interfaces["org.varlink.service"], socket_connection) self.info = _service.GetInfo() if close_socket: socket_connection.close() return self.info['interfaces'] def get_interface(self, interface_name, socket_connection=None): if not socket_connection: socket_connection = self.open_connection() close_socket = True else: close_socket = False _service = self.handler(self._interfaces["org.varlink.service"], socket_connection) # noinspection PyUnresolvedReferences desc = _service.GetInterfaceDescription(interface_name) interface = Interface(desc['description']) self._interfaces[interface.name] = interface if close_socket: socket_connection.close() return interface def add_interface(self, interface): """Manually add or overwrite an interface definition from an Interface object. :param interface: an Interface() object """ if not isinstance(interface, Interface): raise TypeError self._interfaces[interface.name] = interface python-31.0.0/varlink/error.py000066400000000000000000000101271415340367700163000ustar00rootroot00000000000000from __future__ import print_function from __future__ import unicode_literals import json try: from types import SimpleNamespace ConnectionError = ConnectionError BrokenPipeError = BrokenPipeError except: # Python 2 from argparse import Namespace as SimpleNamespace class ConnectionError(OSError): pass class BrokenPipeError(ConnectionError): pass class VarlinkEncoder(json.JSONEncoder): """The Encoder used to encode JSON""" def default(self, o): if isinstance(o, set): return dict.fromkeys(o, {}) if isinstance(o, SimpleNamespace): return o.__dict__ if isinstance(o, VarlinkError): return o.as_dict() return json.JSONEncoder.default(self, o) class VarlinkError(Exception): """The base class for varlink error exceptions""" @classmethod def new(cls, message, namespaced=False): if message['error'] == 'org.varlink.service.InterfaceNotFound': return InterfaceNotFound.new(message, namespaced) elif message['error'] == 'org.varlink.service.InvalidParameter': return InvalidParameter.new(message, namespaced) elif message['error'] == 'org.varlink.service.MethodNotImplemented': return MethodNotImplemented.new(message, namespaced) elif message['error'] == 'org.varlink.service.MethodNotImplemented': return MethodNotImplemented.new(message, namespaced) else: return cls(message, namespaced) def __init__(self, message, namespaced=False): if not namespaced and not isinstance(message, dict): raise TypeError # normalize to dictionary Exception.__init__(self, json.loads(json.dumps(message, cls=VarlinkEncoder))) def error(self): """returns the exception varlink error name""" return self.args[0].get('error') def parameters(self, namespaced=False): """returns the exception varlink error parameters""" if namespaced: return json.loads(json.dumps(self.args[0]['parameters']), object_hook=lambda d: SimpleNamespace(**d)) else: return self.args[0].get('parameters') def as_dict(self): return self.args[0] class InterfaceNotFound(VarlinkError): """The standardized varlink InterfaceNotFound error as a python exception""" @classmethod def new(cls, message, namespaced=False): return cls(namespaced and message['parameters'].interface or message['parameters'].get('interface', None)) def __init__(self, interface): VarlinkError.__init__(self, {'error': 'org.varlink.service.InterfaceNotFound', 'parameters': {'interface': interface}}) class MethodNotFound(VarlinkError): """The standardized varlink MethodNotFound error as a python exception""" @classmethod def new(cls, message, namespaced=False): return cls(namespaced and message['parameters'].method or message['parameters'].get('method', None)) def __init__(self, method): VarlinkError.__init__(self, {'error': 'org.varlink.service.MethodNotFound', 'parameters': {'method': method}}) class MethodNotImplemented(VarlinkError): """The standardized varlink MethodNotImplemented error as a python exception""" @classmethod def new(cls, message, namespaced=False): return cls(namespaced and message['parameters'].method or message['parameters'].get('method', None)) def __init__(self, method): VarlinkError.__init__(self, {'error': 'org.varlink.service.MethodNotImplemented', 'parameters': {'method': method}}) class InvalidParameter(VarlinkError): """The standardized varlink InvalidParameter error as a python exception""" @classmethod def new(cls, message, namespaced=False): return cls(namespaced and message['parameters'].parameter or message['parameters'].get('parameter', None)) def __init__(self, name): VarlinkError.__init__(self, {'error': 'org.varlink.service.InvalidParameter', 'parameters': {'parameter': name}}) python-31.0.0/varlink/mock.py000066400000000000000000000243411415340367700161030ustar00rootroot00000000000000import datetime import inspect import os import subprocess import sys import textwrap import time import uuid import varlink if sys.version_info[0] == 2: raise ImportError("The mock module isn't compatible with python 2") def cast_type(typeof): cast = {'str': 'string'} typeof = str(typeof).replace("", "") return cast.get(typeof, typeof) def get_ignored(): ignore = dir(MockedService) return ignore def get_interface_attributs(interface, ignored): attributs = {"callables": [], "others": []} for attr in dir(interface): if attr in ignored: continue attribut = getattr(interface, attr) if callable(attribut): attributs["callables"].append(attr) else: attributs["others"].append(attr) return attributs def generate_callable_interface(interface, attr): attribut = getattr(interface, attr) signature = inspect.signature(attribut) params = signature.parameters.values() sign = [] for param in params: if param.name == "self": continue typeof = param.annotation sign.append("{}: {}".format(param.name, cast_type(typeof))) returned = signature.return_annotation if returned: returned = cast_type(returned) doc = attribut.__doc__ if not doc: raise ValueError( "docstring format must be:" "return name: type") doc = doc.replace("return ", "") if ":" in doc: returned = doc else: returned = "{}: {}".format(doc, returned) else: returned = "" return "method {name}({signature}) -> ({returned})".format( name=attr, signature=",".join(sign), returned=returned ) class MockedServiceProcess(): address = None vendor = None product = None version = None url = None interface = None interface_file = None interface_name = None interface_content = None service_to_mock = None def run(self): mocked_service = varlink.Service( vendor=self.vendor, product=self.product, version=self.version, url=self.url) instanciated_service = self.service_to_mock() mocked_service._set_interface( self.interface_file, instanciated_service) class ServiceRequestHandler(varlink.RequestHandler): service = mocked_service self.varlink_server = varlink.ThreadingServer( self.address, ServiceRequestHandler) self.varlink_server.serve_forever() def service_generator(service, info, filename="mockedservice.py"): with open(filename, "w+") as pyfp: pyfp.write(textwrap.dedent("""\ ''' Generated by varlink mocking system {datetime} Only for testing purpose and unit testing ''' """.format(datetime=datetime.datetime.now()))) pyfp.write("import varlink\n\n") pyfp.write(inspect.getsource(service)) pyfp.write("\n\n") pyfp.write(inspect.getsource(MockedServiceProcess)) pyfp.write("\n\n") pyfp.write("if __name__ == '__main__':\n") pyfp.write(" msp = MockedServiceProcess()\n") for key, value in info.items(): surround = "'" if value["type"] == "raw": surround = "" pyfp.write(" msp.{key} = {surround}{value}{surround}\n".format( key=key, value=value["value"], surround=surround)) pyfp.write(" msp.run()\n") def mockedservice(fake_service=None, fake_types=None, address='unix:@test', name=None, vendor='varlink', product='mock', version=1, url='http://localhost'): """ Varlink mocking service To mock a fake service and merely test your varlink client against. The mocking feature is for testing purpose, it's allow you to test your varlink client against a fake service which will returned self handed result defined in your object who will be mocked. Example: >>> import unittest >>> from varlink import mock >>> import varlink >>> >>> >>> types = ''' >>> type MyPersonalType ( >>> foo: string, >>> bar: string, >>> ) >>> ''' >>> >>> >>> class Service(): >>> >>> def Test1(self, param1: int) -> dict: >>> ''' >>> return test: MyPersonalType >>> ''' >>> return { >>> "test": { >>> "foo": "bim", >>> "bar": "boom" >>> } >>> } >>> >>> def Test2(self, param1: str) -> dict: >>> ''' >>> return (test: string) >>> ''' >>> return {"test": param1} >>> >>> def Test3(self, param1: int) -> dict: >>> ''' >>> return (test: int, boom: string, foo: string, bar: 42) >>> ''' >>> return { >>> "test": param1 * 2, >>> "boom": "foo", >>> "foo": "bar", >>> "bar": 42, >>> } >>> >>> >>> class TestMyClientWithMockedService(unittest.TestCase): >>> >>> @mock.mockedservice( >>> fake_service=Service, >>> fake_types=types, >>> name='org.service.com', >>> address='unix:@foo' >>> ) >>> def test_my_client_against_a_mock(self): >>> with varlink.Client("unix:@foo") as client: >>> connection = client.open('org.service.com') >>> self.assertEqual( >>> connection.Test1(param1=1)["test"]["bar"], "boom") >>> self.assertEqual( >>> connection.Test2(param1="foo")["test"], "foo") >>> self.assertEqual( >>> connection.Test3(param1=6)["test"], 12) >>> self.assertEqual( >>> connection.Test3(param1=6)["bar"], 42) First you need to define a sample class that will be passed to your decorator `mock.mockedservice` and then a service will be initialized and launched automatically, and after that you just need to connect your client to him and to establish your connection then now you can call your methods and it will give you the expected result. You can also mock some types too, to help you to mock more complex service and interfaces like podman by example. You can define the return type by using the method docstring like the method Test1 in our previous example. The mocking module is only compatible with python 3 or higher version of python because this module require annotation to generate interface description. If you try to use it with python 2.x it will raise an ``ImportError``. """ def decorator(func): def wrapper(*args, **kwargs): with MockedService(fake_service, fake_types, name=name, address=address): try: func(*args, **kwargs) except BrokenPipeError: # manage fake service stoping pass return return wrapper return decorator class MockedService(): def __init__(self, service, types, address='unix:@test', name=None, vendor='varlink', product='mock', version=1, url='http://localhost'): if not name: module = service.__module__ try: self.name = os.path.splitext(module)[1].replace('.', '') except IndexError: self.name = module else: self.name = name self.identifier = str(uuid.uuid4()) self.interface_description = None self.service = service self.types = types self.address = address self.vendor = vendor self.product = product self.version = version self.url = url self.service_info = { "address": {'type': 'inherited', 'value': address}, "vendor": {'type': 'inherited', 'value': vendor}, "product": {'type': 'inherited', 'value': product}, "version": {'type': 'raw', 'value': version}, "url": {'type': 'inherited', 'value': url}, "interface_name": {'type': 'inherited', 'value': self.name}, "interface_file": { 'type': 'inherited', 'value': self.get_interface_file_path()}, "service_to_mock": {'type': 'raw', 'value': service.__name__}, } self.generate_interface() def generate_interface(self): ignore = get_ignored() self.interface_description = ["interface {}".format(self.name)] if self.types: for line in self.types.split("\n"): self.interface_description.append(line) attributs = get_interface_attributs(self.service, ignore) for attr in attributs["callables"]: self.interface_description.append(generate_callable_interface( self.service, attr)) def get_interface_file_path(self): return "/tmp/{}".format(self.name) def generate_interface_file(self): tfp = open(self.get_interface_file_path(), "w+") tfp.write("\n".join(self.interface_description)) tfp.close() def delete_interface_files(self): os.remove(self.get_interface_file_path()) os.remove(self.mocked_service_file) def service_start(self): self.service_pid = subprocess.Popen( [sys.executable, self.mocked_service_file], env = { "PYTHONPATH": ':'.join(sys.path) } ) time.sleep(2) def service_stop(self): self.service_pid.kill() self.service_pid.communicate() def __enter__(self): self.mocked_service_file = "/tmp/{}".format(self.identifier) service_generator( self.service, self.service_info, filename=self.mocked_service_file) self.generate_interface_file() self.service_start() return self def __exit__(self, type, value, traceback): self.service_stop() self.delete_interface_files() python-31.0.0/varlink/org.varlink.service.varlink000066400000000000000000000016161415340367700220630ustar00rootroot00000000000000# The Varlink Service Interface is provided by every varlink service. It # describes the service and the interfaces it implements. interface org.varlink.service # Get a list of all the interfaces a service provides and information # about the implementation. method GetInfo() -> ( vendor: string, product: string, version: string, url: string, interfaces: []string ) # Get the description of an interface that is implemented by this service. method GetInterfaceDescription(interface: string) -> (description: string) # The requested interface was not found. error InterfaceNotFound (interface: string) # The requested method was not found error MethodNotFound (method: string) # The interface defines the requested method, but the service does not # implement it. error MethodNotImplemented (method: string) # One of the passed parameters is invalid. error InvalidParameter (parameter: string) python-31.0.0/varlink/scanner.py000066400000000000000000000346361415340367700166130ustar00rootroot00000000000000#!-*-coding:utf8-*- from __future__ import print_function from __future__ import unicode_literals try: from builtins import str from builtins import int from builtins import object from builtins import unicode except ImportError: pass import re try: basestring except NameError: basestring = str try: from types import SimpleNamespace except: # Python 2 from argparse import Namespace as SimpleNamespace try: from collections.abc import (Set, Mapping) except: # Python 2 from collections import (Set, Mapping) from collections import OrderedDict from .error import (MethodNotFound, InvalidParameter) class Scanner(object): """Class for scanning a varlink interface definition.""" def __init__(self, string): if hasattr(re, "ASCII"): ASCII = re.ASCII else: ASCII = 0 self.whitespace = re.compile(r'([ \t\n]|#.*$)+', ASCII | re.MULTILINE) self.docstring = re.compile(r'(?:.?)+#(.*)(?:\n|\r\n)') # FIXME: nested () self.method_signature = re.compile(r'([ \t\n]|#.*$)*(\([^)]*\))([ \t\n]|#.*$)*->([ \t\n]|#.*$)*(\([^)]*\))', ASCII | re.MULTILINE) self.keyword_pattern = re.compile(r'\b[a-z]+\b|[:,(){}]|->|\[\]|\?|\[string\]\(\)|\[string\]', ASCII) self.patterns = { 'interface-name': re.compile(r'[A-Za-z]([A-Za-z])*([.][A-Za-z0-9]([-]*[A-Za-z0-9])*)+|xn--([0-9a-z])*([.][A-Za-z0-9]([-]*[A-Za-z0-9])*)+'), 'member-name': re.compile(r'\b[A-Z][A-Za-z0-9]*\b', ASCII), 'identifier': re.compile(r'\b[A-Za-z]([_]?[A-Za-z0-9])*\b', ASCII), } self.string = string self.pos = 0 self.current_doc = "" def get(self, expected): m = self.whitespace.match(self.string, self.pos) if m: doc = self.docstring.findall(self.string[m.start():m.end()]) if len(doc): try: self.current_doc += "\n".join(doc) except UnicodeError: self.current_doc += "\n".join( [el.decode("utf-8") for el in doc]) self.pos = m.end() pattern = self.patterns.get(expected) if pattern: m = pattern.match(self.string, self.pos) if m: self.pos = m.end() return m.group(0) else: m = self.keyword_pattern.match(self.string, self.pos) if m and m.group(0) == expected: self.pos = m.end() return True def expect(self, expected): value = self.get(expected) if not value: raise SyntaxError("expected '{}'".format(expected)) return value def end(self): m = self.whitespace.match(self.string, self.pos) if m: doc = self.docstring.findall(self.string[m.start():m.end()]) if len(doc): try: self.current_doc += "\n".join(doc) except UnicodeError: self.current_doc += "\n".join( [el.decode("utf-8") for el in doc]) self.pos = m.end() return self.pos >= len(self.string) def read_type(self, lastmaybe=False): if self.get('?'): if lastmaybe: raise SyntaxError("double '??'") return _Maybe(self.read_type(lastmaybe=True)) if self.get('[string]()'): return set() if self.get('[string]'): return _Dict(self.read_type()) if self.get('[]'): return _Array(self.read_type()) if self.get('object'): return _Object() if self.get('bool'): t = bool() elif self.get('int'): t = int() elif self.get('float'): t = float() elif self.get('string'): t = str() else: name = self.get('member-name') if name: t = _CustomType(name) else: t = self.read_struct() return t def read_struct(self): _isenum = None self.expect('(') fields = OrderedDict() if not self.get(')'): while True: name = self.expect('identifier') if _isenum == None: if self.get(':'): _isenum = False fields[name] = self.read_type() if not self.get(','): break continue elif self.get(','): _isenum = True fields[name] = True continue else: raise SyntaxError("after '{}'".format(name)) elif not _isenum: try: self.expect(':') fields[name] = self.read_type() except SyntaxError as e: raise SyntaxError("after '{}': {}".format(name, e)) else: fields[name] = True if not self.get(','): break self.expect(')') if _isenum: return _Enum(fields.keys()) else: return _Struct(fields) def read_member(self): if self.get('type'): try: _name = self.expect('member-name') except SyntaxError: m = self.whitespace.match(self.string, self.pos) if m: start = m.end() else: start = self.pos m = self.whitespace.search(self.string, start) if m: stop = m.start() else: stop = start raise SyntaxError("'{}' not a valid type name.".format(self.string[start:stop])) try: _type = self.read_type() except SyntaxError as e: raise SyntaxError("in '{}': {}".format(_name, e)) doc = self.current_doc self.current_doc = "" return _Alias(_name, _type, doc) elif self.get('method'): name = self.expect('member-name') # FIXME sig = self.method_signature.match(self.string, self.pos) if sig: sig = name + sig.group(0) in_type = self.read_struct() self.expect('->') out_type = self.read_struct() doc = self.current_doc self.current_doc = "" return _Method(name, in_type, out_type, sig, doc) elif self.get('error'): doc = self.current_doc self.current_doc = "" return _Error(self.expect('member-name'), self.read_type(), doc) else: raise SyntaxError('expected type, method, or error') class _Object(object): pass class _Struct(object): def __init__(self, fields): self.fields = OrderedDict(fields) class _Enum(object): def __init__(self, fields): self.fields = fields class _Array(object): def __init__(self, element_type): self.element_type = element_type class _Maybe(object): def __init__(self, element_type): self.element_type = element_type class _Dict(object): def __init__(self, element_type): self.element_type = element_type class _CustomType(object): def __init__(self, name): self.name = name class _Alias(object): def __init__(self, name, varlink_type, doc=None): self.name = name self.type = varlink_type self.doc = doc class _Method(object): def __init__(self, name, in_type, out_type, _signature, doc=None): self.name = name self.in_type = in_type self.out_type = out_type self.signature = _signature self.doc = doc class _Error(object): def __init__(self, name, varlink_type, doc=None): self.name = name self.type = varlink_type self.doc = doc class Interface(object): """Class for a parsed varlink interface definition.""" def __init__(self, description): """description -- description string in varlink interface definition language""" self.description = description scanner = Scanner(description) scanner.expect('interface') self.name = scanner.expect('interface-name') self.doc = scanner.current_doc scanner.current_doc = "" self.members = OrderedDict() while not scanner.end(): member = scanner.read_member() self.members[member.name] = member def get_description(self): """return the description string in varlink interface definition language""" return self.description def get_method(self, name): method = self.members.get(name) if method and isinstance(method, _Method): return method raise MethodNotFound(name) def filter_params(self, parent_name, varlink_type, _namespaced, args, kwargs): # print("filter_params", type(varlink_type), repr(varlink_type), args, kwargs, type(args)) if isinstance(varlink_type, _Maybe): if args == None: return None return self.filter_params(parent_name, varlink_type.element_type, _namespaced, args, kwargs) if isinstance(varlink_type, _Dict): if args == None: return {} if isinstance(args, Mapping): for (k, v) in args.items(): args[k] = self.filter_params(parent_name + '[' + k + ']', varlink_type.element_type, _namespaced, v, None) return args else: InvalidParameter(parent_name) if isinstance(varlink_type, _CustomType): # print("CustomType", varlink_type.name) return self.filter_params(parent_name, self.members.get(varlink_type.name), _namespaced, args, kwargs) if isinstance(varlink_type, _Alias): # print("Alias", varlink_type.name) return self.filter_params(parent_name, varlink_type.type, _namespaced, args, kwargs) if isinstance(varlink_type, _Object): return args if isinstance(varlink_type, _Enum) and ( isinstance(args, str) or isinstance(args, unicode) ): # print("Returned str:", args) return args if isinstance(varlink_type, _Array): if args == None: return [] return [self.filter_params(parent_name + '[]', varlink_type.element_type, _namespaced, x, None) for x in args] if isinstance(varlink_type, Set): # print("Returned set:", set(args)) return set(args) if isinstance(varlink_type, basestring) and isinstance(args, basestring): return args if isinstance(varlink_type, float) and (isinstance(args, float) or isinstance(args, int)): # print("Returned float:", args) return float(args) if isinstance(varlink_type, bool) and isinstance(args, bool): # print("Returned bool:", args) return args if isinstance(varlink_type, int) and (isinstance(args, float) or isinstance(args, int)): # print("Returned int:", args) if isinstance(args, float): return int(args + 0.5) return int(args) if not isinstance(varlink_type, _Struct): raise InvalidParameter(parent_name) # SyntaxError("Expected type %s, got %s with value '%s'" % (type(varlink_type), type(args), # args)) if _namespaced: out = SimpleNamespace() else: out = {} varlink_struct = None if not isinstance(args, tuple): varlink_struct = args args = None for name in varlink_type.fields: if isinstance(args, tuple): if args: val = args[0] if len(args) > 1: args = args[1:] else: args = None ret = self.filter_params(parent_name + "." + name, varlink_type.fields[name], _namespaced, val, None) if ret != None: # print("SetOUT:", name) if _namespaced: setattr(out, name, ret) else: out[name] = ret continue else: if name in kwargs: ret = self.filter_params(parent_name + "." + name, varlink_type.fields[name], _namespaced, kwargs[name], None) if ret != None: # print("SetOUT:", name) if _namespaced: setattr(out, name, ret) else: out[name] = ret continue if varlink_struct: if isinstance(varlink_struct, Mapping): if name not in varlink_struct: continue val = varlink_struct[name] ret = self.filter_params(parent_name + "." + name, varlink_type.fields[name], _namespaced, val, None) if ret != None: # print("SetOUT:", name) if _namespaced: setattr(out, name, ret) else: out[name] = ret elif hasattr(varlink_struct, name): val = getattr(varlink_struct, name) ret = self.filter_params(parent_name + "." + name, varlink_type.fields[name], _namespaced, val, None) if ret != None: # print("SetOUT:", name) if _namespaced: setattr(out, name, ret) else: out[name] = ret else: continue return out python-31.0.0/varlink/server.py000066400000000000000000000422311415340367700164560ustar00rootroot00000000000000# coding=utf-8 from __future__ import print_function from __future__ import unicode_literals import inspect import json import os import socket import stat import string import sys try: from builtins import int from builtins import object from builtins import open from builtins import range except ImportError: pass from .error import (InterfaceNotFound, InvalidParameter, MethodNotImplemented, VarlinkEncoder, VarlinkError, ConnectionError) from .scanner import Interface try: from socketserver import (StreamRequestHandler, BaseServer, ThreadingMixIn) except ImportError: # Python2 from SocketServer import (StreamRequestHandler, BaseServer, ThreadingMixIn) if hasattr(os, "fork"): try: from socketserver import ForkingMixIn except ImportError: # Python2 from SocketServer import ForkingMixIn from types import GeneratorType class Service(object): """Varlink service server handler To use the Service, a global object is instantiated: >>> service = Service( >>> vendor='Red Hat', >>> product='Manage System Accounts', >>> version='1', >>> interface_dir=os.path.dirname(__file__) >>> ) For the class implementing the methods of a specific varlink interface a decorator is used: >>> @service.interface('com.redhat.system.accounts') >>> class Accounts: >>> pass The varlink file corresponding to this interface is loaded from the 'interface_dir' specified in the constructor of the Service. It has to end in '.varlink'. Use a :class:`RequestHandler` with your Service object and run a :class:`Server` with it. If you want to use your own server with the Service object, split the incoming stream for every null byte and feed it to the :meth:`Service.handle` method. Write any message returned from this generator function to the output stream. >>> for outgoing_message in service.handle(incoming_message): >>> connection.write(outgoing_message) Note: varlink only handles one method call at a time on one connection. """ def __init__(self, vendor='', product='', version='', url='', interface_dir='.', namespaced=False): """Initialize the service with the data org.varlink.service.GetInfo() returns :param interface_dir: the directory with the \*.varlink files for the interfaces """ self.vendor = vendor self.product = product self.version = version self.url = url self.interface_dir = interface_dir self._namespaced = namespaced self.interfaces = {} self.interfaces_handlers = {} directory = os.path.dirname(__file__) self._add_interface(os.path.abspath(os.path.join(directory, 'org.varlink.service.varlink')), self) def GetInfo(self): """The standardized org.varlink.service.GetInfo() varlink method.""" return { 'vendor': self.vendor, 'product': self.product, 'version': self.version, 'url': self.url, 'interfaces': list(self.interfaces.keys()) } def GetInterfaceDescription(self, interface): """The standardized org.varlink.service.GetInterfaceDescription() varlink method.""" try: i = self.interfaces[interface] except KeyError: raise InterfaceNotFound(interface) return {'description': i.description} def _handle(self, message, raw_message, _server=None, _request=None): try: interface_name, _, method_name = message.get('method', '').rpartition('.') if not interface_name or not method_name: raise InterfaceNotFound(interface_name) interface = self.interfaces.get(interface_name) if not interface: raise InterfaceNotFound(interface_name) method = interface.get_method(method_name) parameters = message.get('parameters', {}) if parameters == None: parameters = {} handler = self.interfaces_handlers[interface.name] for name in parameters: if name not in method.in_type.fields: raise InvalidParameter(name) for name in method.in_type.fields: if name not in parameters: parameters[name] = None parameters = interface.filter_params("server.call", method.in_type, self._namespaced, parameters, None) func = getattr(handler, method_name, None) if not func or not callable(func): raise MethodNotImplemented(method_name) kwargs = {} if hasattr(inspect, "signature"): sig = inspect.signature(func) arg_names = [(sig.parameters[k].kind in ( inspect.Parameter.POSITIONAL_OR_KEYWORD, inspect.Parameter.KEYWORD_ONLY) and k or None) for k in sig.parameters.keys()] else: from itertools import izip spec = inspect.getargspec(func) matched_args = [reversed(x) for x in [spec.args, spec.defaults or []]] arg_names = dict(izip(*matched_args)) if message.get('more', False) or message.get('oneway', False) or message.get('upgrade', False): if message.get('more', False) and '_more' in arg_names: kwargs["_more"] = True if message.get('oneway', False) and '_oneway' in arg_names: kwargs["_oneway"] = True if message.get('upgrade', False) and '_upgrade' in arg_names: kwargs["_upgrade"] = True if '_raw' in arg_names: kwargs["_raw"] = raw_message if '_message' in arg_names: kwargs["_message"] = message if '_interface' in arg_names: kwargs["_interface"] = interface if '_method' in arg_names: kwargs["_method"] = method if '_server' in arg_names: kwargs["_server"] = _server if '_request' in arg_names: kwargs["_request"] = _request if self._namespaced: # FIXME: check for Maybe before taking None as default value out = func(*(getattr(parameters, k, default=None) for k in method.in_type.fields.keys()), **kwargs) else: # FIXME: check for Maybe before taking None as default value out = func(*(parameters.get(k) for k in method.in_type.fields.keys()), **kwargs) if isinstance(out, GeneratorType): try: for o in out: if isinstance(o, Exception): raise o if kwargs.get("_oneway", False): continue cont = True if '_continues' in o: cont = o['_continues'] del o['_continues'] yield {'continues': bool(cont), 'parameters': interface.filter_params("server.reply", method.out_type, self._namespaced, o, None) or {}} else: yield {'parameters': interface.filter_params("server.reply", method.out_type, self._namespaced, o, None) or {}} if not cont: return except ConnectionError as e: try: out.throw(e) except StopIteration: pass else: if message.get('oneway', False): yield None else: yield {'parameters': out or {}} except VarlinkError as error: yield error def handle(self, message, _server=None, _request=None): """This generator function handles any incoming message. Write any returned bytes to the output stream. >>> for outgoing_message in service.handle(incoming_message): >>> connection.write(outgoing_message) """ if not message: return if message[-1] == 0: message = message[:-1] string = message.decode('utf-8') handle = self._handle(json.loads(string), message, _server, _request) for out in handle: if out == None: return try: yield json.dumps(out, cls=VarlinkEncoder).encode('utf-8') except ConnectionError as e: try: handle.throw(e) except StopIteration: pass def _add_interface(self, filename, handler): if not os.path.isabs(filename): filename = os.path.join(self.interface_dir, filename + '.varlink') with open(filename) as f: interface = Interface(f.read()) self.interfaces[interface.name] = interface self.interfaces_handlers[interface.name] = handler def _set_interface(self, filename, interface_class): if 'class' in str(type(interface_class)): ic = interface_class else: ic = interface_class() self._add_interface(filename, ic) return interface_class def interface(self, filename): def decorator(interface_class): self._add_interface(filename, interface_class()) return interface_class return decorator def get_listen_fd(): if "LISTEN_FDS" not in os.environ: return None if "LISTEN_PID" not in os.environ: return None try: if int(os.environ["LISTEN_PID"]) != os.getpid(): return None except: return None try: fds = int(os.environ["LISTEN_FDS"]) except: return None if fds < 1: return None if fds == 1: try: if stat.S_ISSOCK(os.fstat(3).st_mode): return 3 else: return None except OSError: return None fields = string.split(os.environ["LISTEN_FDNAMES"], ":") if len(fields) != fds: return None for i in range(len(fields)): if fields[i] == "varlink": try: if stat.S_ISSOCK(os.fstat(i + 3).st_mode): return i + 3 else: return None except OSError: return None class RequestHandler(StreamRequestHandler): """Varlink request handler To use as an argument for the VarlinkServer constructor. Instantiate your own class and set the class variable service to your global :class:`Service` object. """ service = None def handle(self): message = b'' self.request.setblocking(True) while not self.rfile.closed: try: c = self.rfile.read(1) except BrokenPipeError: break if c == b'': break if c != b'\0': message += c continue for reply in self.service.handle(message, _server=self.server, _request=self.request): if reply != None: self.wfile.write(reply + b'\0') message = b'' class Server(BaseServer): """Server The same as the standard socketserver.TCPServer, to initialize with a subclass of :class:`RequestHandler`. >>> import varlink >>> import os >>> >>> service = varlink.Service(vendor='Example', product='Examples', version='1', url='http://example.com', >>> interface_dir=os.path.dirname(__file__)) >>> >>> class ServiceRequestHandler(varlink.RequestHandler): >>> service = service >>> >>> @service.interface('com.example.service') >>> class Example: >>> # com.example.service method implementation here … >>> pass >>> >>> server = varlink.ThreadingServer(sys.argv[1][10:], ServiceRequestHandler) >>> server.serve_forever() """ address_family = socket.AF_INET socket_type = socket.SOCK_STREAM request_queue_size = 5 allow_reuse_address = True def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True): self.remove_file = None self.mode = None self.listen_fd = get_listen_fd() if self.listen_fd: server_address = self.listen_fd self.address_family = socket.AF_UNIX self.socket = socket.fromfd(self.listen_fd, socket.AF_UNIX, socket.SOCK_STREAM) elif server_address.startswith("unix:"): self.address_family = socket.AF_UNIX address = server_address[5:] m = address.rfind(';mode=') if m != -1: self.mode = address[m + 6:] address = address[:m] if address[0] == '@': address = address.replace('@', '\0', 1) self.mode = None else: self.remove_file = address server_address = address self.socket = socket.socket(self.address_family, self.socket_type) self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) elif server_address.startswith("tcp:"): address = server_address[4:] p = address.rfind(':') if p != -1: port = int(address[p + 1:]) address = address[:p] else: raise ConnectionError("Invalid address 'tcp:%s'" % address) address = address.replace('[', '') address = address.replace(']', '') try: res = socket.getaddrinfo(address, port, proto=socket.IPPROTO_TCP, flags=socket.AI_NUMERICHOST) except TypeError: res = socket.getaddrinfo(address, port, self.address_family, self.socket_type, socket.IPPROTO_TCP, socket.AI_NUMERICHOST) af, socktype, proto, canonname, sa = res[0] self.address_family = af self.socket_type = socktype self.socket = socket.socket(self.address_family, self.socket_type) server_address = sa[0:2] else: raise ConnectionError("Invalid address '%s'" % server_address) BaseServer.__init__(self, server_address, RequestHandlerClass) if bind_and_activate: try: self.server_bind() self.server_activate() except: self.server_close() raise def server_bind(self): """Called by constructor to bind the socket. May be overridden. """ if self.allow_reuse_address: self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.socket.setblocking(True) if not self.listen_fd: self.socket.bind(self.server_address) self.server_address = self.socket.getsockname() if self.server_address[0] == 0: self.server_address = '@' + self.server_address[1:].decode('utf-8') if self.mode: os.fchmod(self.socket.fileno(), mode=int(self.mode, 8)) elif self.mode: os.chmod(self.server_address, mode=int(self.mode, 8)) def server_activate(self): """Called by constructor to activate the server. May be overridden. """ self.socket.listen(self.request_queue_size) def server_close(self): """Called to clean-up the server. May be overridden. """ if self.remove_file: try: os.remove(self.remove_file) except: pass self.socket.close() def fileno(self): """Return socket file number. Interface required by selector. """ return self.socket.fileno() def get_request(self): """Get the request and client address from the socket. May be overridden. """ return self.socket.accept() def shutdown_request(self, request): """Called to shutdown and close an individual request.""" try: # explicitly shutdown. socket.close() merely releases # the socket and waits for GC to perform the actual close. request.shutdown(socket.SHUT_RDWR) except: pass # some platforms may raise ENOTCONN here self.close_request(request) def close_request(self, request): """Called to clean up an individual request.""" request.close() def __enter__(self): return self def __exit__(self, *args): self.server_close() class ThreadingServer(ThreadingMixIn, Server): pass if hasattr(os, "fork"): class ForkingServer(ForkingMixIn, Server): pass python-31.0.0/varlink/tests/000077500000000000000000000000001415340367700157365ustar00rootroot00000000000000python-31.0.0/varlink/tests/__init__.py000066400000000000000000000011361415340367700200500ustar00rootroot00000000000000import os import sys import unittest here = os.path.dirname(__file__) loader = unittest.defaultTestLoader def suite(): suite = unittest.TestSuite() for fn in os.listdir(here): if fn.startswith("test_mocks") and sys.version_info.major < 3: continue if fn.startswith("test") and fn.endswith(".py"): modname = "varlink.tests." + fn[:-3] __import__(modname) module = sys.modules[modname] suite.addTest(loader.loadTestsFromModule(module)) return suite if __name__ == "__main__": unittest.main(defaultTest="suite") python-31.0.0/varlink/tests/org.example.more.varlink000066400000000000000000000007371415340367700225170ustar00rootroot00000000000000# Example Varlink service interface org.example.more # Enum, returning either start, progress or end # progress: [0-100] type State ( start: ?bool, progress: ?int, end: ?bool ) # Returns the same string method Ping(ping: string) -> (pong: string) # Dummy progress method # n: number of progress steps method TestMore(n: int) -> (state: State) # Stop serving method StopServing(reason: ?string) -> () # Something failed in TestMore error TestMoreError (reason: string) python-31.0.0/varlink/tests/org.varlink.certification.varlink000066400000000000000000000043161415340367700244100ustar00rootroot00000000000000# Interface to test varlink implementations against. # First you write a varlink client calling: # Start, Test01, Test02, …, Test09, End # The return value of the previous call should be the argument of the next call. # Then you test this client against well known servers like python or rust from # https://github.com/varlink/ # # Next you write a varlink server providing the same service as the well known ones. # Now run your client against it and run well known clients like python or rust # from https://github.com/varlink/ against your server. If all works out, then # your new language bindings should be varlink certified. interface org.varlink.certification type Interface ( foo: ?[]?[string](foo, bar, baz), anon: (foo: bool, bar: bool) ) type MyType ( object: object, enum: (one, two, three), struct: (first: int, second: string), array: []string, dictionary: [string]string, stringset: [string](), nullable: ?string, nullable_array_struct: ?[](first: int, second: string), interface: Interface ) method Start() -> (client_id: string) method Test01(client_id: string) -> (bool: bool) method Test02(client_id: string, bool: bool) -> (int: int) method Test03(client_id: string, int: int) -> (float: float) method Test04(client_id: string, float: float) -> (string: string) method Test05(client_id: string, string: string) -> ( bool: bool, int: int, float: float, string: string ) method Test06( client_id: string, bool: bool, int: int, float: float, string: string ) -> ( struct: ( bool: bool, int: int, float: float, string: string ) ) method Test07( client_id: string, struct: ( bool: bool, int: int, float: float, string: string ) ) -> (map: [string]string) method Test08(client_id: string, map: [string]string) -> (set: [string]()) method Test09(client_id: string, set: [string]()) -> (mytype: MyType) # returns more than one reply with "continues" method Test10(client_id: string, mytype: MyType) -> (string: string) # must be called as "oneway" method Test11(client_id: string, last_more_replies: []string) -> () method End(client_id: string) -> (all_ok: bool) error ClientIdError () error CertificationError (wants: object, got: object) python-31.0.0/varlink/tests/test_basic_network.py000077500000000000000000000042521415340367700222070ustar00rootroot00000000000000from __future__ import print_function from __future__ import unicode_literals import os import socket import threading import unittest from sys import platform try: from builting import str except ImportError: pass import varlink service = varlink.Service( vendor='Varlink', product='Varlink Examples', version='1', url='http://varlink.org', interface_dir=os.path.dirname(__file__) ) class ServiceRequestHandler(varlink.RequestHandler): service = service class TestService(unittest.TestCase): def do_run(self, address): server = varlink.ThreadingServer(address, ServiceRequestHandler) server_thread = threading.Thread(target=server.serve_forever) server_thread.daemon = True server_thread.start() try: with varlink.Client(address) as client, \ client.open('org.varlink.service') as _connection: info = _connection.GetInfo() self.assertEqual(len(info['interfaces']), 1) self.assertEqual(info['interfaces'][0], "org.varlink.service") self.assertEqual(info, service.GetInfo()) desc = _connection.GetInterfaceDescription(info['interfaces'][0]) self.assertEqual(desc, service.GetInterfaceDescription("org.varlink.service")) _connection.close() finally: server.shutdown() server.server_close() def test_tcp(self): self.do_run("tcp:127.0.0.1:23450") def test_anon_unix(self): if platform.startswith("linux"): self.do_run("unix:@org.varlink.service_anon_test" + str(os.getpid()) + threading.current_thread().name ) def test_unix(self): if hasattr(socket, "AF_UNIX"): self.do_run("unix:org.varlink.service_anon_test_" + str(os.getpid()) + threading.current_thread().name ) def test_wrong_url(self): self.assertRaises(varlink.ConnectionError, self.do_run, "uenix:org.varlink.service_wrong_url_test_%d" % os.getpid()) python-31.0.0/varlink/tests/test_certification.py000077500000000000000000000453311415340367700222030ustar00rootroot00000000000000#!/usr/bin/env python from __future__ import print_function from __future__ import unicode_literals import codecs import getopt import json import math import os import shlex import socket import sys import threading import time import unittest from sys import platform try: from builtins import object from builtins import range from builtins import str except ImportError: pass import varlink ######## CLIENT ############# def run_client(client): print('Connecting to %s\n' % client) with client.open('org.varlink.certification') as con: ret = con.Start() client_id = ret["client_id"] print("client_id:", client_id) ret = con.Test01(client_id) print("Test01:", ret) ret = con.Test02(client_id, ret["bool"]) print("Test02:", ret) ret = con.Test03(client_id, ret["int"]) print("Test03:", ret) ret = con.Test04(client_id, ret["float"]) print("Test04:", ret) ret = con.Test05(client_id, ret["string"]) print("Test05:", ret) ret = con.Test06(client_id, ret["bool"], ret["int"], ret["float"], ret["string"]) print("Test06:", ret) ret = con.Test07(client_id, ret["struct"]) print("Test07:", ret) ret = con.Test08(client_id, ret["map"]) print("Test08:", ret) ret = con.Test09(client_id, ret["set"]) print("Test09:", ret) ret_array = [] for ret in con.Test10(client_id, ret["mytype"], _more=True): print("Test10:", ret) ret_array.append(ret["string"]) ret = con.Test11(client_id, ret_array, _oneway=True) print("Test11:", ret) ret = con.End(client_id) print("End:", ret) print("Certification passed") ######## SERVER ############# service = varlink.Service( vendor='Varlink', product='Varlink Examples', version='1', url='http://varlink.org', interface_dir=os.path.dirname(__file__) ) class ServiceRequestHandler(varlink.RequestHandler): service = service def sorted_json(dct): if isinstance(dct, type([])): return sorted(dct) return dct class CertificationError(varlink.VarlinkError): def __init__(self, wants, got): varlink.VarlinkError.__init__(self, {'error': 'org.varlink.certification.CertificationError', 'parameters': {'wants': wants, 'got': got}}) @service.interface('org.varlink.certification') class CertService(object): next_method = {} def new_client_id(self, _server): client_id = codecs.getencoder('hex')(os.urandom(16))[0].decode("ascii") if not hasattr(_server, "next_method"): _server.next_method = {} if not hasattr(_server, "lifetimes"): _server.lifetimes = [] _server.next_method[client_id] = "Start" _server.lifetimes.append((time.time(), client_id)) return client_id def check_lifetimes(self, _server): if not hasattr(_server, "lifetimes"): return now = time.time() while True: if len(_server.lifetimes) == 0: return (t, client_id) = _server.lifetimes[0] if (now - t) < (60 * 60 * 12): return del _server.lifetimes[0] if hasattr(_server, "next_method") and client_id in _server.next_method: del _server.next_method[client_id] def assert_raw(self, client_id, _server, _raw, _message, wants): if wants != _message: del _server.next_method[client_id] raise CertificationError(wants, json.loads(_raw.decode('utf-8'))) def assert_cmp(self, client_id, _server, _raw, wants, _bool): if not _bool: del _server.next_method[client_id] raise CertificationError(wants, json.loads(_raw.decode('utf-8'))) def assert_method(self, client_id, _server, from_method, next_method): if not hasattr(_server, "next_method") or client_id not in _server.next_method: raise CertificationError({"method": "org.varlink.certification.Start+++"}, {"method": "org.varlink.certification." + from_method}) self.check_lifetimes(_server) if from_method != _server.next_method[client_id]: raise CertificationError("Call to method org.varlink.certification." + _server.next_method[client_id], "Call to method org.varlink.certification." + from_method) _server.next_method[client_id] = next_method def Start(self, _server=None, _raw=None, _message=None, _oneway=False): client_id = self.new_client_id(_server) if 'parameters' in _message and not _message['parameters']: del _message['parameters'] self.assert_method(client_id, _server, "Start", "Test01") self.assert_raw(client_id, _server, _raw, _message, { "method": "org.varlink.certification.Start", }) return {"client_id": client_id} # () -> (bool: bool) def Test01(self, client_id, _server=None, _raw=None, _message=None): self.assert_method(client_id, _server, "Test01", "Test02") self.assert_raw(client_id, _server, _raw, _message, { "method": "org.varlink.certification.Test01", "parameters": {"client_id": client_id} }) return {"bool": True} # (bool: bool) -> (int: int) def Test02(self, client_id, _bool, _server=None, _raw=None, _message=None): self.assert_method(client_id, _server, "Test02", "Test03") wants = { "method": "org.varlink.certification.Test02", "parameters": {"client_id": client_id, "bool": True} } self.assert_cmp(client_id, _server, _raw, wants, _bool == True) self.assert_raw(client_id, _server, _raw, _message, wants) return {"int": 1} # (int: int) -> (float: float) def Test03(self, client_id, _int, _server=None, _raw=None, _message=None): self.assert_method(client_id, _server, "Test03", "Test04") wants = { "method": "org.varlink.certification.Test03", "parameters": {"client_id": client_id, "int": 1} } self.assert_cmp(client_id, _server, _raw, wants, _int == 1) self.assert_raw(client_id, _server, _raw, _message, wants) return {"float": 1.0} # (float: float) -> (string: string) def Test04(self, client_id, _float, _server=None, _raw=None, _message=None): self.assert_method(client_id, _server, "Test04", "Test05") wants = { "method": "org.varlink.certification.Test04", "parameters": {"client_id": client_id, "float": 1.0} } self.assert_cmp(client_id, _server, _raw, wants, _float == 1.0) self.assert_raw(client_id, _server, _raw, _message, wants) return {"string": "ping"} # (string: string) -> (bool: bool, int: int, float: float, string: string) def Test05(self, client_id, _string, _server=None, _raw=None, _message=None): self.assert_method(client_id, _server, "Test05", "Test06") wants = { "method": "org.varlink.certification.Test05", "parameters": {"client_id": client_id, "string": "ping"} } self.assert_cmp(client_id, _server, _raw, wants, _string == "ping") self.assert_raw(client_id, _server, _raw, _message, wants) return {"bool": False, "int": 2, "float": math.pi, "string": "a lot of string"} # (bool: bool, int: int, float: float, string: string) # -> (struct: (bool: bool, int: int, float: float, string: string)) def Test06(self, client_id, _bool, _int, _float, _string, _server=None, _raw=None, _message=None): self.assert_method(client_id, _server, "Test06", "Test07") wants = { "method": "org.varlink.certification.Test06", "parameters": { "client_id": client_id, "bool": False, "int": 2, "float": math.pi, "string": "a lot of string" } } self.assert_raw(client_id, _server, _raw, _message, wants) self.assert_cmp(client_id, _server, _raw, wants, _int == 2) self.assert_cmp(client_id, _server, _raw, wants, _bool == False) self.assert_cmp(client_id, _server, _raw, wants, _float == math.pi) self.assert_cmp(client_id, _server, _raw, wants, _string == "a lot of string") return {"struct": {"bool": False, "int": 2, "float": math.pi, "string": "a lot of string"}} # (struct: (bool: bool, int: int, float: float, string: string)) -> (map: [string]string) def Test07(self, client_id, _dict, _server=None, _raw=None, _message=None): self.assert_method(client_id, _server, "Test07", "Test08") wants = { "method": "org.varlink.certification.Test07", "parameters": { "client_id": client_id, "struct": {"bool": False, "int": 2, "float": math.pi, "string": "a lot of string"} } } self.assert_raw(client_id, _server, _raw, _message, wants) self.assert_cmp(client_id, _server, _raw, wants, _dict["int"] == 2) self.assert_cmp(client_id, _server, _raw, wants, _dict["bool"] == False) self.assert_cmp(client_id, _server, _raw, wants, _dict["float"] == math.pi) self.assert_cmp(client_id, _server, _raw, wants, _dict["string"] == "a lot of string") return {"map": {"foo": "Foo", "bar": "Bar"}} # (map: [string]string) -> (set: [string]()) def Test08(self, client_id, _map, _server=None, _raw=None, _message=None): self.assert_method(client_id, _server, "Test08", "Test09") self.assert_raw(client_id, _server, _raw, _message, { "method": "org.varlink.certification.Test08", "parameters": {"client_id": client_id, "map": {"foo": "Foo", "bar": "Bar"}} }) return {"set": {"one", "two", "three"}} # (set: [string]()) -> (mytype: MyType) def Test09(self, client_id, _set, _server=None, _raw=None, _message=None): self.assert_method(client_id, _server, "Test09", "Test10") wants = { "method": "org.varlink.certification.Test09", "parameters": { "client_id": client_id, "set": {"one": {}, "three": {}, "two": {}} } } self.assert_raw(client_id, _server, _raw, _message, wants) self.assert_cmp(client_id, _server, _raw, wants, isinstance(_set, set)) self.assert_cmp(client_id, _server, _raw, wants, len(_set) == 3) self.assert_cmp(client_id, _server, _raw, wants, "one" in _set) self.assert_cmp(client_id, _server, _raw, wants, "two" in _set) self.assert_cmp(client_id, _server, _raw, wants, "three" in _set) return { "client_id": client_id, "mytype": { "object": {"method": "org.varlink.certification.Test09", "parameters": {"map": {"foo": "Foo", "bar": "Bar"}}}, "enum": "two", "struct": {"first": 1, "second": "2"}, "array": ["one", "two", "three"], "dictionary": {"foo": "Foo", "bar": "Bar"}, "stringset": {"one", "two", "three"}, "nullable": None, "nullable_array_struct": None, "interface": { "foo": [ None, {"foo": "foo", "bar": "bar"}, None, {"one": "foo", "two": "bar"} ], "anon": {"foo": True, "bar": False} } } } # method Test10(mytype: MyType) -> (string: string) def Test10(self, client_id, mytype, _server=None, _raw=None, _message=None): self.assert_method(client_id, _server, "Test10", "Test11") wants = { "method": "org.varlink.certification.Test10", "more": True, "parameters": { "client_id": client_id, "mytype": { "object": {"method": "org.varlink.certification.Test09", "parameters": {"map": {"foo": "Foo", "bar": "Bar"}}}, "enum": "two", "struct": {"first": 1, "second": "2"}, "array": ["one", "two", "three"], "dictionary": {"foo": "Foo", "bar": "Bar"}, "stringset": {"one", "two", "three"}, "interface": { "foo": [ None, {"foo": "foo", "bar": "bar"}, None, {"one": "foo", "two": "bar"} ], "anon": {"foo": True, "bar": False} } } } } if "nullable" in mytype: self.assert_cmp(client_id, _server, _raw, wants, mytype["nullable"] == None) del mytype["nullable"] if "nullable_array_struct" in mytype: self.assert_cmp(client_id, _server, _raw, wants, mytype["nullable_array_struct"] == None) del mytype["nullable_array_struct"] self.assert_cmp(client_id, _server, _raw, wants, mytype == wants["parameters"]["mytype"]) for i in range(1, 11): yield {"string": "Reply number %d" % i, '_continues': i != 10} # method Test11(last_more_replies: []string) -> () def Test11(self, client_id, last_more_replies, _server=None, _raw=None, _message=None, _oneway=False): self.assert_method(client_id, _server, "Test11", "End") wants = { "oneway": True, "method": "org.varlink.certification.Test11", "parameters": { "client_id": client_id, "last_more_replies": [ "Reply number 1", "Reply number 2", "Reply number 3", "Reply number 4", "Reply number 5", "Reply number 6", "Reply number 7", "Reply number 8", "Reply number 9", "Reply number 10" ] } } self.assert_cmp(client_id, _server, _raw, wants, _oneway) for i in range(0, 10): self.assert_cmp(client_id, _server, _raw, wants, last_more_replies[i] == "Reply number %d" % (i + 1)) # method End() -> () def End(self, client_id, _server=None, _raw=None, _message=None): self.assert_method(client_id, _server, "End", "Start") self.assert_raw(client_id, _server, _raw, _message, { "method": "org.varlink.certification.End", "parameters": {"client_id": client_id} }) del _server.next_method[client_id] return {"all_ok": True} def run_server(address): with varlink.ThreadingServer(address, ServiceRequestHandler) as server: print("Listening on", server.server_address) try: server.serve_forever() except KeyboardInterrupt: pass ######## MAIN ############# def usage(): print('Usage: %s [[--client] --varlink=]' % sys.argv[0], file=sys.stderr) print('\tSelf Exec: $ %s' % sys.argv[0], file=sys.stderr) print('\tServer : $ %s --varlink=' % sys.argv[0], file=sys.stderr) print('\tClient : $ %s --client --varlink=' % sys.argv[0], file=sys.stderr) print('\tClient : $ %s --client --bridge=' % sys.argv[0], file=sys.stderr) print('\tClient : $ %s --client --activate=' % sys.argv[0], file=sys.stderr) if __name__ == '__main__': try: opts, args = getopt.getopt(sys.argv[1:], "b:A:", ["help", "client", "varlink=", "bridge=", "activate="]) except getopt.GetoptError: usage() sys.exit(2) address = None client_mode = False activate = None bridge = None for opt, arg in opts: if opt == "--help": usage() sys.exit(0) elif opt == "--varlink": address = arg elif opt == "--bridge" or opt == "-b": bridge = arg elif opt == "--activate" or opt == "-A": activate = arg elif opt == "--client": client_mode = True client = None if client_mode: if bridge: client = varlink.Client.new_with_bridge(shlex.split(bridge)) if activate: client = varlink.Client.new_with_activate(shlex.split(activate)) if address: client = varlink.Client.new_with_address(address) if not address and not client_mode: if not hasattr(socket, "AF_UNIX"): print("varlink activate: not supported on platform %s" % platform, file=sys.stderr) usage() sys.exit(2) client_mode = True with varlink.Client.new_with_activate([__file__, "--varlink=$VARLINK_ADDRESS"]) as client: run_client(client) elif client_mode: with client: run_client(client) else: run_server(address) sys.exit(0) ######## UNITTEST ############# class TestService(unittest.TestCase): @classmethod def setUpClass(cls): if hasattr(socket, "AF_UNIX"): cls.address = "unix:org.varlink.certification_" \ + str(os.getpid()) \ + threading.current_thread().name else: cls.address = "tcp:127.0.0.1:23456" cls.server = varlink.ThreadingServer(cls.address, ServiceRequestHandler) server_thread = threading.Thread(target=cls.server.serve_forever) server_thread.daemon = True server_thread.start() def test_client(self): run_client(varlink.Client.new_with_address(self.address)) def test_01(self): with varlink.Client(self.address) as client, \ client.open('org.varlink.certification') as con: ret = con.Start() client_id = ret["client_id"] ret = con.Test01(client_id) print("Test01:", ret) ret = con.Test02(client_id, ret["bool"]) print("Test02:", ret) self.assertRaises(varlink.VarlinkError, con.Test03, "test") self.assertRaises(varlink.VarlinkError, con.Test03, client_id, 0) self.assertRaises(varlink.VarlinkError, con.Test03, client_id, ret["int"]) ret = con.Start() client_id = ret["client_id"] ret = con.Test01(client_id) print("Test01:", ret) ret = con.Test02(client_id, ret["bool"]) print("Test02:", ret) self.assertRaises(varlink.VarlinkError, con.Test01, client_id) @classmethod def tearDownClass(cls): cls.server.shutdown() cls.server.server_close() python-31.0.0/varlink/tests/test_mocks.py000066400000000000000000000041721415340367700204670ustar00rootroot00000000000000import time import unittest from varlink import mock import varlink types = """ type MyPersonalType ( foo: string, bar: string ) """ class Service(): def Test1(self, param1: int) -> str: """return test: MyPersonalType""" return { "test": { "foo": "bim", "bar": "boom" } } def Test2(self, param1: str="test") -> None: pass def Test3(self, param1: str) -> str: """return test""" return {"test": param1} class TestMockMechanisms(unittest.TestCase): @mock.mockedservice( fake_service=Service, fake_types=types, name='org.service.com', address='unix:@foo' ) def test_init(self): with varlink.Client("unix:@foo") as client: connection = client.open('org.service.com') self.assertEqual(connection.Test1(param1=1)["test"]["bar"], "boom") self.assertEqual(connection.Test3(param1="foo")["test"], "foo") class TestMockUtilities(unittest.TestCase): def test_cast_type(self): self.assertEqual(mock.cast_type(""), 'int') self.assertEqual(mock.cast_type(""), 'string') def test_get_ignored(self): expected_ignore = dir(mock.MockedService) ignored = mock.get_ignored() self.assertEqual(expected_ignore, ignored) def test_get_attributs(self): service = Service() attributs = mock.get_interface_attributs(service, mock.get_ignored()) expected_result = { "callables": ["Test1", "Test2", "Test3"], "others": [] } self.assertEqual(attributs, expected_result) def test_generate_callable_interface(self): service = Service() generated_itf = mock.generate_callable_interface(service, 'Test1') self.assertEqual( generated_itf, "method Test1(param1: int) -> (test: MyPersonalType)") generated_itf = mock.generate_callable_interface(service, 'Test3') self.assertEqual( generated_itf, "method Test3(param1: string) -> (test: string)") python-31.0.0/varlink/tests/test_orgexamplemore.py000077500000000000000000000204171415340367700224040ustar00rootroot00000000000000#!/usr/bin/env python """Server and Client example of varlink for python From the main git repository directory run:: $ PYTHONPATH=$(pwd) python3 ./varlink/tests/test_orgexamplemore.py or:: $ PYTHONPATH=$(pwd) python3 ./varlink/tests/test_orgexamplemore.py --varlink="unix:@test" & Listening on @test [1] 6434 $ PYTHONPATH=$(pwd) python3 ./varlink/tests/test_orgexamplemore.py --client --varlink="unix:@test" [...] """ from __future__ import print_function from __future__ import unicode_literals import argparse import os import shlex import socket import sys import textwrap import threading import time import unittest from sys import platform try: from builtins import int from builtins import next from builtins import object from builtins import range except ImportError: pass import varlink ######## CLIENT ############# def run_client(client): print('Connecting to %s\n' % client) try: with \ client.open('org.example.more', namespaced=True) as con1, \ client.open('org.example.more', namespaced=True) as con2: for m in con1.TestMore(10, _more=True): if hasattr(m.state, 'start') and m.state.start != None: if m.state.start: print("--- Start ---", file=sys.stderr) if hasattr(m.state, 'end') and m.state.end != None: if m.state.end: print("--- End ---", file=sys.stderr) if hasattr(m.state, 'progress') and m.state.progress != None: print("Progress:", m.state.progress, file=sys.stderr) if m.state.progress > 50: ret = con2.Ping("Test") print("Ping: ", ret.pong) except varlink.ConnectionError as e: print("ConnectionError:", e) raise e except varlink.VarlinkError as e: print(e) print(e.error()) print(e.parameters()) raise e ######## SERVER ############# service = varlink.Service( vendor='Varlink', product='Varlink Examples', version='1', url='http://varlink.org', interface_dir=os.path.dirname(__file__) ) class ServiceRequestHandler(varlink.RequestHandler): service = service class ActionFailed(varlink.VarlinkError): def __init__(self, reason): varlink.VarlinkError.__init__(self, {'error': 'org.example.more.ActionFailed', 'parameters': {'field': reason}}) @service.interface('org.example.more') class Example(object): sleep_duration = 1 def TestMore(self, n, _more=True, _server=None): try: if not _more: yield varlink.InvalidParameter('more') yield {'state': {'start': True}, '_continues': True} for i in range(0, n): yield {'state': {'progress': int(i * 100 / n)}, '_continues': True} time.sleep(self.sleep_duration) yield {'state': {'progress': 100}, '_continues': True} yield {'state': {'end': True}, '_continues': False} except Exception as error: print("ERROR", error, file=sys.stderr) if _server: _server.shutdown() def Ping(self, ping): return {'pong': ping} def StopServing(self, reason=None, _request=None, _server=None): print("Server ends.") if _request: print("Shutting down client connection") _server.shutdown_request(_request) if _server: print("Shutting down server") _server.shutdown() def TestMap(self, map): i = 1 ret = {} for (key, val) in map.items(): ret[key] = {"i": i, "val": val} i += 1 return {'map': ret} def TestObject(self, object): import json return {"object": json.loads(json.dumps(object))} def run_server(address): with varlink.ThreadingServer(address, ServiceRequestHandler) as server: print("Listening on", server.server_address) try: server.serve_forever() except KeyboardInterrupt: pass ######## MAIN ############# def epilog(): return textwrap.dedent(""" Examples: \tSelf Exec: $ {0} \tServer : $ {0} --varlink= \tClient : $ {0} --client --varlink= \tClient : $ {0} --client --bridge= \tClient : $ {0} --client --activate= """.format(sys.argv[0])) if __name__ == '__main__': parser = argparse.ArgumentParser( description='Varlink org.example.more test case', epilog=epilog(), formatter_class=argparse.RawTextHelpFormatter ) parser.add_argument( '--varlink', type=str, help='The varlink address' ) parser.add_argument( '-b', '--bridge', type=str, help='bridge command' ) parser.add_argument( '-A', '--activate', type=str, help='activation command' ) parser.add_argument( '--client', action='store_true', help='launch the client mode' ) args = parser.parse_args() address = args.varlink client_mode = args.client activate = args.activate bridge = args.bridge client = None if client_mode: if bridge: client = varlink.Client.new_with_bridge(shlex.split(bridge)) if activate: client = varlink.Client.new_with_activate(shlex.split(activate)) if address: client = varlink.Client.new_with_address(address) if not address and not client_mode: if not hasattr(socket, "AF_UNIX"): print("varlink activate: not supported on platform %s" % platform, file=sys.stderr) parser.print_help() sys.exit(2) client_mode = True with varlink.Client.new_with_activate( [__file__, "--varlink=$VARLINK_ADDRESS"]) as client: run_client(client) elif client_mode: with client: run_client(client) else: run_server(address) sys.exit(0) ######## UNITTEST ############# class TestService(unittest.TestCase): def test_service(self): address = "tcp:127.0.0.1:23451" Example.sleep_duration = 0.1 server = varlink.ThreadingServer(address, ServiceRequestHandler) server_thread = threading.Thread(target=server.serve_forever) server_thread.daemon = True server_thread.start() try: client = varlink.Client.new_with_address(address) run_client(client) with \ client.open('org.example.more', namespaced=True) as con1, \ client.open('org.example.more', namespaced=True) as con2: self.assertEqual(con1.Ping("Test").pong, "Test") it = con1.TestMore(10, _more=True) m = next(it) self.assertTrue(hasattr(m.state, 'start')) self.assertFalse(hasattr(m.state, 'end')) self.assertFalse(hasattr(m.state, 'progress')) self.assertIsNotNone(m.state.start) for i in range(0, 110, 10): m = next(it) self.assertTrue(hasattr(m.state, 'progress')) self.assertFalse(hasattr(m.state, 'start')) self.assertFalse(hasattr(m.state, 'end')) self.assertIsNotNone(m.state.progress) self.assertEqual(i, m.state.progress) if i > 50: ret = con2.Ping("Test") self.assertEqual("Test", ret.pong) m = next(it) self.assertTrue(hasattr(m.state, 'end')) self.assertFalse(hasattr(m.state, 'start')) self.assertFalse(hasattr(m.state, 'progress')) self.assertIsNotNone(m.state.end) self.assertRaises(StopIteration, next, it) con1.StopServing(_oneway=True) time.sleep(0.5) self.assertRaises(varlink.ConnectionError, con1.Ping, "Test") finally: server.shutdown() server.server_close() python-31.0.0/varlink/tests/test_scanner.py000077500000000000000000000120411415340367700210010ustar00rootroot00000000000000from __future__ import print_function from __future__ import unicode_literals import unittest import varlink class TestScanner(unittest.TestCase): def test_scanner_1(self): interface = varlink.Interface("""# Example Varlink service interface org.example.more # Enum, returning either start, progress or end # progress: [0-100] type State ( start: ?bool, progress: ?int, end: ?bool ) method TestMap(map: [string]string) -> (map: [string](i: int, val: string)) # Returns the same string method Ping(ping: string) -> (pong: string) # Dummy progress method # n: number of progress steps method TestMore(n: int) -> (state: State) # Stop serving method StopServing() -> () type ErrorChain ( description: string, caused_by: ?ErrorChain ) error ActionFailed (reason: ?ErrorChain) """) self.assertEqual(interface.name, "org.example.more") self.assertIsNotNone(interface.get_method("Ping")) self.assertIsNotNone(interface.get_method("TestMore")) self.assertIsNotNone(interface.get_method("TestMap")) self.assertIsNotNone(interface.get_method("StopServing")) self.assertIsInstance(interface.members.get("ActionFailed"), varlink.scanner._Error) self.assertIsInstance(interface.members.get("State"), varlink.scanner._Alias) def test_doubleoption(self): interface = None try: interface = varlink.Interface(""" interface org.example.doubleoption method Foo(a: ??string) -> () """) except SyntaxError: pass self.assertIsNone(interface) def test_complex(self): interface = varlink.Interface(""" interface org.example.complex type TypeEnum ( a, b, c ) type TypeFoo ( bool: bool, int: int, float: float, string: ?string, enum: ?[]( foo, bar, baz ), type: ?TypeEnum, anon: ( foo: bool, bar: int, baz: [](a: int, b: int) ), object: object ) method Foo(a: (b: bool, c: int), foo: TypeFoo) -> (a: [](b: bool, c: int), foo: TypeFoo) error ErrorFoo (a: (b: bool, c: int), foo: TypeFoo) """) self.assertEqual(interface.name, "org.example.complex") self.assertIsNotNone(interface.get_method("Foo")) self.assertIsInstance(interface.members.get("ErrorFoo"), varlink.scanner._Error) self.assertIsInstance(interface.members.get("TypeEnum"), varlink.scanner._Alias) def test_interfacename(self): self.assertRaises(SyntaxError, varlink.Interface, "interface .a.b.c\nmethod F()->()") self.assertRaises(SyntaxError, varlink.Interface, "interface com.-example.leadinghyphen\nmethod F()->()") self.assertRaises(SyntaxError, varlink.Interface, "interface com.example-.danglinghyphen-\nmethod F()->()") self.assertRaises(SyntaxError, varlink.Interface, "interface co9.example.number-toplevel\nmethod F()->()") self.assertRaises(SyntaxError, varlink.Interface, "interface 1om.example.number-toplevel\nmethod F()->()") self.assertRaises(SyntaxError, varlink.Interface, "interface ab\nmethod F()->()") self.assertRaises(SyntaxError, varlink.Interface, "interface .a.b.c\nmethod F()->()") self.assertRaises(SyntaxError, varlink.Interface, "interface a.b.c.\nmethod F()->()") self.assertRaises(SyntaxError, varlink.Interface, "interface a..b.c\nmethod F()->()") self.assertRaises(SyntaxError, varlink.Interface, "interface 1.b.c\nmethod F()->()") self.assertRaises(SyntaxError, varlink.Interface, "interface 8a.0.0\nmethod F()->()") self.assertRaises(SyntaxError, varlink.Interface, "interface -a.b.c\nmethod F()->()") self.assertRaises(SyntaxError, varlink.Interface, "interface a.b.c-\nmethod F()->()") self.assertRaises(SyntaxError, varlink.Interface, "interface a.b-.c-\nmethod F()->()") self.assertRaises(SyntaxError, varlink.Interface, "interface a.-b.c-\nmethod F()->()") self.assertRaises(SyntaxError, varlink.Interface, "interface a.-.c\nmethod F()->()") self.assertRaises(SyntaxError, varlink.Interface, "interface a.*.c\nmethod F()->()") self.assertRaises(SyntaxError, varlink.Interface, "interface a.?\nmethod F()->()") self.assertIsNotNone(varlink.Interface("interface a.b\nmethod F()->()").name) self.assertIsNotNone(varlink.Interface("interface a.b.c\nmethod F()->()").name) self.assertIsNotNone(varlink.Interface("interface a.1\nmethod F()->()").name) self.assertIsNotNone(varlink.Interface("interface a.0.0\nmethod F()->()").name) self.assertIsNotNone(varlink.Interface("interface org.varlink.service\nmethod F()->()").name) self.assertIsNotNone(varlink.Interface("interface com.example.0example\nmethod F()->()").name) self.assertIsNotNone(varlink.Interface("interface com.example.example-dash\nmethod F()->()").name) self.assertIsNotNone(varlink.Interface("interface xn--lgbbat1ad8j.example.algeria\nmethod F()->()").name) self.assertIsNotNone(varlink.Interface("interface xn--c1yn36f.xn--c1yn36f.xn--c1yn36f\nmethod F()->()").name)