├── .bumpversion.cfg ├── .github └── workflows │ ├── bump_version.yml │ ├── deploy.yml │ └── test.yml ├── .gitignore ├── AUTHORS ├── LICENSE ├── MAINTENANCE.md ├── MANIFEST.in ├── README.md ├── Vagrantfile ├── docs ├── Makefile ├── conf.py └── index.rst ├── examples ├── chat.py └── high_volume.py ├── pyre ├── __init__.py ├── pyre.py ├── pyre_event.py ├── pyre_group.py ├── pyre_node.py ├── pyre_peer.py ├── zactor.py ├── zbeacon.py ├── zhelper.py ├── zre_msg.py └── zsocket.py ├── setup.py └── tests ├── test_network_disconnect.py ├── test_pyre.py ├── test_zactor.py ├── test_zbeacon.py └── test_zhelper.py /.bumpversion.cfg: -------------------------------------------------------------------------------- 1 | [bumpversion] 2 | current_version = 0.3.5 3 | commit = True 4 | tag = True 5 | 6 | [bumpversion:file:setup.py] 7 | 8 | [bumpversion:file:pyre/__init__.py] 9 | -------------------------------------------------------------------------------- /.github/workflows/bump_version.yml: -------------------------------------------------------------------------------- 1 | name: Bump version 2 | on: 3 | pull_request: 4 | branches: [master] 5 | types: [closed] 6 | workflow_dispatch: 7 | inputs: 8 | version_part: 9 | description: > 10 | Version part to bump before deployment. 11 | Possible options {none, major, minor, patch} 12 | required: true 13 | default: 'patch' 14 | 15 | jobs: 16 | get_version_part_manually: 17 | name: Bump version on manual workflow dispatch 18 | if: github.event.inputs.version_part 19 | runs-on: ubuntu-latest 20 | env: 21 | VERSION_PART: ${{ github.event.inputs.version_part }} 22 | outputs: 23 | # will be empty if validation fails 24 | version_part: ${{ steps.validated_input.outputs.version_part }} 25 | steps: 26 | - name: Cancel on invalid input 27 | if: > 28 | !( 29 | env.VERSION_PART == 'none' || 30 | env.VERSION_PART == 'major' || 31 | env.VERSION_PART == 'minor' || 32 | env.VERSION_PART == 'patch' 33 | ) 34 | run: | 35 | echo "::error:: \`$VERSION_PART\` is not a valid version part. Must be one of {none, major, minor, patch}" 36 | exit 1 37 | - name: Set version part based on manual input 38 | id: validated_input 39 | run: echo "::set-output name=version_part::$VERSION_PART" 40 | 41 | get_version_part_on_pr_merge: 42 | name: Bump version on pull reuqest merge 43 | if: github.event.pull_request.merged == true 44 | runs-on: ubuntu-latest 45 | outputs: 46 | version_part: ${{ join(steps.*.outputs.version_part, '') }} 47 | steps: 48 | - name: Cancel on bump:none 49 | id: bump_none 50 | if: contains(github.event.pull_request.labels.*.name, 'bump:none') 51 | run: echo "::set-output name=version_part::none" 52 | - name: Bump major 53 | id: bump_major 54 | if: > 55 | steps.bump_none.conclusion == 'skipped' && 56 | contains(github.event.pull_request.labels.*.name, 'bump:major') 57 | run: echo "::set-output name=version_part::major" 58 | - name: Bump minor 59 | id: bump_minor 60 | if: > 61 | steps.bump_none.conclusion == 'skipped' && 62 | steps.bump_major.conclusion == 'skipped' && 63 | contains(github.event.pull_request.labels.*.name, 'bump:minor') 64 | run: echo "::set-output name=version_part::minor" 65 | - name: Bump patch 66 | id: bump_patch 67 | if: > 68 | steps.bump_none.conclusion == 'skipped' && 69 | steps.bump_major.conclusion == 'skipped' && 70 | steps.bump_minor.conclusion == 'skipped' 71 | run: echo "::set-output name=version_part::patch" 72 | 73 | bump_version: 74 | name: Bump version 75 | needs: [get_version_part_on_pr_merge, get_version_part_manually] 76 | # always() needed to not automatically skip this job due to one of the 77 | # get_version_part_* jobs being skipped and bump_version depending on both. 78 | if: > 79 | always() && 80 | ( 81 | needs.get_version_part_on_pr_merge.result == 'success' || 82 | needs.get_version_part_manually.result == 'success' 83 | ) && 84 | join(needs.*.outputs.version_part, '') != 'none' 85 | env: 86 | VERSION_PART: ${{ join(needs.*.outputs.version_part, '') }} 87 | runs-on: ubuntu-latest 88 | steps: 89 | - uses: actions/checkout@v4 90 | - uses: actions/setup-python@v5 91 | with: 92 | python-version: '3.12' 93 | - name: Install bump2version 94 | run: pip install bump2version 95 | - uses: oleksiyrudenko/gha-git-credentials@v2-latest 96 | with: 97 | token: ${{ secrets.GITHUB_TOKEN }} 98 | - name: Bump version 99 | run: bump2version --verbose "$VERSION_PART" 100 | - name: Push changes 101 | uses: ad-m/github-push-action@master 102 | with: 103 | tags: true 104 | branch: ${{ github.ref }} 105 | github_token: ${{ secrets.GITHUB_TOKEN }} 106 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy to PyPI 2 | on: 3 | push: 4 | tags: 5 | - "**" 6 | workflow_dispatch: 7 | 8 | jobs: 9 | build_sdist: 10 | name: Build source distribution 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | - uses: actions/setup-python@v5 15 | with: 16 | python-version: '3.12' 17 | - name: Build source package 18 | run: | 19 | pip install build 20 | python -m build --sdist . 21 | - name: Upload source package 22 | uses: actions/upload-artifact@v4 23 | with: 24 | name: distribution 25 | path: dist/ 26 | 27 | publish: 28 | name: Deploy to PyPI 29 | runs-on: ubuntu-latest 30 | needs: [build_sdist] 31 | steps: 32 | - uses: actions/checkout@v4 33 | - name: Download source package 34 | uses: actions/download-artifact@v4 35 | with: 36 | name: distribution 37 | path: dist/ 38 | - name: Deploy to PyPI 39 | uses: pypa/gh-action-pypi-publish@master 40 | with: 41 | user: __token__ 42 | password: ${{ secrets.PYPI_TOKEN }} 43 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test on push or pull request 2 | on: 3 | push: 4 | branches: [master] 5 | pull_request: 6 | workflow_dispatch: 7 | 8 | jobs: 9 | build_sdist: 10 | name: Build source distribution 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | - uses: actions/setup-python@v5 15 | with: 16 | python-version: '3.12' 17 | - name: Build source package 18 | run: | 19 | pip install build 20 | python -m build --sdist . 21 | - name: Upload source package 22 | uses: actions/upload-artifact@v4 23 | with: 24 | name: distribution 25 | path: dist/ 26 | 27 | run_tests: 28 | name: "Run tests" 29 | needs: [build_sdist] 30 | runs-on: ${{ matrix.os }} 31 | strategy: 32 | fail-fast: false 33 | max-parallel: 1 # tests interfere with each other if run in parallel 34 | matrix: 35 | # order matters - this setup minimizes the interference between jobs 36 | python-version: [3.8, 3.9] 37 | os: [ubuntu-latest, windows-latest, macOS-latest] 38 | steps: 39 | - uses: actions/checkout@v4 40 | - name: Set up Python ${{ matrix.python-version }} 41 | uses: actions/setup-python@v5 42 | with: 43 | python-version: ${{ matrix.python-version }} 44 | - uses: actions/download-artifact@v4 45 | with: 46 | name: distribution 47 | path: dist/ 48 | - name: Install dependencies 49 | shell: bash # enables file pattern matching on Windows 50 | run: | 51 | python -m pip install --upgrade pip 52 | pip install nose 53 | pip install dist/zeromq_pyre-*.tar.gz 54 | - name: Test with nose 55 | working-directory: tests 56 | run: python -m nose -v --exe 57 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # vagrant 2 | .vagrant 3 | 4 | *.py[cod] 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Packages 10 | *.egg 11 | *.egg-info 12 | *.whl 13 | dist 14 | build 15 | eggs 16 | parts 17 | bin 18 | var 19 | sdist 20 | develop-eggs 21 | .installed.cfg 22 | lib 23 | lib64 24 | __pycache__ 25 | 26 | # Installer logs 27 | pip-log.txt 28 | 29 | # Unit test / coverage reports 30 | .coverage 31 | .tox 32 | nosetests.xml 33 | 34 | # Translations 35 | *.mo 36 | 37 | # Mr Developer 38 | .mr.developer.cfg 39 | .project 40 | .pydevproject 41 | 42 | # Editor swap files 43 | *.swp 44 | 45 | # sphinx dirs 46 | _build 47 | _static 48 | _templates 49 | 50 | # IDEs 51 | .idea/ 52 | .vscode/ -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Maintainer 2 | ========== 3 | 4 | Arnaud Loonstra 5 | 6 | Contributors 7 | ============ 8 | Aldo Hoeben 9 | Rio Kierkels 10 | Pieter Hintjens 11 | HKU MAPLAB 12 | Steven Silvester 13 | Pablo Prietz 14 | 15 | Corporate Contributors 16 | ====================== 17 | Copyright (c) 2013 HKU MAPLAB 18 | Copyright (c) 2013 Stichting z25.org 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /MAINTENANCE.md: -------------------------------------------------------------------------------- 1 | # Maintenace 2 | 3 | Maintenance is based on extra tools that are defined as [optional dependencies](https://setuptools.readthedocs.io/en/latest/userguide/dependency_management.html#optional-dependencies) in [`setup.py`](setup.py). 4 | The sections below refer to them as "extra requirements". 5 | 6 | To automate maintenance as much as possible, this repository features three Github Actions: 7 | 1. [Test on push or pull request](.github/workflows/test.yml) - see [Testing](#testing) 8 | 1. [Bump version](.github/workflows/bump_version.yml) - see [Deployment](#deployment) 9 | 1. [Deploy to PyPI](.github/workflows/deploy.yml) - see [Deployment](#deployment) 10 | 11 | ## Testing 12 | 13 | You can find a series of unit tests in the [`tests`](tests) directory. The recommended 14 | way to execute them is by using the [`nose`](https://nose.readthedocs.io/en/latest/) 15 | module. It is installed as part of the `test` extra requirements. 16 | 17 | To run the tests locally, execute `python -m nose -v --exe` within the project's root directory. 18 | 19 | All tests are run automatically on new pull requests via the 20 | ["Test on push or pull request" Github Action](.github/workflows/test.yml). This action 21 | triggers on: 22 | - new pull requests 23 | - pull request updates 24 | - commits being pushed to the `master` branch 25 | - manual dispatch via [Github UI](https://github.com/zeromq/pyre/actions/workflows/test.yml) 26 | 27 | ## Deployment 28 | 29 | Deployment works by bumping the version, building a source distribution (`sdist`), and 30 | uploading it to [PyPI](https://pypi.org/project/zeromq-pyre/) using [`twine`](https://twine.readthedocs.io/). 31 | 32 | These steps are automated as part of the ["Bump version"](.github/workflows/bump_version.yml) and ["Deploy to PyPI"](.github/workflows/deploy.yml) 33 | Github Actions. See below for details. 34 | 35 | ### Versioning 36 | 37 | This project follows the [Semantic Versioning Specification](https://semver.org/). 38 | 39 | To avoid human error, it is recommended to use the 40 | [`bump2version`](https://github.com/c4urself/bump2version) tool (configured in 41 | [`.bumpversion.cfg`](.bumpversion.cfg)). `bump2version` can be installed locally using 42 | the `deploy` extra requirements. To manually bump the version, run `bump2version `, 43 | where `` is either `major`, `minor`, or `patch`. 44 | 45 | **Note 1:** It is **not** recommended to run this tool manually. Instead, this step has 46 | been automated as part of the ["Bump version" Github Action](.github/workflows/bump_version.yml). 47 | To push the version-bumped commit back to the repo, the action requires more permissions 48 | than the [default `GITHUB_TOKEN` provides](https://github.com/zeromq/pyre/pull/155#issuecomment-861020168). 49 | Instead, it [requires a personal access token](https://docs.github.com/en/actions/reference/authentication-in-a-workflow#granting-additional-permissions) 50 | (PAT; stored and accessed as the `PERSONAL_ACCESS_TOKEN` secret). This allows writing 51 | to the repository and triggering the ["Deploy to PyPI" Github Action](.github/workflows/deploy.yml). 52 | 53 | **Note 2:** Due to security restrictions, pull requests from forked repositories do not 54 | have access to the workflow's secret. As a result, the "Bump version" workflow will fail 55 | and not trigger a deployment. In these cases, the deployment needs to be triggered by 56 | manually dispatching the workflow or pushing a tagged commit. 57 | 58 | ### Building a distribution 59 | 60 | The lowest common denominator for distributing a Python package is a [source distribution](https://packaging.python.org/guides/distributing-packages-using-setuptools/#source-distributions). 61 | It is a single artifact that can be installed and tested on Python 2 and Python 3. 62 | 63 | Note: It is best practice to also provide [pure Python wheels](https://packaging.python.org/guides/distributing-packages-using-setuptools/#pure-python-wheels). 64 | But the project would have to provide different wheels for Python 2 and 3 which would 65 | double the testing effort. Until project support for Python 2.7 is dropped (see #152 for 66 | reference), it is likely the best option to only distribute source distributions. 67 | 68 | The ["Test on push or pull request" Github Action](.github/workflows/test.yml) is 69 | setup to install and test the same source distribution that is also distributed to PyPI. 70 | 71 | ### Python Package Index (PyPI) 72 | 73 | The [Python Package Index (PyPI)](https://pypi.org/) is the official repository of 74 | software for the Python programming language. Its tight integration with the [recommended 75 | package installer (`pip`)](https://pypi.org/project/pip/) is the easiest way for users 76 | to install the project. It also allows other projects to easily define this project as 77 | a dependency. 78 | 79 | When triggered, the ["Deploy to PyPI" Github Action](.github/workflows/deploy.yml) builds a source distribution, and deploys it to PyPI. See [Github Action usage](#github-action-usage) 80 | below for information when the Github Action is triggered and how to control the version 81 | part that will be bumped. 82 | 83 | #### Authentification 84 | 85 | It is [strongly recommended to use PyPI API tokens](https://pypi.org/help/#apitoken) for 86 | the deployment authentification. The ["Deploy to PyPI" Github Action](.github/workflows/deploy.yml) 87 | is set up to use the [`PYPI_TOKEN` repository secret](https://github.com/zeromq/pyre/settings/secrets/actions) 88 | as an API token. 89 | 90 | ### Github Action usage 91 | 92 | The [Bump version](.github/workflows/bump_version.yml) Github Action triggers on: 93 | - Merged pull requests to the `master` branch 94 | - [Manual workflow dispatch](https://github.com/zeromq/pyre/actions/workflows/bump_version.yml) 95 | 96 | 97 | The ["Deploy to PyPI"](.github/workflows/deploy.yml) Github Action triggers on: 98 | - tags being pushed to the repository, including version bumps created by the [Bump version](.github/workflows/bump_version.yml) Github Action 99 | 100 | There are four version part values for automatic version bumping: `none`, `major`, 101 | `minor`, `patch` (default). For pull requests, you can assign one of the `bump:*` 102 | labels. If no label is assigned, the action will default to bumping the `patch` part. 103 | Should more than one `bump:*` label be assigned, the Github action will bump 104 | the part with the highest priority (`none` > `major` > `minor` > `patch`). 105 | 106 | For the manual action dispatch, one can pass one of the values directly to the action 107 | via the UI. 108 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE 2 | include AUTHORS 3 | include README.md 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Pyre 2 | ==== 3 | 4 | This is a Python port of [Zyre](http://zyre.org) 1.0, implementing the same [ZRE protocol](http://rfc.zeromq.org/spec:36). 5 | 6 | # Pyre - an open-source framework for proximity-based peer-to-peer applications 7 | 8 | ## Description 9 | 10 | Pyre does local area discovery and clustering. A Pyre node broadcasts 11 | UDP beacons, and connects to peers that it finds. This class wraps a 12 | Pyre node with a message-based API. 13 | 14 | All incoming events are messages delivered via the recv call of a Pyre 15 | instance. The first frame defines the type of the message, and following 16 | frames provide further values: 17 | 18 | ENTER fromnode headers 19 | a new peer has entered the network 20 | EXIT fromnode 21 | a peer has left the network 22 | JOIN fromnode groupname 23 | a peer has joined a specific group 24 | LEAVE fromnode groupname 25 | a peer has joined a specific group 26 | WHISPER fromnode message 27 | a peer has sent this node a message 28 | SHOUT fromnode groupname message 29 | a peer has sent one of our groups a message 30 | 31 | In SHOUT and WHISPER the message is a single frame in this version 32 | of Pyre. In ENTER, the headers frame contains a packed dictionary, 33 | that can be unpacked using json.loads(msg) (see chat client). 34 | 35 | To join or leave a group, use the join() and leave() methods. 36 | To set a header value, use the set_header() method. To send a message 37 | to a single peer, use whisper(). To send a message to a group, use 38 | shout(). 39 | 40 | ## Installation 41 | 42 | For now use Pip: 43 | 44 | pip install https://github.com/zeromq/pyre/archive/master.zip 45 | 46 | ## API 47 | 48 | import pyre 49 | # Constructor, creates a new Zyre node. Note that until you start the 50 | # node it is silent and invisible to other nodes on the network. 51 | node = pyre.Pyre() 52 | 53 | # Set node header; these are provided to other nodes during discovery 54 | # and come in each ENTER message. 55 | node.set_header(name, value) 56 | 57 | # (TODO: Currently a Pyre node starts immediately) Start node, after setting header values. When you start a node it 58 | # begins discovery and connection. 59 | node.start() 60 | 61 | # Stop node, this signals to other peers that this node will go away. 62 | # This is polite; however you can also just destroy the node without 63 | # stopping it. 64 | node.stop() 65 | 66 | # Join a named group; after joining a group you can send messages to 67 | # the group and all Zyre nodes in that group will receive them. 68 | node.join(group) 69 | 70 | # Leave a group 71 | node.leave(group) 72 | 73 | # Receive next message from network; the message may be a control 74 | # message (ENTER, EXIT, JOIN, LEAVE) or data (WHISPER, SHOUT). 75 | # Returns a list of message frames 76 | msgs = node.recv(); 77 | 78 | # Send message to single peer, specified as a UUID object (import uuid) 79 | # Destroys message after sending 80 | node.whisper(peer, msg) 81 | 82 | # Send message to a named group 83 | # Destroys message after sending 84 | node.shout(group, msg); 85 | 86 | # Send string to single peer specified as a UUID string. 87 | # String is formatted using printf specifiers. 88 | node.whispers(peer, msg_string) 89 | 90 | # Send message to a named group 91 | # Destroys message after sending 92 | node.shouts(group, msg_string); 93 | 94 | # Return handle to the Zyre node, for polling 95 | node.get_socket() 96 | # use node.get_socket().getsockopt(zmq.FD) to acquire 97 | # the filedescriptor 98 | # Don't use this for getting Pyre events you can use the 99 | # node.inbox to get those events 100 | 101 | ## Example Chat Client 102 | 103 | ```python 104 | try: 105 | from zyre_pyzmq import Zyre as Pyre 106 | except Exception as e: 107 | print("using Python native module", e) 108 | from pyre import Pyre 109 | 110 | from pyre import zhelper 111 | import zmq 112 | import uuid 113 | import logging 114 | import sys 115 | import json 116 | 117 | def chat_task(ctx, pipe): 118 | n = Pyre("CHAT") 119 | n.set_header("CHAT_Header1","example header1") 120 | n.set_header("CHAT_Header2","example header2") 121 | n.join("CHAT") 122 | n.start() 123 | 124 | poller = zmq.Poller() 125 | poller.register(pipe, zmq.POLLIN) 126 | print(n.socket()) 127 | poller.register(n.socket(), zmq.POLLIN) 128 | print(n.socket()) 129 | while(True): 130 | items = dict(poller.poll()) 131 | print(n.socket(), items) 132 | if pipe in items and items[pipe] == zmq.POLLIN: 133 | message = pipe.recv() 134 | # message to quit 135 | if message.decode('utf-8') == "$$STOP": 136 | break 137 | print("CHAT_TASK: %s" % message) 138 | n.shouts("CHAT", message.decode('utf-8')) 139 | else: 140 | #if n.socket() in items and items[n.socket()] == zmq.POLLIN: 141 | cmds = n.recv() 142 | msg_type = cmds.pop(0) 143 | print("NODE_MSG TYPE: %s" % msg_type) 144 | print("NODE_MSG PEER: %s" % uuid.UUID(bytes=cmds.pop(0))) 145 | print("NODE_MSG NAME: %s" % cmds.pop(0)) 146 | if msg_type.decode('utf-8') == "SHOUT": 147 | print("NODE_MSG GROUP: %s" % cmds.pop(0)) 148 | elif msg_type.decode('utf-8') == "ENTER": 149 | headers = json.loads(cmds.pop(0).decode('utf-8')) 150 | print("NODE_MSG HEADERS: %s" % headers) 151 | for key in headers: 152 | print("key = {0}, value = {1}".format(key, headers[key])) 153 | print("NODE_MSG CONT: %s" % cmds) 154 | n.stop() 155 | 156 | 157 | if __name__ == '__main__': 158 | # Create a StreamHandler for debugging 159 | logger = logging.getLogger("pyre") 160 | logger.setLevel(logging.INFO) 161 | logger.addHandler(logging.StreamHandler()) 162 | logger.propagate = False 163 | 164 | ctx = zmq.Context() 165 | chat_pipe = zhelper.zthread_fork(ctx, chat_task) 166 | # input in python 2 is different 167 | if sys.version_info.major < 3: 168 | input = raw_input 169 | 170 | while True: 171 | try: 172 | msg = input() 173 | chat_pipe.send(msg.encode('utf_8')) 174 | except (KeyboardInterrupt, SystemExit): 175 | break 176 | chat_pipe.send("$$STOP".encode('utf_8')) 177 | print("FINISHED") 178 | ``` 179 | 180 | Look at the [ZOCP](https://github.com/z25/pyZOCP) project for examples of how Pyre can be 181 | integrated into different environments and frameworks, i.e.: 182 | - [Urwid](https://github.com/z25/pyZOCP/blob/master/examples/urwZOCP.py) 183 | - [Blender](https://github.com/z25/pyZOCP/blob/master/examples/BpyZOCP.py) 184 | - [Glib](https://github.com/z25/pyZOCP/blob/master/examples/glib_node.py) 185 | - [QT](https://github.com/z25/pyZOCP/blob/master/examples/qt_ui_node.py) 186 | 187 | 188 | Pyre uses the [Python Logging](https://docs.python.org/3.4/library/logging.html) module. 189 | To change the debug level: 190 | 191 | ``` 192 | # Create a StreamHandler for debugging 193 | logger = logging.getLogger("pyre") 194 | logger.setLevel(logging.INFO) 195 | # i.e. logging.DEBUG, logging.WARNING 196 | logger.addHandler(logging.StreamHandler()) 197 | logger.propagate = False 198 | 199 | ``` 200 | 201 | ## Requirements 202 | 203 | Python only needs PyZMQ. On some older versions of Python 204 | it also needs the [ipaddress](https://docs.python.org/3.4/library/ipaddress.html?highlight=ipaddress#module-ipaddress) module. 205 | 206 | The recommended Python version is 3.3+ 207 | 208 | 209 | ## Project Organization 210 | 211 | Pyre is owned by all its authors and contributors. This is an open source 212 | project licensed under the LGPLv3. To contribute to Zyre please read the 213 | [C4.1 process](http://rfc.zeromq.org/spec:22) that we use. 214 | 215 | To report an issue, use the [PYRE issue tracker](https://github.com/zeromq/pyre/issues) at github.com. 216 | 217 | For more information on this project's maintenance, see [`MAINTENANCE.md`](MAINTENANCE.md). 218 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | #e -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | # This will setup a clean Ubuntu1404 LTS env with a python virtualenv called "pyre" for testing 5 | 6 | $script = <