├── .coveragerc
├── .gitignore
├── .travis.yml
├── CHANGELOG.rst
├── GUIDE.rst
├── LICENSE
├── MANIFEST.in
├── Makefile
├── README.md
├── UPGRADE.rst
├── benchmarks
├── test_concurrent_requests.py
├── test_peer_selection.py
└── test_roundtrip.py
├── crossdock
├── Dockerfile
├── __init__.py
├── docker-compose.yml
├── rules.mk
├── server
│ ├── __init__.py
│ ├── api.py
│ ├── server.py
│ └── simple-service.thrift
└── setup.py
├── docs
├── .gitignore
├── Makefile
├── api.rst
├── changelog.rst
├── conf.py
├── faq.rst
├── guide.rst
└── index.rst
├── examples
├── benchmark
│ └── thrift
│ │ ├── client.py
│ │ └── server.py
├── guide
│ └── keyvalue
│ │ ├── keyvalue
│ │ ├── __init__.py
│ │ ├── client.py
│ │ └── server.py
│ │ ├── service.thrift
│ │ └── setup.py
├── simple
│ ├── json
│ │ ├── client.py
│ │ └── server.py
│ ├── raw
│ │ ├── client.py
│ │ └── server.py
│ └── thrift
│ │ ├── client.py
│ │ └── server.py
└── sync
│ └── fanout
│ ├── client.py
│ └── server.py
├── hooks
└── pre-commit
├── requirements-docs.in
├── requirements-docs.txt
├── requirements-test.txt
├── requirements.in
├── requirements.txt
├── scripts
└── install-hooks.sh
├── setup.cfg
├── setup.py
├── tchannel
├── __init__.py
├── _future.py
├── _queue.py
├── container
│ ├── __init__.py
│ └── heap.py
├── context.py
├── deprecate.py
├── enum.py
├── errors.py
├── event.py
├── frame.py
├── glossary.py
├── health
│ ├── __init__.py
│ ├── health.py
│ └── meta.thrift
├── io.py
├── messages
│ ├── __init__.py
│ ├── base.py
│ ├── call_continue.py
│ ├── call_request.py
│ ├── call_request_continue.py
│ ├── call_response.py
│ ├── call_response_continue.py
│ ├── cancel.py
│ ├── claim.py
│ ├── common.py
│ ├── error.py
│ ├── init_request.py
│ ├── init_response.py
│ ├── ping_request.py
│ ├── ping_response.py
│ └── types.py
├── net.py
├── peer_heap.py
├── peer_strategy.py
├── request.py
├── response.py
├── retry.py
├── rw.py
├── schemes
│ ├── __init__.py
│ ├── json.py
│ ├── raw.py
│ └── thrift.py
├── serializer
│ ├── __init__.py
│ ├── json.py
│ ├── raw.py
│ └── thrift.py
├── singleton.py
├── statsd.py
├── status.py
├── sync
│ ├── __init__.py
│ ├── client.py
│ ├── singleton.py
│ └── thrift.py
├── tchannel.py
├── tcurl.py
├── testing
│ ├── __init__.py
│ └── vcr
│ │ ├── __init__.py
│ │ ├── cassette.py
│ │ ├── config.py
│ │ ├── exceptions.py
│ │ ├── patch.py
│ │ ├── proxy.py
│ │ ├── proxy.thrift
│ │ ├── record_modes.py
│ │ ├── server.py
│ │ └── yaml.py
├── thrift
│ ├── __init__.py
│ ├── client.py
│ ├── module.py
│ ├── reflection.py
│ ├── rw.py
│ └── server.py
├── tornado
│ ├── __init__.py
│ ├── connection.py
│ ├── dispatch.py
│ ├── hyperbahn.py
│ ├── message_factory.py
│ ├── peer.py
│ ├── request.py
│ ├── response.py
│ ├── stream.py
│ ├── tchannel.py
│ ├── tombstone.py
│ └── util.py
├── tracing.py
├── transport.py
└── zipkin
│ ├── __init__.py
│ └── zipkin_trace.py
├── tests
├── __init__.py
├── conftest.py
├── container
│ └── test_heap.py
├── data
│ ├── __init__.py
│ ├── generated
│ │ ├── ThriftTest
│ │ │ ├── SecondService-remote
│ │ │ ├── SecondService.py
│ │ │ ├── ThriftTest-remote
│ │ │ ├── ThriftTest.py
│ │ │ ├── __init__.py
│ │ │ ├── constants.py
│ │ │ └── ttypes.py
│ │ └── __init__.py
│ ├── hosts.json
│ └── idls
│ │ └── ThriftTest.thrift
├── integration
│ ├── __init__.py
│ ├── json
│ │ ├── __init__.py
│ │ └── test_json_server.py
│ ├── test_client_server.py
│ ├── test_error_handling.py
│ ├── test_retry.py
│ ├── thrift
│ │ ├── __init__.py
│ │ ├── test_thriftrw.py
│ │ └── test_tornado_client.py
│ └── tornado
│ │ ├── __init__.py
│ │ └── test_connection_reuse.py
├── messages
│ ├── __init__.py
│ └── test_common.py
├── mock_server.py
├── schemes
│ ├── test_json.py
│ ├── test_raw.py
│ └── test_thrift.py
├── serializer
│ ├── test_serializer_json.py
│ ├── test_serializer_raw.py
│ └── test_serializer_thrift.py
├── sync
│ ├── __init__.py
│ ├── test_client.py
│ ├── test_singleton.py
│ └── test_thrift.py
├── test_checksum.py
├── test_context.py
├── test_crossdock.py
├── test_dispatch.py
├── test_event.py
├── test_examples.py
├── test_forwarding.py
├── test_frame.py
├── test_future.py
├── test_health.py
├── test_hyperbahn.py
├── test_hyperbahn_blackhole.py
├── test_message_factory.py
├── test_messages.py
├── test_peer_heap.py
├── test_peer_strategy.py
├── test_queue.py
├── test_rw.py
├── test_singleton.py
├── test_statsd.py
├── test_stream.py
├── test_tchannel.py
├── test_tcurl.py
├── test_tracing.py
├── test_types.py
├── testing
│ └── vcr
│ │ ├── __init__.py
│ │ ├── integration
│ │ ├── data
│ │ │ ├── old_with_tracing.yaml
│ │ │ └── old_without_tracing.yaml
│ │ ├── test_sync_client.py
│ │ └── test_vcr.py
│ │ ├── strategies.py
│ │ ├── test_cassette.py
│ │ ├── test_patcher.py
│ │ └── test_server.py
├── thrift
│ ├── test_module.py
│ ├── test_multiple_services.py
│ ├── test_reflection.py
│ ├── test_rw.py
│ └── test_server.py
├── tornado
│ ├── __init__.py
│ ├── conftest.py
│ ├── test_connection.py
│ ├── test_dispatch.py
│ ├── test_hyperbahn.py
│ ├── test_peer.py
│ ├── test_request.py
│ ├── test_tchannel.py
│ └── test_tombstone.py
└── util.py
└── tox.ini
/.coveragerc:
--------------------------------------------------------------------------------
1 | [run]
2 | branch = true
3 |
4 | [report]
5 | exclude_lines =
6 | pragma: no cover
7 | raise NotImplementedError
8 | def __repr__
9 | def __str__
10 | omit =
11 | tchannel/health/thrift/*
12 | tchannel/zipkin/thrift/*
13 | tchannel/testing/vcr/proxy/*
14 | tchannel/testing/data/*
15 | examples/benchmark/thrift/service/*
16 | examples/guide/keyvalue/keyvalue/service/*
17 |
18 | [xml]
19 | output = coverage.xml
20 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | env/
3 | *.xml
4 | *.egg-info
5 | *.coverage*
6 | *.pyc
7 | .phutil_module_cache
8 | build/
9 | dist/
10 | htmlcov
11 | .tox
12 | *.swp
13 | .hypothesis/
14 | .DS_Store
15 | .idea/
16 | .cache/
17 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | ---
2 | sudo: required
3 | services:
4 | - docker
5 |
6 | #
7 | # testing multiple subdirectories
8 | # @see https://lord.io/blog/2014/travis-multiple-subdirs/
9 | # @see https://www.dominicrodger.com/tox-and-travis.html
10 | #
11 | language: python
12 | python:
13 | - "2.7"
14 | - "3.4"
15 | - "3.6"
16 | - "3.7"
17 | env:
18 | global:
19 | - DOCKER_COMPOSE_VERSION=1.8.0
20 | - COMMIT=${TRAVIS_COMMIT::8}
21 | # DOCKER_PASS
22 | - secure: jmujcA176F+2qIGThWJi2RVmAUf8bhqjri6FJJenknwHsl5gs3UXkgtux6h2C02IOvyj97Ng6GN5WIDRObhKcnke2M6KBKguWh9CapX5XgAEci9i1HYUG9bWrlzwYvWuj/VLZJwmzoprCVJExzRVghw9jWmDv8TTQs6T8RJuWORMN9nn51ovm+4FrMoOVQj4mUBEvU+mag+0i5INsJ2JZyXUpx2qTu05urjsEUtJxFq3dLSsEKM2y9oKRmJTO57xhGSS0ew+QRvr8h15HRUydqW5kmaTbLRCX2vuXLSlU9iMUdQxYsrht5i8NoGriv+uoxHaa/s904BwefMXWqmV76EXSKSypXBzBu2QhTvTX/wusnibiriMwkLLd0Xl+1bRqs5A+U+csi8u2R7Uim6wuWOCpg/ce6S/BWMWQtdkt90IY0a2cm2furp7g5kSqPShLz3wbKeD7d7GoYAcL53jI713ruaJBO+KJkr8IqyP7yk2KXa3l97OoaA9h9BYYgYd2fX9NQ6wHUA2qsNloJFydgGoGQ5jbG3da3x7kN7wHzOyNYOiB0LPejnQG66OrxWBvkcGR7zeEGIm+GicbwGZ03EebLl0LnXnvmSE5jJtcnjFQRLc4QLIdWViwTaZgll9h9A43GEum08JMqpKg/tWg1h+CnpC/dyizNSPGNqA5yg=
23 | # DOCKER_USER
24 | - secure: zTYWdKexitzOizfPCxZ/O7LwSy0B8P0iwwzHueXnVC/Z/8tR0lPorqfy8pY1Ctc5yRuFMDMrmk+HHcb1ox+QoBcWrSZwmR5GAe5Uf7Hy/qzngqGb4qg6cAtUEIzPQN8PGNI0sun50WiiZV4/M05pCL5IKYgilsfKE8ecBXmEu9iMyH7f1U9h7KVQoxe1VaOZHhSz549xUnug5H4Xq+ou62MxDhkLHMkBf9BsFyHWNbw06YIbbArho8V8Hmz28y3ejsjtRzPGS+BHhsX9lCek7MI9NNM5MWar1ZRhmU6qShmoRl9H0eG3bgs5/8iUQWLQQeEgEr8DgFcVYfHAavJqBenyir7Qok+9PBuP5lc/SvNgLmG5pHsUX+Py0luJzyTqDoFn+2TV+zfFYVdWpMnXrYKyE+NGL7QSvhfPby4EzAMA1KeS3QkK9F+1LPU7U6+Q92+cQ69GZGaVG8tAdy/XpJw+q+PSjflNxRVXkJp1mqGkyU07h5bGwCj6GE1FvE160QiFVg0Tz7rNGLRmYPRLAxk8gCc6E+rMljF+/LoLWdYohFfRoPe2K0l8rsi/wqsX4xLp22oWr29woKF40lyIokTLcv1sDo8xwMSeNA0ledUTREGb33fWoWoB/ZJDAnKZMY1SWUEi+owGtW3aRJlN5auOoPoe7LxMf3HlzyojEaQ=
25 | matrix:
26 | - TOX_ENV=crossdock
27 | - TOX_ENV=cover
28 | - TOX_ENV=flake8
29 | - TOX_ENV=docs
30 |
31 | before_install:
32 | - make crossdock_install_ci
33 |
34 | install:
35 | - make install
36 |
37 | script:
38 | - make test_ci
39 | - make crossdock_logs_ci
40 |
41 | after_success:
42 | - coveralls -v
43 |
44 | #after_success:
45 | #- export REPO=tchannelhub/xdock-py
46 | #- export BRANCH=$(if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then echo $TRAVIS_BRANCH; else echo $TRAVIS_PULL_REQUEST_BRANCH; fi)
47 | #- export TAG=`if [ "$BRANCH" == "master" ]; then echo "latest"; else echo $BRANCH; fi`
48 | #- export DOCKER=$(if [ "$TOX_ENV" == "crossdock" ]; then echo docker; else echo true; fi)
49 | #- $DOCKER login -u $DOCKER_USER -p $DOCKER_PASS
50 | #- $DOCKER build -f crossdock/Dockerfile -t $REPO:$COMMIT .
51 | #- $DOCKER tag $REPO:$COMMIT $REPO:$TAG
52 | #- $DOCKER tag $REPO:$COMMIT $REPO:travis-$TRAVIS_BUILD_NUMBER
53 | #- $DOCKER push $REPO
54 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2015 Uber Technologies, Inc.
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 | The above copyright notice and this permission notice shall be included in
10 | all copies or substantial portions of the Software.
11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
13 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
14 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
15 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
16 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
17 | THE SOFTWARE.
18 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | recursive-include tchannel *
2 | include README.rst
3 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | project := tchannel
2 |
3 | flake8 := flake8
4 | pytest := PYTHONDONTWRITEBYTECODE=1 py.test --tb short \
5 | --cov-config .coveragerc --cov $(project) \
6 | --async-test-timeout=1 --timeout=30 tests
7 |
8 | html_report := --cov-report html
9 | test_args := --cov-report term-missing
10 |
11 | TEST_HOST=127.0.0.1
12 | TEST_PORT=0
13 | TEST_LOG_FILE=test-server.log
14 |
15 | .DEFAULT_GOAL := test-lint
16 |
17 | -include crossdock/rules.mk
18 |
19 |
20 | env/bin/activate:
21 | virtualenv env
22 |
23 | env_install: env/bin/activate
24 | ./env/bin/pip install -r requirements-test.txt
25 | ./env/bin/python setup.py develop
26 |
27 | .PHONY: tox_install
28 | tox_install:
29 | pip install -r requirements-test.txt
30 | python setup.py develop
31 |
32 | .PHONY: install
33 | install: clean
34 | ifdef TOX_ENV
35 | make tox_install
36 | else
37 | make env_install
38 | endif
39 |
40 | .PHONY: test_server
41 | test_server:
42 | # TODO: use ${TEST_LOG_FILE}
43 | ./env/bin/python examples/tchannel_server.py --host ${TEST_HOST} --port ${TEST_PORT}
44 |
45 | .PHONY: test
46 | test: clean
47 | $(pytest) $(test_args)
48 |
49 | .PHONY: test_ci
50 | test_ci: clean
51 | ifeq ($(TOX_ENV), crossdock)
52 | $(MAKE) crossdock
53 | else
54 | pip install --upgrade setuptools
55 | tox -e $(TOX_ENV) -- tests
56 | endif
57 |
58 | .PHONY: benchmark
59 | benchmark:
60 | py.test benchmarks --benchmark-autosave --benchmark-save-data --benchmark-warmup --benchmark-disable-gc --benchmark-histogram
61 |
62 | .PHONY: testhtml
63 | testhtml: clean
64 | $(pytest) $(html_report) && open htmlcov/index.html
65 |
66 | .PHONY: clean
67 | clean:
68 | rm -rf dist/
69 | rm -rf build/
70 | @find $(project) tests -name "*.pyc" -delete
71 |
72 | .PHONY: lint
73 | lint:
74 | @$(flake8) $(project) tests examples setup.py
75 |
76 | .PHONY: test-lint
77 | test-lint: test lint
78 |
79 | .PHONY: docs
80 | docs:
81 | make -C docs html
82 |
83 | .PHONY: docsopen
84 | docsopen: docs
85 | open docs/_build/html/index.html
86 |
87 | .PHONY: vcr-thrift
88 | vcr-thrift:
89 | make -C ./tchannel/testing/vcr all
90 |
91 | .PHONY: gen_thrift
92 | gen_thrift:
93 | thrift --gen py:new_style,slots,dynamic -out tests/data/generated tests/data/idls/ThriftTest.thrift
94 |
95 | .PHONY: deps
96 | deps: requirements.txt requirements-docs.txt requirements-test.txt
97 | pip-sync requirements.txt requirements-docs.txt requirements-test.txt
98 |
99 | requirements.txt: requirements.in
100 | pip-compile --no-index requirements.in
101 | # Workaround for https://github.com/nvie/pip-tools/issues/325
102 | sed -i .txt '/-e /c\ ' requirements.txt
103 |
104 | requirements-docs.txt: requirements-docs.in
105 | pip-compile --no-index requirements-docs.in
106 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # TChannel for Python
2 |
3 | ### This project is now End Of Life.
4 |
5 | The Open Source team at Uber has marked this project as no longer active, and so it is now **End of Life** here at Uber.
6 |
7 | We are actively searching for contributors or organizations who would like to adopt this project. If you or your organizaiton is interested in taking over this project, or wants to build something similar, please reach out to us: ospo@uber.com.
8 |
9 | The project is archived to prevent any confusion about it's status.
10 |
11 | More details can be found on this issue: [Bug 497: This repository will be archived...](https://github.com/uber/tchannel-python/issues/497).
12 |
13 | ### Original Documentation
14 |
15 | A Python implementation of [TChannel](https://github.com/uber/tchannel).
16 |
17 | [Documentation](http://tchannel-python.readthedocs.org/en/latest/) is available on Read the Docs.
18 |
19 |
--------------------------------------------------------------------------------
/benchmarks/test_concurrent_requests.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import (
22 | absolute_import, unicode_literals, print_function, division
23 | )
24 |
25 | from tornado import gen
26 | from tornado.ioloop import IOLoop
27 |
28 | from tchannel import TChannel
29 |
30 |
31 | def setup_servers(num):
32 | servers = []
33 |
34 | for i in range(num):
35 | server = TChannel('server' + str(i))
36 |
37 | @server.raw.register
38 | def hello(request):
39 | return 'hello'
40 |
41 | server.listen()
42 | servers.append(server)
43 |
44 | return servers
45 |
46 |
47 | @gen.coroutine
48 | def setup_client(servers):
49 | known_peers = []
50 | for i in range(1000):
51 | known_peers.append('1.1.1.1:'+str(i))
52 | client = TChannel('client', known_peers=known_peers)
53 | # Add a bunch of unconnected peers
54 |
55 | @client.raw.register
56 | def hello(request):
57 | return 'hello'
58 | client.listen()
59 |
60 | # Open incoming connection from the server to the client.
61 | for server in servers:
62 | yield server.raw(
63 | service='server',
64 | endpoint='hello',
65 | body='hi',
66 | hostport=client.hostport,
67 | )
68 |
69 | raise gen.Return(client)
70 |
71 |
72 | @gen.coroutine
73 | def peer_test(client):
74 | fs = []
75 | for _ in range(100):
76 | fs.append(client.raw(
77 | service='server',
78 | endpoint='hello',
79 | body='hi',
80 | timeout=1000
81 | ))
82 | yield fs
83 |
84 |
85 | def stress_test(client):
86 | IOLoop.current().run_sync(lambda: peer_test(client))
87 |
88 |
89 | def test_peer_heap(benchmark):
90 | servers = setup_servers(100)
91 | client = IOLoop.current().run_sync(lambda: setup_client(servers))
92 |
93 | benchmark(stress_test, client)
94 |
--------------------------------------------------------------------------------
/benchmarks/test_roundtrip.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from tornado import ioloop, gen
22 |
23 | from tchannel import TChannel, thrift
24 |
25 |
26 | service = thrift.load(
27 | path='examples/guide/keyvalue/service.thrift',
28 | service='benchmark-server',
29 | )
30 |
31 |
32 | def test_roundtrip(benchmark):
33 | loop = ioloop.IOLoop.current()
34 |
35 | server = TChannel('benchmark-server')
36 | server.listen()
37 |
38 | clients = [TChannel('benchmark-client') for _ in range(10)]
39 |
40 | @server.thrift.register(service.KeyValue)
41 | def getValue(request):
42 | return 'bar'
43 |
44 | def roundtrip():
45 | @gen.coroutine
46 | def doit():
47 | futures = []
48 | # 10 clients send 10 requests concurrently
49 | for client in clients:
50 | for _ in range(10):
51 | futures.append(
52 | client.thrift(
53 | service.KeyValue.getValue("foo"),
54 | hostport=server.hostport,
55 | )
56 | )
57 | yield futures
58 |
59 | return loop.run_sync(doit)
60 |
61 | # Establish initial connection
62 | roundtrip()
63 |
64 | benchmark(roundtrip)
65 |
--------------------------------------------------------------------------------
/crossdock/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM python:2.7
2 |
3 | ENV APPDIR /usr/src/app/
4 | RUN mkdir -p ${APPDIR}
5 | WORKDIR ${APPDIR}
6 |
7 | # Application installation
8 | COPY requirements-test.txt ${APPDIR}
9 | COPY requirements.txt ${APPDIR}
10 |
11 | COPY setup.py ${APPDIR}
12 | COPY setup.cfg ${APPDIR}
13 | COPY tchannel ${APPDIR}/tchannel/
14 |
15 | # RUN pip install -U 'pip>=7,<8'
16 | RUN pip install --no-cache-dir -r requirements-test.txt
17 | RUN pip install --no-cache-dir -r requirements.txt
18 | RUN python setup.py install
19 |
20 | COPY crossdock ${APPDIR}/crossdock/
21 | COPY crossdock/setup.py ${APPDIR}/setup_crossdock.py
22 | RUN python setup_crossdock.py install
23 |
24 | CMD ["crossdock"]
25 | EXPOSE 8080-8082
26 |
--------------------------------------------------------------------------------
/crossdock/__init__.py:
--------------------------------------------------------------------------------
1 | from __future__ import absolute_import
2 |
--------------------------------------------------------------------------------
/crossdock/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '2'
2 |
3 | services:
4 | crossdock:
5 | image: crossdock/crossdock
6 | links:
7 | - go
8 | - python
9 | environment:
10 | - WAIT_FOR=go,python
11 |
12 | - AXIS_CLIENT=go
13 | - AXIS_S1NAME=go
14 | - AXIS_SAMPLED=true,false
15 | - AXIS_S2NAME=go,python
16 | - AXIS_S2ENCODING=json,thrift
17 | - AXIS_S3NAME=go,python
18 | - AXIS_S3ENCODING=json,thrift
19 |
20 | - BEHAVIOR_TRACE=client,s1name,sampled,s2name,s2encoding,s3name,s3encoding
21 | - REPORT=list
22 |
23 | go:
24 | image: tchannelhub/xdock-go:dev
25 | ports:
26 | - "8080-8082"
27 |
28 | python:
29 | build:
30 | context: ../.
31 | dockerfile: crossdock/Dockerfile
32 | ports:
33 | - "8080-8082"
34 |
--------------------------------------------------------------------------------
/crossdock/rules.mk:
--------------------------------------------------------------------------------
1 | XDOCK_YAML=crossdock/docker-compose.yml
2 |
3 | .PHONY: clean-compile
4 | clean-compile:
5 | find . -name '*.pyc' -exec rm {} \;
6 |
7 | .PHONY: docker
8 | docker: clean-compile
9 | docker build -f crossdock/Dockerfile -t jaeger-client-python .
10 |
11 | .PHONY: crossdock
12 | crossdock:
13 | docker-compose -f $(XDOCK_YAML) kill python
14 | docker-compose -f $(XDOCK_YAML) rm -f python
15 | docker-compose -f $(XDOCK_YAML) build python
16 | docker-compose -f $(XDOCK_YAML) run crossdock
17 |
18 | .PHONY: crossdock-fresh
19 | crossdock-fresh:
20 | docker-compose -f $(XDOCK_YAML) kill
21 | docker-compose -f $(XDOCK_YAML) rm --force
22 | docker-compose -f $(XDOCK_YAML) pull
23 | docker-compose -f $(XDOCK_YAML) build
24 | docker-compose -f $(XDOCK_YAML) run crossdock
25 |
26 | .PHONY: crossdock-logs
27 | crossdock-logs:
28 | docker-compose -f $(XDOCK_YAML) logs
29 |
30 | .PHONY: crossdock_install_ci
31 | crossdock_install_ci:
32 | ifeq ($(TOX_ENV), crossdock)
33 | docker version
34 | @echo "Installing docker-compose $${DOCKER_COMPOSE_VERSION:?'DOCKER_COMPOSE_VERSION env not set'}"
35 | sudo rm -f /usr/local/bin/docker-compose
36 | curl -L https://github.com/docker/compose/releases/download/$${DOCKER_COMPOSE_VERSION}/docker-compose-`uname -s`-`uname -m` > docker-compose
37 | chmod +x docker-compose
38 | sudo mv docker-compose /usr/local/bin
39 | docker-compose version
40 | else
41 | true
42 | endif
43 |
44 | .PHONY: crossdock_logs_ci
45 | crossdock_logs_ci:
46 | ifeq ($(TOX_ENV), crossdock)
47 | docker-compose -f $(XDOCK_YAML) logs
48 | else
49 | true
50 | endif
51 |
--------------------------------------------------------------------------------
/crossdock/server/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/uber/tchannel-python/248741146cb25f32fc3a1a3fabfb13d1d6b62355/crossdock/server/__init__.py
--------------------------------------------------------------------------------
/crossdock/server/simple-service.thrift:
--------------------------------------------------------------------------------
1 | struct Data {
2 | 1: required bool b1,
3 | 2: required string s2,
4 | 3: required i32 i3
5 | }
6 |
7 | service SimpleService {
8 | Data Call(1: Data arg)
9 | }
10 |
11 |
--------------------------------------------------------------------------------
/crossdock/setup.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 |
4 | from setuptools import setup, find_packages
5 |
6 | setup(
7 | name='crossdock',
8 | version='1.0.0',
9 | include_package_data=True,
10 | zip_safe=False,
11 | packages=find_packages(exclude=['tests', 'example', 'tests.*']),
12 | entry_points={
13 | 'console_scripts': [
14 | 'crossdock = crossdock.server.server:serve',
15 | ]
16 | },
17 | install_requires=[
18 | # all dependencies are included in tchannel already
19 | ],
20 | )
21 |
--------------------------------------------------------------------------------
/docs/.gitignore:
--------------------------------------------------------------------------------
1 | _build
2 |
--------------------------------------------------------------------------------
/docs/api.rst:
--------------------------------------------------------------------------------
1 | API Documentation
2 | =================
3 |
4 |
5 | TChannel
6 | --------
7 |
8 | .. autoclass:: tchannel.TChannel
9 | :special-members: __init__
10 | :members:
11 |
12 | .. autoclass:: tchannel.singleton.TChannel
13 | :members:
14 |
15 | .. autoclass:: tchannel.Request
16 | :members:
17 |
18 | .. autoclass:: tchannel.Response
19 | :members:
20 |
21 |
22 | Serialization Schemes
23 | ---------------------
24 |
25 | Thrift
26 | ~~~~~~
27 |
28 | .. autoclass:: tchannel.schemes.ThriftArgScheme
29 | :members: __call__, register
30 |
31 | .. autofunction:: tchannel.thrift.load
32 |
33 | .. autofunction:: tchannel.thrift_request_builder
34 |
35 | JSON
36 | ~~~~
37 |
38 | .. autoclass:: tchannel.schemes.JsonArgScheme
39 | :members: __call__, register
40 |
41 | Raw
42 | ~~~
43 | .. autoclass:: tchannel.schemes.RawArgScheme
44 | :members: __call__, register
45 |
46 |
47 | Exception Handling
48 | ------------------
49 |
50 | Errors
51 | ~~~~~~
52 | .. automodule:: tchannel.errors
53 | :members:
54 | :show-inheritance:
55 |
56 | Retry Behavior
57 | ~~~~~~~~~~~~~~
58 |
59 | These values can be passed as the ``retry_on`` behavior to
60 | :py:meth:`tchannel.TChannel.call`.
61 |
62 | .. automodule:: tchannel.retry
63 | :members:
64 |
65 |
66 | Synchronous Client
67 | ------------------
68 |
69 | .. autoclass:: tchannel.sync.TChannel
70 | :inherited-members:
71 | :members:
72 |
73 | .. autoclass:: tchannel.sync.singleton.TChannel
74 | :inherited-members:
75 | :members:
76 |
77 |
78 | Testing
79 | -------
80 |
81 | .. automodule:: tchannel.testing.vcr
82 |
83 | ..
84 | This automodule directive intentionally doesn't include :members: because
85 | the module documentation for it explicitly calls out members that should be
86 | documented.
87 |
88 |
--------------------------------------------------------------------------------
/docs/changelog.rst:
--------------------------------------------------------------------------------
1 | =========
2 | Changelog
3 | =========
4 |
5 | .. include:: ../CHANGELOG.rst
6 | .. include:: ../UPGRADE.rst
7 |
--------------------------------------------------------------------------------
/docs/faq.rst:
--------------------------------------------------------------------------------
1 | FAQ
2 | ===
3 |
4 | .. _fallback-endpoint:
5 |
6 | Can I register an endpoint that accepts all requests?
7 | -----------------------------------------------------
8 |
9 | The fallback endpoint is the endpoint called when an unrecognized request is
10 | received. By default, the fallback endpoint simply returns a
11 | ``BadRequestError`` to the caller. This behavior may be changed by
12 | registering an endpoint with ``TChannel.FALLBACK``.
13 |
14 | .. code-block:: python
15 |
16 | from tchannel import TChannel
17 |
18 | server = TChannel(name='myservice')
19 |
20 | @server.register(TChannel.FALLBACK)
21 | def handler(request):
22 | # ...
23 |
24 | This may be used to implement a TChannel server that can handle requests to all
25 | endpoints. Note that for the fallback endpoint, you have access to the raw
26 | bytes of the headers and the body. These must be serialized/deserialized
27 | manually.
28 |
29 | Why do I keep getting a "Cannot serialize MyType into a 'MyType'" error?
30 | ------------------------------------------------------------------------
31 |
32 | You are trying to mix code generated by Apache Thrift with the module generated
33 | by :py:func:`tchannel.thrift.load`. These are two separate ways of using Thrift
34 | with TChannel and the classes generated by either cannot be mixed and matched.
35 | You should be using only one of these approaches to interact with a specific
36 | service.
37 |
--------------------------------------------------------------------------------
/docs/guide.rst:
--------------------------------------------------------------------------------
1 | .. include:: ../GUIDE.rst
2 |
--------------------------------------------------------------------------------
/docs/index.rst:
--------------------------------------------------------------------------------
1 | TChannel for Python
2 | ===================
3 |
4 |
5 | |build-status| |coverage|
6 |
7 |
8 | A Python implementation of `TChannel`_.
9 |
10 |
11 | .. _TChannel: http://tchannel.readthedocs.org/
12 |
13 | .. |build-status| image:: https://travis-ci.org/uber/tchannel-python.svg?branch=master
14 | :alt: build status
15 | :scale: 100%
16 | :target: https://travis-ci.org/uber/tchannel-python
17 |
18 | .. |coverage| image:: https://coveralls.io/repos/uber/tchannel-python/badge.svg?branch=master&service=github
19 | :alt: coverage
20 | :scale: 100%
21 | :target: https://coveralls.io/github/uber/tchannel-python?branch=master
22 |
23 |
24 | .. toctree::
25 | :maxdepth: 2
26 |
27 | guide
28 | api
29 | faq
30 | changelog
31 |
--------------------------------------------------------------------------------
/examples/benchmark/thrift/client.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # Copyright (c) 2016 Uber Technologies, Inc.
3 | #
4 | # Permission is hereby granted, free of charge, to any person obtaining a copy
5 | # of this software and associated documentation files (the "Software"), to deal
6 | # in the Software without restriction, including without limitation the rights
7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | # copies of the Software, and to permit persons to whom the Software is
9 | # furnished to do so, subject to the following conditions:
10 | #
11 | # The above copyright notice and this permission notice shall be included in
12 | # all copies or substantial portions of the Software.
13 | #
14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | # THE SOFTWARE.
21 |
22 | from __future__ import absolute_import
23 | from __future__ import print_function
24 | import sys
25 | import threading
26 |
27 | from tornado import gen, ioloop
28 | from tchannel import TChannel, thrift
29 | from six.moves import range
30 |
31 | tchannel = TChannel('thrift-benchmark-client')
32 | service = thrift.load(
33 | path='examples/guide/keyvalue/service.thrift',
34 | service='thrift-benchmark',
35 | hostport='localhost:12345',
36 | )
37 |
38 | local = threading.local()
39 | local.requests = 0
40 |
41 |
42 | def report_work():
43 | print(local.requests)
44 | sys.stdout.flush()
45 | local.requests = 0
46 |
47 |
48 | @gen.coroutine
49 | def do_work():
50 |
51 | data = "a" * 4096
52 |
53 | while True:
54 | yield tchannel.thrift(
55 | request=service.KeyValue.setValue("key", data),
56 | )
57 | local.requests += 1
58 |
59 |
60 | if __name__ == '__main__':
61 | if len(sys.argv) > 1:
62 | concurrency = int(sys.argv[1])
63 | else:
64 | concurrency = 100
65 |
66 | sys.stderr.write('using concurrency %s\n' % concurrency)
67 | sys.stderr.flush()
68 |
69 | for _ in range(concurrency):
70 | do_work()
71 |
72 | ioloop.PeriodicCallback(report_work, 1000).start()
73 |
74 | try:
75 | ioloop.IOLoop.current().start()
76 | except KeyboardInterrupt:
77 | pass
78 |
--------------------------------------------------------------------------------
/examples/benchmark/thrift/server.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # Copyright (c) 2016 Uber Technologies, Inc.
3 | #
4 | # Permission is hereby granted, free of charge, to any person obtaining a copy
5 | # of this software and associated documentation files (the "Software"), to deal
6 | # in the Software without restriction, including without limitation the rights
7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | # copies of the Software, and to permit persons to whom the Software is
9 | # furnished to do so, subject to the following conditions:
10 | #
11 | # The above copyright notice and this permission notice shall be included in
12 | # all copies or substantial portions of the Software.
13 | #
14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | # THE SOFTWARE.
21 |
22 | from __future__ import absolute_import
23 | from tornado import ioloop
24 | from tchannel import TChannel, thrift
25 |
26 | tchannel = TChannel('keyvalue-server', hostport='localhost:12345')
27 | service = thrift.load('examples/guide/keyvalue/service.thrift')
28 |
29 | values = {'hello': 'world'}
30 |
31 |
32 | @tchannel.thrift.register(service.KeyValue)
33 | def getValue(request):
34 | key = request.body.key
35 | value = values.get(key)
36 |
37 | if value is None:
38 | raise service.NotFoundError(key)
39 |
40 | return value
41 |
42 |
43 | @tchannel.thrift.register(service.KeyValue)
44 | def setValue(request):
45 | key = request.body.key
46 | value = request.body.value
47 | values[key] = value
48 |
49 |
50 | def run():
51 | tchannel.listen()
52 |
53 |
54 | if __name__ == '__main__':
55 | run()
56 | ioloop.IOLoop.current().start()
57 |
--------------------------------------------------------------------------------
/examples/guide/keyvalue/keyvalue/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/uber/tchannel-python/248741146cb25f32fc3a1a3fabfb13d1d6b62355/examples/guide/keyvalue/keyvalue/__init__.py
--------------------------------------------------------------------------------
/examples/guide/keyvalue/keyvalue/client.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 | from __future__ import print_function
23 | from tornado import gen, ioloop
24 | from tchannel import TChannel, thrift
25 |
26 | tchannel = TChannel('keyvalue-consumer')
27 | service = thrift.load(
28 | path='examples/guide/keyvalue/service.thrift',
29 | service='keyvalue-server',
30 | hostport='localhost:8889',
31 | )
32 |
33 |
34 | @gen.coroutine
35 | def run():
36 |
37 | yield tchannel.thrift(
38 | service.KeyValue.setValue("foo", "Hello, world!"),
39 | )
40 |
41 | response = yield tchannel.thrift(
42 | service.KeyValue.getValue("foo"),
43 | )
44 |
45 | print(response.body)
46 |
47 |
48 | if __name__ == '__main__':
49 | ioloop.IOLoop.current().run_sync(run)
50 |
--------------------------------------------------------------------------------
/examples/guide/keyvalue/keyvalue/server.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 | from tornado import ioloop
23 | from tchannel import TChannel, thrift
24 |
25 | tchannel = TChannel('keyvalue-service', hostport='localhost:8889')
26 | service = thrift.load('examples/guide/keyvalue/service.thrift')
27 |
28 | values = {'hello': 'world'}
29 |
30 |
31 | @tchannel.thrift.register(service.KeyValue)
32 | def getValue(request):
33 | key = request.body.key
34 | value = values.get(key)
35 |
36 | if value is None:
37 | raise service.NotFoundError(key)
38 |
39 | return value
40 |
41 |
42 | @tchannel.thrift.register(service.KeyValue)
43 | def setValue(request):
44 | key = request.body.key
45 | value = request.body.value
46 | values[key] = value
47 |
48 |
49 | def run():
50 | tchannel.listen()
51 |
52 |
53 | if __name__ == '__main__':
54 | run()
55 | ioloop.IOLoop.current().start()
56 |
--------------------------------------------------------------------------------
/examples/guide/keyvalue/service.thrift:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2015 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | exception NotFoundError {
22 | 1: required string key,
23 | }
24 |
25 | service KeyValue {
26 |
27 | string getValue(
28 | 1: string key,
29 | ) throws (
30 | 1: NotFoundError notFound,
31 | )
32 |
33 | void setValue(
34 | 1: string key,
35 | 2: string value,
36 | )
37 | }
38 |
--------------------------------------------------------------------------------
/examples/guide/keyvalue/setup.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 | from setuptools import setup, find_packages
23 |
24 | setup(name='keyvalue', packages=find_packages())
25 |
--------------------------------------------------------------------------------
/examples/simple/json/client.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 | from __future__ import print_function
23 | import json
24 |
25 | from tornado import gen, ioloop
26 |
27 | from tchannel import TChannel
28 |
29 |
30 | tchannel = TChannel('json-client')
31 |
32 |
33 | @gen.coroutine
34 | def make_request():
35 |
36 | resp = yield tchannel.json(
37 | service='json-server',
38 | endpoint='endpoint',
39 | body={
40 | 'req': 'body'
41 | },
42 | headers={
43 | 'req': 'header'
44 | },
45 | hostport='localhost:54496',
46 | )
47 |
48 | raise gen.Return(resp)
49 |
50 |
51 | resp = ioloop.IOLoop.current().run_sync(make_request)
52 |
53 | assert resp.headers == {
54 | 'resp': 'header',
55 | }
56 | assert resp.body == {
57 | 'resp': 'body',
58 | }
59 |
60 | print(json.dumps(resp.body))
61 | print(json.dumps(resp.headers))
62 |
--------------------------------------------------------------------------------
/examples/simple/json/server.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 | from __future__ import print_function
23 | from tornado import ioloop
24 |
25 | from tchannel import TChannel, Response
26 |
27 |
28 | tchannel = TChannel('json-server', hostport='localhost:54496')
29 |
30 |
31 | @tchannel.json.register
32 | def endpoint(request):
33 |
34 | assert request.headers == {'req': 'header'}
35 | assert request.body == {'req': 'body'}
36 |
37 | return Response({'resp': 'body'}, headers={'resp': 'header'})
38 |
39 |
40 | tchannel.listen()
41 |
42 | print(tchannel.hostport)
43 |
44 | ioloop.IOLoop.current().start()
45 |
--------------------------------------------------------------------------------
/examples/simple/raw/client.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 | from __future__ import print_function
23 | from tornado import gen, ioloop
24 |
25 | from tchannel import TChannel
26 |
27 |
28 | tchannel = TChannel('raw-client')
29 |
30 |
31 | @gen.coroutine
32 | def make_request():
33 |
34 | resp = yield tchannel.raw(
35 | service='raw-server',
36 | endpoint='endpoint',
37 | body='req body',
38 | headers='req headers',
39 | hostport='localhost:54495',
40 | )
41 |
42 | raise gen.Return(resp)
43 |
44 |
45 | resp = ioloop.IOLoop.current().run_sync(make_request)
46 |
47 | assert resp.headers == b'resp headers'
48 | assert resp.body == b'resp body'
49 |
50 | print(resp.body.decode('utf8'))
51 | print(resp.headers.decode('utf8'))
52 |
--------------------------------------------------------------------------------
/examples/simple/raw/server.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import print_function
22 |
23 | from __future__ import absolute_import
24 | from tornado import gen, ioloop
25 |
26 | from tchannel import TChannel, Response
27 |
28 |
29 | tchannel = TChannel('raw-server', hostport='localhost:54495')
30 |
31 |
32 | @tchannel.raw.register
33 | @gen.coroutine
34 | def endpoint(request):
35 |
36 | assert request.headers == b'req headers'
37 | assert request.body == b'req body'
38 |
39 | return Response(b'resp body', headers=b'resp headers')
40 |
41 |
42 | tchannel.listen()
43 |
44 | print(tchannel.hostport)
45 |
46 | ioloop.IOLoop.current().start()
47 |
--------------------------------------------------------------------------------
/examples/simple/thrift/client.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 | from __future__ import print_function
23 | import json
24 |
25 | from tornado import gen
26 | from tornado import ioloop
27 |
28 | from tchannel import TChannel, thrift
29 |
30 | tchannel = TChannel('thrift-client')
31 | service = thrift.load(
32 | path='tests/data/idls/ThriftTest.thrift',
33 | service='thrift-server',
34 | hostport='localhost:54497',
35 | )
36 |
37 |
38 | @gen.coroutine
39 | def make_request():
40 |
41 | resp = yield tchannel.thrift(
42 | request=service.ThriftTest.testString(thing="req"),
43 | headers={
44 | 'req': 'header',
45 | },
46 | )
47 |
48 | raise gen.Return(resp)
49 |
50 |
51 | resp = ioloop.IOLoop.current().run_sync(make_request)
52 |
53 | assert resp.headers == {
54 | 'resp': 'header',
55 | }
56 | assert resp.body == 'resp'
57 |
58 | print(resp.body)
59 | print(json.dumps(resp.headers))
60 |
--------------------------------------------------------------------------------
/examples/simple/thrift/server.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 |
23 | from __future__ import print_function
24 | from tornado import gen, ioloop
25 | from tchannel import TChannel, Response, thrift
26 |
27 | tchannel = TChannel('thrift-server', hostport='localhost:54497')
28 | service = thrift.load('tests/data/idls/ThriftTest.thrift')
29 |
30 |
31 | @tchannel.thrift.register(service.ThriftTest)
32 | @gen.coroutine
33 | def testString(request):
34 |
35 | assert request.headers == {'req': 'header'}
36 | assert request.body.thing == 'req'
37 |
38 | return Response('resp', headers={'resp': 'header'})
39 |
40 |
41 | tchannel.listen()
42 |
43 | print(tchannel.hostport)
44 |
45 | ioloop.IOLoop.current().start()
46 |
--------------------------------------------------------------------------------
/examples/sync/fanout/client.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 | from __future__ import print_function
23 | import json
24 |
25 | from tchannel import thrift
26 | from tchannel.sync import TChannel
27 | from six.moves import range
28 |
29 | tchannel = TChannel('thrift-client')
30 | service = thrift.load(
31 | path='tests/data/idls/ThriftTest.thrift',
32 | service='thrift-server',
33 | hostport='localhost:54498',
34 | )
35 |
36 |
37 | def make_requests():
38 |
39 | # Fan-out
40 | futures = [tchannel.thrift(
41 | request=service.ThriftTest.testString(thing="req"),
42 | headers={
43 | 'req': 'header',
44 | },
45 | ) for _ in range(20)]
46 |
47 | # Fan-in
48 | for future in futures:
49 | response = future.result()
50 |
51 | return response
52 |
53 |
54 | resp = make_requests()
55 |
56 | assert resp.headers == {
57 | 'resp': 'header',
58 | }
59 | assert resp.body == 'resp' * 100000
60 |
61 | print(resp.body[:4])
62 | print(json.dumps(resp.headers))
63 |
--------------------------------------------------------------------------------
/examples/sync/fanout/server.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 |
23 | from __future__ import print_function
24 | from tornado import gen, ioloop
25 | from tchannel import TChannel, Response, thrift
26 |
27 | tchannel = TChannel('thrift-server', hostport='localhost:54498')
28 | service = thrift.load('tests/data/idls/ThriftTest.thrift')
29 |
30 |
31 | @tchannel.thrift.register(service.ThriftTest)
32 | @gen.coroutine
33 | def testString(request):
34 |
35 | assert request.headers == {'req': 'header'}
36 | assert request.body.thing == 'req'
37 |
38 | return Response('resp' * 100000, headers={'resp': 'header'})
39 |
40 |
41 | tchannel.listen()
42 |
43 | print(tchannel.hostport)
44 |
45 | ioloop.IOLoop.current().start()
46 |
--------------------------------------------------------------------------------
/hooks/pre-commit:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Ensures that the license blurb is added to all source code.
4 |
5 | findup() {
6 | if [[ -f "$1/$2" ]]; then
7 | echo "$1"
8 | else
9 | findUp "$1/.." "$2"
10 | fi
11 | }
12 |
13 | ROOT=$(realpath "$(findup "." "setup.py")")
14 | LICENSE_EXEC="$ROOT/node_modules/.bin/uber-licence"
15 |
16 | ensure_license() {
17 | cwd="$(pwd)"
18 | # This is ugly but it basically executes the uber-license tool, parses the
19 | # output to figure out which files were changed, and stages them into the
20 | # commit.
21 | ("$LICENSE_EXEC" --file '*.py' || exit 1) \
22 | | grep '^fixed file ' | perl -F"'" -ane 'print "$F[1]\n"' \
23 | | while read fileName; do
24 | echo "License added to $cwd/$fileName"
25 | git add "$fileName"
26 | done
27 | }
28 |
29 | unset GIT_DIR
30 |
31 | # Ensure uber-licence is installed.
32 | if [[ ! -x "$LICENSE_EXEC" ]]; then
33 | echo "uber-licence is not installed. Installing."
34 | pushd "$ROOT" &>/dev/null
35 | npm i uber-licence
36 | popd &>/dev/null
37 | fi
38 |
39 | for d in tchannel tests benchmarks examples; do
40 | pushd "$ROOT/$d" &>/dev/null
41 | ensure_license
42 | popd &>/dev/null
43 | done
44 |
--------------------------------------------------------------------------------
/requirements-docs.in:
--------------------------------------------------------------------------------
1 | wrapt
2 | thrift==0.9.2
3 | sphinx
4 | sphinx_rtd_theme
5 |
--------------------------------------------------------------------------------
/requirements-docs.txt:
--------------------------------------------------------------------------------
1 | #
2 | # This file is autogenerated by pip-compile
3 | # Make changes in requirements-docs.in, then run this to update:
4 | #
5 | # pip-compile --no-index requirements-docs.in
6 | #
7 | alabaster==0.7.7 # via sphinx
8 | babel==2.2.0 # via sphinx
9 | docutils==0.12 # via sphinx
10 | jinja2==2.8 # via sphinx
11 | markupsafe==0.23 # via jinja2
12 | pygments==2.1.2 # via sphinx
13 | pytz==2015.7 # via babel
14 | six==1.10.0 # via sphinx
15 | snowballstemmer==1.2.1 # via sphinx
16 | sphinx-rtd-theme==0.1.9
17 | sphinx==1.3.6
18 | thrift==0.9.2
19 | wrapt==1.10.8
20 |
--------------------------------------------------------------------------------
/requirements-test.txt:
--------------------------------------------------------------------------------
1 | # Testing and coverage
2 | pytest<5
3 | pytest-cov
4 | pytest-timeout
5 | pytest-tornado
6 | # bound pygal due to https://github.com/ionelmc/pytest-benchmark/issues/50
7 | pygal<2.1
8 | pytest-benchmark[histogram]>=3,<4
9 |
10 | # Property based tests
11 | hypothesis>=1.14,<2
12 |
13 | # Integration test utilities
14 | sh
15 | psutil
16 |
17 | # Test all the pythons
18 | tox
19 |
20 | #
21 | # futures is installed implicitly through threadloop,
22 | # but tox has an issue where it doesn't pick that up,
23 | # causing tests to fail. This can be reproduced locally
24 | # by removing futures from this file and running:
25 | #
26 | # tox -e py27 tests
27 | #
28 | # @see https://travis-ci.org/uber/tchannel-python/jobs/86710564
29 | #
30 | futures
31 |
32 | # Syntax checker
33 | flake8==2.2.5
34 |
35 | # Optional dependency, but must be tested er'ry time
36 | thrift==0.11.0
37 |
38 | # Smarter decorators
39 | wrapt>=1.10,<1.11
40 |
41 | # OpenTracing reference implementation
42 | jaeger_client>4
43 |
44 | # Mocks
45 | mock==1.0.1
46 | doubles
47 |
48 | # for releases
49 | pip-tools
50 | zest.releaser[recommended]
51 | twine
52 |
53 | # for debugging
54 | ipdb
55 | coveralls
56 |
57 | PyYAML
58 |
--------------------------------------------------------------------------------
/requirements.in:
--------------------------------------------------------------------------------
1 | # High-level dependencies are read from setup.py.
2 | -e .
3 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | #
2 | # This file is autogenerated by pip-compile
3 | # To update, run:
4 | #
5 | # pip-compile requirements.in
6 | #
7 | backports-abc==0.4 # via tornado
8 | contextlib2==0.5.1 # via opentracing-instrumentation
9 | crcmod==1.7
10 | future==0.17.1 # via opentracing-instrumentation
11 | futures==3.0.5
12 | opentracing-instrumentation==3.2.0
13 | opentracing==2.2.0 # via opentracing-instrumentation
14 | ply==3.8 # via thriftrw
15 | six==1.10.0 # via opentracing-instrumentation, thriftrw
16 | threadloop==1.0.1
17 | thriftrw==1.2.3
18 | tornado==4.3 # via opentracing-instrumentation, threadloop
19 | wrapt==1.10.8 # via opentracing-instrumentation
20 |
--------------------------------------------------------------------------------
/scripts/install-hooks.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # This installs all hooks inside ./hooks into .git/hooks, skipping anything
4 | # that's already there.
5 |
6 | set -e
7 |
8 | ROOT=$(pwd)
9 |
10 | if [[ ! -d "$ROOT/.git" ]]; then
11 | echo "Please run this from the project root."
12 | exit 1
13 | fi
14 |
15 | find "$ROOT/hooks" -type file | while read hook; do
16 | name=$(basename "$hook")
17 | dest="$ROOT/.git/hooks/$name"
18 | if [[ -f "$dest" ]]; then
19 | echo "Skipping hook $name because it's already installed."
20 | else
21 | ln -s "$hook" "$dest"
22 | echo "Installed hook $name."
23 | fi
24 | done
25 |
--------------------------------------------------------------------------------
/setup.cfg:
--------------------------------------------------------------------------------
1 | [zest.releaser]
2 | create-wheel = yes
3 | python-file-with-version = tchannel/__init__.py
4 |
5 | [flake8]
6 | # Ignore files generated by Thrift
7 | exclude =
8 | examples/guide/keyvalue/keyvalue/service/*,
9 | examples/benchmark/thrift/service/*,
10 | tchannel/health/thrift/*,
11 | tchannel/zipkin/thrift/*,
12 | tchannel/testing/vcr/proxy/*,
13 | tests/data/generated/ThriftTest/*
14 |
15 | [tool:pytest]
16 | markers =
17 | call: mark a test as using the client's calling API.
18 | integration: mark a test as a full client/server interaction between separate processes.
19 | heapfuzz: mark a test for heapfuzz
20 | concurrency_test: mark a test for concurrency
21 | hypothesis: mark a test for hypothesis
22 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | from setuptools import find_packages, setup
2 |
3 | import re
4 |
5 | version = None
6 | with open('tchannel/__init__.py', 'r') as f:
7 | for line in f:
8 | m = re.match(r'^__version__\s*=\s*(["\'])([^"\']+)\1', line)
9 | if m:
10 | version = m.group(2)
11 | break
12 |
13 | if not version:
14 | raise Exception(
15 | 'Could not determine version number from tchannel/__init__.py'
16 | )
17 |
18 |
19 | setup(
20 | name='tchannel',
21 | version=version,
22 | author=', '.join([
23 | 'Abhinav Gupta',
24 | 'Aiden Scandella',
25 | 'Bryce Lampe',
26 | 'Grayson Koonce',
27 | 'Junchao Wu',
28 | ]),
29 | author_email='abg@uber.com',
30 | description='Network multiplexing and framing protocol for RPC',
31 | license='MIT',
32 | url='https://github.com/uber/tchannel-python',
33 | keywords=['rpc'],
34 | classifiers=[
35 | 'Development Status :: 4 - Beta',
36 | 'Intended Audience :: Developers',
37 | 'License :: OSI Approved :: MIT License',
38 | 'Programming Language :: Python :: 2',
39 | 'Programming Language :: Python :: Implementation :: PyPy',
40 | 'Topic :: Software Development :: Libraries :: Python Modules',
41 | ],
42 | packages=find_packages(exclude=['crossdock', 'tests', 'tests.*']),
43 | package_data={
44 | '': ['*.thrift'],
45 | },
46 | install_requires=[
47 | # stdlib backports, no constraints needed
48 | 'contextlib2',
49 | 'futures',
50 |
51 | # external deps
52 | 'crcmod>=1,<2',
53 | 'tornado>=4.3,<5',
54 |
55 | # tchannel deps
56 | 'thriftrw>=0.4,<2',
57 | 'threadloop>=1,<2',
58 |
59 | # tracing deps
60 | 'opentracing>2',
61 | 'opentracing_instrumentation>3',
62 |
63 | # python 3 compat
64 | 'six',
65 | 'future'
66 | ],
67 | extras_require={
68 | 'vcr': ['PyYAML', 'mock', 'wrapt'],
69 | },
70 | entry_points={
71 | 'console_scripts': [
72 | 'tcurl.py = tchannel.tcurl:start_ioloop'
73 | ]
74 | },
75 | )
76 |
--------------------------------------------------------------------------------
/tchannel/__init__.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import (
22 | absolute_import, division, print_function, unicode_literals
23 | )
24 |
25 | __version__ = '2.0.2.dev0'
26 | # Update setup.py when changing this. zest.releaser doesn't support updating
27 | # both of them yet.
28 |
29 |
30 | from .response import Response # noqa
31 | from .request import Request # noqa
32 | from .tchannel import TChannel # noqa
33 | from .thrift import thrift_request_builder # noqa
34 |
--------------------------------------------------------------------------------
/tchannel/_future.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import (
22 | absolute_import, unicode_literals, division, print_function
23 | )
24 |
25 | import sys
26 | from functools import wraps
27 |
28 | from tornado.gen import is_future
29 |
30 |
31 | def fail_to(future):
32 | """A decorator for function callbacks to catch uncaught non-async
33 | exceptions and forward them to the given future.
34 |
35 | The primary use for this is to catch exceptions in async callbacks and
36 | propagate them to futures. For example, consider,
37 |
38 | .. code-block:: python
39 |
40 | answer = Future()
41 |
42 | def on_done(future):
43 | foo = bar()
44 | answer.set_result(foo)
45 |
46 | some_async_operation().add_done_callback(on_done)
47 |
48 | If ``bar()`` fails, ``answer`` will never get filled with an exception or
49 | a result. Now if we change ``on_done`` to,
50 |
51 | .. code-block:: python
52 |
53 | @fail_to(answer)
54 | def on_done(future):
55 | foo = bar()
56 | answer.set_result(foo)
57 |
58 | Uncaught exceptions in ``on_done`` will be caught and propagated to
59 | ``answer``. Note that ``on_done`` will return None if an exception was
60 | caught.
61 |
62 | :param answer:
63 | Future to which the result will be written.
64 | """
65 | assert is_future(future), 'you forgot to pass a future'
66 |
67 | def decorator(f):
68 |
69 | @wraps(f)
70 | def new_f(*args, **kwargs):
71 | try:
72 | return f(*args, **kwargs)
73 | except Exception:
74 | future.set_exc_info(sys.exc_info())
75 |
76 | return new_f
77 |
78 | return decorator
79 |
--------------------------------------------------------------------------------
/tchannel/container/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/uber/tchannel-python/248741146cb25f32fc3a1a3fabfb13d1d6b62355/tchannel/container/__init__.py
--------------------------------------------------------------------------------
/tchannel/deprecate.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import (
22 | absolute_import, division, print_function, unicode_literals
23 | )
24 |
25 | import functools
26 | import warnings
27 |
28 |
29 | def deprecate(message):
30 | """Loudly prints warning."""
31 | warnings.simplefilter('default')
32 | warnings.warn(message, category=DeprecationWarning)
33 | warnings.resetwarnings()
34 |
35 |
36 | def deprecated(message):
37 | """Warn every time a fn is called."""
38 | def decorator(fn):
39 | @functools.wraps(fn)
40 | def new_fn(*args, **kwargs):
41 | deprecate(message)
42 | return fn(*args, **kwargs)
43 | return new_fn
44 | return decorator
45 |
--------------------------------------------------------------------------------
/tchannel/enum.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 |
23 | import collections
24 |
25 |
26 | def enum(class_name, **values):
27 | class_type = collections.namedtuple(class_name, list(values.keys()))
28 | instance = class_type(**values)
29 | return instance
30 |
--------------------------------------------------------------------------------
/tchannel/frame.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 |
23 | from collections import namedtuple
24 |
25 | from . import rw
26 | from .errors import ReadError
27 | from .io import BytesIO
28 |
29 | FrameHeader = namedtuple('FrameHeader', 'message_type message_id')
30 | Frame = namedtuple('Frame', 'header payload')
31 |
32 |
33 | class FrameReadWriter(rw.ReadWriter):
34 |
35 | # ReadWriter for Frame size
36 | size_rw = rw.number(2) # size:2
37 |
38 | # ReadWriter for FrameHeaders
39 | header_rw = rw.instance(
40 | FrameHeader,
41 | ('message_type', rw.number(1)), # type:1
42 | (rw.skip, rw.constant(rw.number(1), 0)), # reserved:1
43 | ('message_id', rw.number(4)), # id:4
44 | (rw.skip, rw.constant(rw.number(8), 0)), # reserved:8
45 | )
46 |
47 | def read(self, stream, size=None):
48 | if not size:
49 | try:
50 | size = self.size_rw.read(stream)
51 | except ReadError:
52 | return None
53 | if not size:
54 | return None
55 |
56 | body = self.take(stream, size - self.size_rw.width())
57 |
58 | header_width = self.header_rw.width()
59 | header_body, payload = body[:header_width], body[header_width:]
60 |
61 | header = self.header_rw.read(BytesIO(header_body))
62 | return Frame(header, payload)
63 |
64 | def write(self, frame, stream):
65 | prelude_size = self.size_rw.width() + self.header_rw.width()
66 | size = prelude_size + len(frame.payload)
67 |
68 | self.size_rw.write(size, stream)
69 | self.header_rw.write(frame.header, stream)
70 | stream.write(frame.payload)
71 |
72 | return stream
73 |
74 | def width(self):
75 | return self.size_rw.width() + self.header_rw.width()
76 |
77 | frame_rw = FrameReadWriter()
78 |
--------------------------------------------------------------------------------
/tchannel/glossary.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 |
23 | import platform
24 |
25 | from . import __version__
26 |
27 | # Largest message ID supported by the system.
28 | # Message ID 0xffffffff is reserved
29 | MAX_MESSAGE_ID = 0xfffffffe
30 |
31 | # CallRequestMessage uses it as the default TTL value for the message.
32 | DEFAULT_TIMEOUT = 30 # seconds
33 |
34 | TCHANNEL_LANGUAGE = 'python'
35 |
36 | # python environment, eg 'CPython-2.7.10'
37 | TCHANNEL_LANGUAGE_VERSION = (
38 | platform.python_implementation() + '-' + platform.python_version()
39 | )
40 |
41 | # version format x.y.z
42 | TCHANNEL_VERSION = __version__
43 |
--------------------------------------------------------------------------------
/tchannel/health/__init__.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 |
23 | from .health import health # noqa
24 | from .health.meta import HealthStatus # noqa
25 | from .health.meta import Meta # noqa
26 |
--------------------------------------------------------------------------------
/tchannel/health/health.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 |
23 | import os
24 | import sys
25 |
26 | from .. import thrift
27 |
28 |
29 | base = os.path.dirname(__file__)
30 | meta = thrift.load(os.path.join(base, 'meta.thrift'))
31 | sys.modules[__name__ + '.meta'] = meta
32 |
33 |
34 | def health(request):
35 | return meta.HealthStatus(ok=True)
36 |
--------------------------------------------------------------------------------
/tchannel/health/meta.thrift:
--------------------------------------------------------------------------------
1 | struct HealthStatus {
2 | 1: required bool ok
3 | 2: optional string message
4 | }
5 |
6 | service Meta {
7 | HealthStatus health()
8 | }
9 |
--------------------------------------------------------------------------------
/tchannel/io.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 |
23 | try:
24 | from cStringIO import StringIO as BytesIO
25 | except ImportError: # pragma: no cover
26 | from io import BytesIO # noqa
27 |
--------------------------------------------------------------------------------
/tchannel/messages/__init__.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 |
23 | from .call_request import CallRequestMessage, call_req_rw
24 | from .call_request_continue import call_req_c_rw
25 | from .call_response import CallResponseMessage, call_res_rw
26 | from .call_response_continue import call_res_c_rw
27 | from .cancel import CancelMessage, cancel_rw
28 | from .claim import ClaimMessage, claim_rw
29 | from .common import Tracing, ChecksumType
30 | from .error import ErrorMessage, ErrorCode, error_rw
31 | from .init_request import InitRequestMessage, init_req_rw
32 | from .init_response import InitResponseMessage, init_res_rw
33 | from .ping_request import PingRequestMessage, ping_req_rw
34 | from .ping_response import PingResponseMessage, ping_res_rw
35 | from .types import Types
36 |
37 | RW = {
38 | Types.CALL_REQ: call_req_rw,
39 | Types.CALL_REQ_CONTINUE: call_req_c_rw,
40 | Types.CALL_RES: call_res_rw,
41 | Types.CALL_RES_CONTINUE: call_res_c_rw,
42 | Types.CANCEL: cancel_rw,
43 | Types.CLAIM: claim_rw,
44 | Types.ERROR: error_rw,
45 | Types.INIT_REQ: init_req_rw,
46 | Types.INIT_RES: init_res_rw,
47 | Types.PING_REQ: ping_req_rw,
48 | Types.PING_RES: ping_res_rw,
49 | }
50 |
51 | __all__ = [
52 | "RW",
53 | "ChecksumType",
54 | "CallRequestMessage",
55 | "CallRequestContinueMessage",
56 | "CallResponseMessage",
57 | "CallResponseContinueMessage",
58 | "CancelMessage",
59 | "ClaimMessage",
60 | "ErrorMessage",
61 | "ErrorCode",
62 | "InitRequestMessage",
63 | "InitResponseMessage",
64 | "PingRequestMessage",
65 | "PingResponseMessage",
66 | "Tracing",
67 | ]
68 |
--------------------------------------------------------------------------------
/tchannel/messages/base.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 |
23 |
24 | class BaseMessage(object):
25 | """Represent common functionality across all TChannel messages."""
26 | message_type = None
27 |
28 | __slots__ = ('id',)
29 |
30 | def __init__(self, id=0):
31 | self.id = id
32 |
33 | def __eq__(self, other):
34 | if other is None:
35 | return False
36 | return all(
37 | getattr(self, attr) == getattr(other, attr)
38 | for attr in self.__slots__
39 | )
40 |
41 | def __str__(self):
42 | attrs = [
43 | "%s=%s" % (attr, str(getattr(self, attr)))
44 | for attr in self.__slots__
45 | ]
46 |
47 | return "%s(%s)" % (
48 | str(self.__class__.__name__),
49 | ", ".join(attrs)
50 | )
51 |
52 | def __repr__(self):
53 | return self.__str__()
54 |
--------------------------------------------------------------------------------
/tchannel/messages/call_request.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 |
23 | from . import common
24 | from .. import rw
25 | from ..glossary import DEFAULT_TIMEOUT
26 | from .call_request_continue import CallRequestContinueMessage
27 | from .types import Types
28 |
29 |
30 | class CallRequestMessage(CallRequestContinueMessage):
31 | """Initiate an RPC call."""
32 | message_type = Types.CALL_REQ
33 |
34 | __slots__ = CallRequestContinueMessage.__slots__ + (
35 | 'ttl',
36 | 'tracing',
37 | 'service',
38 | 'headers',
39 | )
40 |
41 | def __init__(
42 | self,
43 | flags=0,
44 | ttl=DEFAULT_TIMEOUT,
45 | tracing=None,
46 | service=None,
47 | headers=None,
48 | checksum=None,
49 | args=None,
50 | id=0,
51 | ):
52 | args = args or [b"", b"", b""]
53 | super(CallRequestMessage, self).__init__(flags, checksum, args, id)
54 | self.ttl = ttl
55 | self.tracing = tracing or common.Tracing(0, 0, 0, 0)
56 | self.service = service or ''
57 | self.headers = dict(headers) if headers else {}
58 |
59 | call_req_rw = rw.instance(
60 | CallRequestMessage,
61 | ("flags", rw.number(1)), # flags:1
62 | ("ttl", rw.number(4)), # ttl:4
63 | ("tracing", common.tracing_rw), # tracing:24
64 | # traceflags: 1
65 | ("service", rw.len_prefixed_string(rw.number(1))), # service~1
66 | ("headers", rw.headers( # nh:1 (hk~1 hv~1){nh}
67 | rw.number(1),
68 | rw.len_prefixed_string(rw.number(1))
69 | )),
70 | ("checksum", common.checksum_rw), # csumtype:1 (csum:4){0, 1}
71 | ("args",
72 | rw.args(rw.number(2))), # [arg1~2, arg2~2, arg3~2]
73 | )
74 |
--------------------------------------------------------------------------------
/tchannel/messages/call_request_continue.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 |
23 | from . import common
24 | from .. import rw
25 | from .call_continue import CallContinueMessage
26 | from .types import Types
27 |
28 |
29 | class CallRequestContinueMessage(CallContinueMessage):
30 | """Represent a continuation of a call request (across multiple frames)."""
31 | message_type = Types.CALL_REQ_CONTINUE
32 |
33 | def __init__(
34 | self,
35 | flags=0,
36 | checksum=None,
37 | args=None,
38 | id=0,
39 | ):
40 | super(CallRequestContinueMessage, self).__init__(
41 | flags, checksum, args, id)
42 |
43 | def fragment(self, space_left):
44 | fragment_msg = CallRequestContinueMessage(
45 | flags=self.flags,
46 | checksum=self.checksum,
47 | )
48 | return super(CallRequestContinueMessage, self).\
49 | fragment(space_left, fragment_msg)
50 |
51 |
52 | call_req_c_rw = rw.instance(
53 | CallRequestContinueMessage,
54 | ("flags", rw.number(1)), # flags:1
55 | ("checksum", common.checksum_rw), # csumtype:1 (csum:4){0, 1}
56 | ("args", rw.args(rw.number(2))), # [arg1~2, arg2~2, arg3~2]
57 | )
58 |
--------------------------------------------------------------------------------
/tchannel/messages/call_response.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 |
23 | from . import common
24 | from .. import rw
25 | from .call_response_continue import CallResponseContinueMessage
26 | from .types import Types
27 |
28 |
29 | class CallResponseMessage(CallResponseContinueMessage):
30 | """Respond to an RPC call."""
31 | message_type = Types.CALL_RES
32 |
33 | __slots__ = CallResponseContinueMessage.__slots__ + (
34 | 'code',
35 | 'tracing',
36 | 'headers',
37 | )
38 |
39 | def __init__(
40 | self,
41 | flags=0,
42 | code=0,
43 | tracing=None,
44 | headers=None,
45 | checksum=None,
46 | args=None,
47 | id=0,
48 | ):
49 | args = args or [b"", b"", b""]
50 | super(CallResponseMessage, self).__init__(flags, checksum, args, id)
51 | self.code = code
52 | self.tracing = tracing or common.Tracing(0, 0, 0, 0)
53 | self.headers = dict(headers) if headers else {}
54 |
55 |
56 | call_res_rw = rw.instance(
57 | CallResponseMessage,
58 | ("flags", rw.number(1)), # flags:1
59 | ("code", rw.number(1)), # code:1
60 | ("tracing", common.tracing_rw), # tracing:24
61 | # traceflags: 1
62 | ("headers", rw.headers( # nh:1 (hk~1 hv~1){nh}
63 | rw.number(1),
64 | rw.len_prefixed_string(rw.number(1))
65 | )),
66 | ("checksum", common.checksum_rw), # csumtype:1 (csum:4){0, 1}
67 | ("args",
68 | rw.args(rw.number(2))), # [arg1~2, arg2~2, arg3~2]
69 | )
70 |
--------------------------------------------------------------------------------
/tchannel/messages/call_response_continue.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 |
23 | from . import common
24 | from .. import rw
25 | from .call_continue import CallContinueMessage
26 | from .types import Types
27 |
28 |
29 | class CallResponseContinueMessage(CallContinueMessage):
30 | """Represent a continuation of a call response (across multiple frames)."""
31 | message_type = Types.CALL_RES_CONTINUE
32 |
33 | def __init__(
34 | self,
35 | flags=0,
36 | checksum=None,
37 | args=None,
38 | id=0,
39 | ):
40 | super(CallResponseContinueMessage, self).__init__(
41 | flags, checksum, args, id)
42 |
43 | def fragment(self, space_left):
44 | fragment_msg = CallResponseContinueMessage(
45 | flags=self.flags,
46 | checksum=self.checksum,
47 | )
48 | return super(CallResponseContinueMessage, self).fragment(
49 | space_left, fragment_msg)
50 |
51 | call_res_c_rw = rw.instance(
52 | CallResponseContinueMessage,
53 | ("flags", rw.number(1)), # flags:1
54 | ("checksum", common.checksum_rw), # csumtype:1 (csum:4){0, 1}
55 | ("args", rw.args(rw.number(2))), # [arg1~2, arg2~2, arg3~2]
56 | )
57 |
--------------------------------------------------------------------------------
/tchannel/messages/cancel.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 |
23 | from . import common
24 | from .. import rw
25 | from ..glossary import DEFAULT_TIMEOUT
26 | from .base import BaseMessage
27 |
28 |
29 | class CancelMessage(BaseMessage):
30 | __slots__ = BaseMessage.__slots__ + (
31 | 'ttl',
32 | 'tracing',
33 | 'why',
34 | )
35 |
36 | def __init__(self, ttl=DEFAULT_TIMEOUT, tracing=None, why=None, id=0):
37 | super(CancelMessage, self).__init__(id)
38 | self.ttl = ttl
39 | self.tracing = tracing or common.Tracing(0, 0, 0, 0)
40 | self.why = why or ''
41 |
42 |
43 | cancel_rw = rw.instance(
44 | CancelMessage,
45 | ('ttl', rw.number(4)), # ttl:4
46 | ('tracing', common.tracing_rw), # tracing:24
47 | ('why', rw.len_prefixed_string(rw.number(2))), # why:2
48 | )
49 |
--------------------------------------------------------------------------------
/tchannel/messages/claim.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 |
23 | from . import common
24 | from .. import rw
25 | from ..glossary import DEFAULT_TIMEOUT
26 | from .base import BaseMessage
27 |
28 |
29 | class ClaimMessage(BaseMessage):
30 | __slots__ = BaseMessage.__slots__ + (
31 | 'ttl',
32 | 'tracing',
33 | )
34 |
35 | def __init__(self, ttl=DEFAULT_TIMEOUT, tracing=None, id=0):
36 | super(ClaimMessage, self).__init__(id)
37 | self.ttl = ttl
38 | self.tracing = tracing or common.Tracing(0, 0, 0, 0)
39 |
40 |
41 | claim_rw = rw.instance(
42 | ClaimMessage,
43 | ('ttl', rw.number(4)), # ttl:4
44 | ('tracing', common.tracing_rw), # tracing:24
45 | )
46 |
--------------------------------------------------------------------------------
/tchannel/messages/error.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 |
23 | from . import common
24 | from .. import rw
25 | from ..enum import enum
26 | from .base import BaseMessage
27 | from .types import Types
28 |
29 | ErrorCode = enum(
30 | 'ErrorCode',
31 | timeout=0x01,
32 | cancelled=0x02,
33 | busy=0x03,
34 | declined=0x04,
35 | unexpected=0x05,
36 | bad_request=0x06,
37 | network_error=0x07,
38 | unhealthy=0x08,
39 | fatal=0xff,
40 | )
41 |
42 | error_code_rw = rw.number(1)
43 |
44 |
45 | class ErrorMessage(BaseMessage):
46 | """Respond to a CALL_REQ with a failure at the protocol level."""
47 | message_type = Types.ERROR
48 |
49 | __slots__ = BaseMessage.__slots__ + (
50 | 'code',
51 | 'tracing',
52 | 'description',
53 | )
54 |
55 | ERROR_CODES = {
56 | ErrorCode.timeout: 'timeout',
57 | ErrorCode.cancelled: 'cancelled',
58 | ErrorCode.busy: 'busy',
59 | ErrorCode.declined: 'declined',
60 | ErrorCode.unexpected: 'unexpected',
61 | ErrorCode.bad_request: 'bad request',
62 | ErrorCode.network_error: 'network error',
63 | ErrorCode.unhealthy: 'unhealthy error',
64 | ErrorCode.fatal: 'fatal protocol error'
65 | }
66 |
67 | def __init__(self, code=None, tracing=None, description=None, id=0):
68 | super(ErrorMessage, self).__init__(id)
69 | self.code = code if code else ErrorCode.unexpected
70 | self.description = description or ''
71 | self.tracing = tracing or common.Tracing(0, 0, 0, 0)
72 |
73 | def error_name(self):
74 | """Get a friendly error message."""
75 | return self.ERROR_CODES.get(self.code)
76 |
77 |
78 | error_rw = rw.instance(
79 | ErrorMessage,
80 | ('code', error_code_rw), # code:1
81 | ('tracing', common.tracing_rw), # tracing:24
82 | ('description', rw.len_prefixed_string(rw.number(2))) # message~2
83 | )
84 |
--------------------------------------------------------------------------------
/tchannel/messages/init_request.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 |
23 | from .. import rw
24 | from .base import BaseMessage
25 | from .common import PROTOCOL_VERSION
26 | from .types import Types
27 |
28 |
29 | class InitRequestMessage(BaseMessage):
30 | """Initialize a connection to a TChannel server."""
31 | message_type = Types.INIT_REQ
32 | HOST_PORT = 'host_port'
33 | PROCESS_NAME = 'process_name'
34 |
35 | # Micro-optimizations are the best kinds of optimizations
36 | __slots__ = BaseMessage.__slots__ + (
37 | 'version',
38 | 'headers',
39 | )
40 |
41 | def __init__(
42 | self,
43 | version=None,
44 | headers=None,
45 | id=0,
46 | ):
47 | super(InitRequestMessage, self).__init__(id)
48 | self.version = version or PROTOCOL_VERSION
49 | self.headers = dict(headers) if headers else {}
50 |
51 | @property
52 | def host_port(self):
53 | return self.headers.get(self.HOST_PORT)
54 |
55 | @host_port.setter
56 | def host_port(self, value):
57 | self.headers[self.HOST_PORT] = value
58 |
59 | @property
60 | def process_name(self):
61 | return self.headers.get(self.PROCESS_NAME)
62 |
63 | @process_name.setter
64 | def process_name(self, value):
65 | self.headers[self.PROCESS_NAME] = value
66 |
67 |
68 | init_req_rw = rw.instance(
69 | InitRequestMessage,
70 | ('version', rw.number(2)), # version:2
71 | ('headers', rw.headers( # nh:2 (key~2 value~2){nh}
72 | rw.number(2),
73 | rw.len_prefixed_string(rw.number(2)),
74 | rw.len_prefixed_string(rw.number(2)),
75 | )),
76 | )
77 |
--------------------------------------------------------------------------------
/tchannel/messages/init_response.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 |
23 | from .. import rw
24 | from .init_request import InitRequestMessage
25 | from .types import Types
26 |
27 |
28 | class InitResponseMessage(InitRequestMessage):
29 | """Respond to an initialization request message."""
30 | message_type = Types.INIT_RES
31 |
32 |
33 | init_res_rw = rw.instance(
34 | InitResponseMessage,
35 | ('version', rw.number(2)), # version:2
36 | ('headers', rw.headers( # nh:2 (key~2 value~2){nh}
37 | rw.number(2),
38 | rw.len_prefixed_string(rw.number(2)),
39 | rw.len_prefixed_string(rw.number(2)),
40 | )),
41 | )
42 |
--------------------------------------------------------------------------------
/tchannel/messages/ping_request.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 |
23 | from .. import rw
24 | from .base import BaseMessage
25 | from .types import Types
26 |
27 |
28 | class PingRequestMessage(BaseMessage):
29 | """Initiate a ping request."""
30 | message_type = Types.PING_REQ
31 |
32 |
33 | ping_req_rw = rw.instance(PingRequestMessage) # no body
34 |
--------------------------------------------------------------------------------
/tchannel/messages/ping_response.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 |
23 | from .. import rw
24 | from .base import BaseMessage
25 | from .types import Types
26 |
27 |
28 | class PingResponseMessage(BaseMessage):
29 | """Respond to a ping request."""
30 | message_type = Types.PING_RES
31 |
32 |
33 | ping_res_rw = rw.instance(PingResponseMessage) # no body
34 |
--------------------------------------------------------------------------------
/tchannel/messages/types.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | """TChannel type definitions."""
22 |
23 |
24 | class Types(object):
25 | INIT_REQ = 0x01
26 | INIT_RES = 0x02
27 |
28 | CALL_REQ = 0x03
29 | CALL_RES = 0x04
30 |
31 | CALL_REQ_CONTINUE = 0x13
32 | CALL_RES_CONTINUE = 0x14
33 |
34 | CANCEL = 0xc0
35 | CLAIM = 0xc1
36 |
37 | PING_REQ = 0xd0
38 | PING_RES = 0xd1
39 |
40 | ERROR = 0xff
41 |
--------------------------------------------------------------------------------
/tchannel/net.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 |
23 | import fcntl
24 | import socket
25 | import struct
26 |
27 |
28 | # TODO This module is unix-only. Figure out something for Windows.
29 |
30 |
31 | def interface_ip(interface):
32 | """Determine the IP assigned to us by the given network interface."""
33 | sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
34 | return socket.inet_ntoa(
35 | fcntl.ioctl(
36 | sock.fileno(), 0x8915,
37 | struct.pack('256s', interface[:15].encode('utf-8'))
38 | )[20:24]
39 | )
40 | # Explanation:
41 | # http://stackoverflow.com/questions/11735821/python-get-localhost-ip
42 | # http://stackoverflow.com/questions/24196932/how-can-i-get-the-ip-address-of-eth0-in-python
43 |
44 |
45 | def local_ip():
46 | """Get the local network IP of this machine"""
47 | ip = '127.0.0.1'
48 | try:
49 | ip = socket.gethostbyname(socket.gethostname())
50 | except socket.gaierror:
51 | try:
52 | ip = socket.gethostbyname(socket.gethostname() + '.local')
53 | except socket.gaierror:
54 | pass
55 | if ip.startswith('127.'):
56 | # Check eth0, eth1, eth2, en0, ...
57 | interfaces = [
58 | i + str(n) for i in ("eth", "en", "wlan") for n in range(3)
59 | ] # :(
60 | for interface in interfaces:
61 | try:
62 | ip = interface_ip(interface)
63 | break
64 | except IOError:
65 | pass
66 | return ip
67 |
--------------------------------------------------------------------------------
/tchannel/peer_strategy.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 |
23 | import sys
24 |
25 |
26 | class RankCalculator(object):
27 | """RankCalculator calculates the rank of a peer."""
28 |
29 | def get_rank(self, peer):
30 | raise NotImplementedError()
31 |
32 |
33 | class PreferIncomingCalculator(RankCalculator):
34 |
35 | # TIERS lists three ranges for three different kinds of peers.
36 | # 0: ephemeral peers or unconnected peers
37 | # 1: peers with only outgoing connections
38 | # 2: peers with incoming connections
39 | TIERS = [sys.maxsize, sys.maxsize / 2, 0]
40 |
41 | def get_rank(self, peer):
42 | """Calculate the peer rank based on connections.
43 |
44 | If the peer has no incoming connections, it will have largest rank.
45 | In our peer selection strategy, the largest number has least priority
46 | in the heap.
47 |
48 | If the peer has incoming connections, we will return number of outbound
49 | pending requests and responses.
50 |
51 | :param peer: instance of `tchannel.tornado.peer.Peer`
52 | :return: rank of the peer
53 | """
54 | if not peer.connections:
55 | return self.TIERS[0]
56 |
57 | if not peer.has_incoming_connections:
58 | return self.TIERS[1] + peer.total_outbound_pendings
59 |
60 | return self.TIERS[2] + peer.total_outbound_pendings
61 |
--------------------------------------------------------------------------------
/tchannel/retry.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import (
22 | absolute_import, division, print_function, unicode_literals
23 | )
24 |
25 | #: Retry the request on failures to connect to a remote host. This is the
26 | #: default retry behavior.
27 | CONNECTION_ERROR = 'c'
28 |
29 | #: Never retry the request.
30 | NEVER = 'n'
31 |
32 | #: Retry the request on timeouts waiting for a response.
33 | TIMEOUT = 't'
34 |
35 | #: Retry the request on failures to connect and timeouts after connecting.
36 | CONNECTION_ERROR_AND_TIMEOUT = 'ct'
37 |
38 | DEFAULT = CONNECTION_ERROR
39 |
40 | #: The default number of times to retry a request. This is in addition to the
41 | #: original request.
42 | DEFAULT_RETRY_LIMIT = 4
43 |
--------------------------------------------------------------------------------
/tchannel/schemes/__init__.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import (
22 | absolute_import, division, print_function, unicode_literals
23 | )
24 |
25 | RAW = 'raw'
26 | JSON = 'json'
27 | THRIFT = 'thrift'
28 | DEFAULT = RAW
29 |
30 | DEFAULT_NAMES = (
31 | RAW,
32 | JSON,
33 | THRIFT
34 | )
35 |
36 | from .raw import RawArgScheme # noqa
37 | from .json import JsonArgScheme # noqa
38 | from .thrift import ThriftArgScheme # noqa
39 |
40 | DEFAULT_SCHEMES = (
41 | RawArgScheme,
42 | JsonArgScheme,
43 | ThriftArgScheme
44 | )
45 |
46 | # TODO move constants to schemes/glossary and import here
47 |
--------------------------------------------------------------------------------
/tchannel/serializer/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/uber/tchannel-python/248741146cb25f32fc3a1a3fabfb13d1d6b62355/tchannel/serializer/__init__.py
--------------------------------------------------------------------------------
/tchannel/serializer/json.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 |
23 | import json
24 |
25 | from tchannel.schemes import JSON
26 | import six
27 |
28 |
29 | class JsonSerializer(object):
30 | name = JSON
31 |
32 | def serialize_header(self, headers):
33 | headers = headers or {}
34 |
35 | for k, v in six.iteritems(headers):
36 | if not (isinstance(k, six.string_types) and
37 | isinstance(v, six.string_types)):
38 | raise ValueError(
39 | 'headers must be a map[string]string (a shallow dict '
40 | 'where keys and values are strings)'
41 | )
42 |
43 | return json.dumps(headers)
44 |
45 | def deserialize_header(self, headers):
46 | if not headers:
47 | return {}
48 | if six.PY3 and isinstance(headers, bytes):
49 | headers = headers.decode('utf8')
50 | return json.loads(headers)
51 |
52 | def deserialize_body(self, obj):
53 | if six.PY3 and isinstance(obj, bytes):
54 | obj = obj.decode('utf8')
55 | if not obj:
56 | return {}
57 | return json.loads(obj)
58 |
59 | def serialize_body(self, obj):
60 | if six.PY3 and isinstance(obj, bytes):
61 | obj = obj.decode('utf8')
62 | return json.dumps(obj)
63 |
--------------------------------------------------------------------------------
/tchannel/serializer/raw.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 |
23 | from tchannel.schemes import RAW
24 |
25 |
26 | class RawSerializer(object):
27 | name = RAW
28 |
29 | def deserialize_header(self, obj):
30 | return obj
31 |
32 | def serialize_header(self, obj):
33 | return obj
34 |
35 | def deserialize_body(self, obj):
36 | return obj
37 |
38 | def serialize_body(self, obj):
39 | return obj
40 |
--------------------------------------------------------------------------------
/tchannel/singleton.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import (
22 | absolute_import, division, print_function, unicode_literals
23 | )
24 |
25 | from threading import local
26 |
27 | from . import TChannel as AsyncTChannel
28 | from .errors import SingletonNotPreparedError
29 |
30 |
31 | class TChannel(object):
32 | """Maintain a single TChannel instance per-thread."""
33 |
34 | tchannel_cls = AsyncTChannel
35 |
36 | local = local()
37 | local.tchannel = None
38 |
39 | prepared = False
40 | args = None
41 | kwargs = None
42 |
43 | @classmethod
44 | def prepare(cls, *args, **kwargs):
45 | """Set arguments to be used when instantiating a TChannel instance.
46 |
47 | Arguments are the same as :py:meth:`tchannel.TChannel.__init__`.
48 | """
49 | cls.args = args
50 | cls.kwargs = kwargs
51 | cls.prepared = True
52 |
53 | @classmethod
54 | def reset(cls, *args, **kwargs):
55 | """Undo call to prepare, useful for testing."""
56 | cls.local.tchannel = None
57 | cls.args = None
58 | cls.kwargs = None
59 | cls.prepared = False
60 |
61 | @classmethod
62 | def get_instance(cls):
63 | """Get a configured, thread-safe, singleton TChannel instance.
64 |
65 | :returns tchannel.TChannel:
66 | """
67 | if not cls.prepared:
68 | raise SingletonNotPreparedError(
69 | "prepare must be called before get_instance"
70 | )
71 |
72 | if hasattr(cls.local, 'tchannel') and cls.local.tchannel is not None:
73 | return cls.local.tchannel
74 |
75 | cls.local.tchannel = cls.tchannel_cls(*cls.args, **cls.kwargs)
76 |
77 | return cls.local.tchannel
78 |
--------------------------------------------------------------------------------
/tchannel/status.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import (
22 | absolute_import, division, print_function, unicode_literals
23 | )
24 |
25 | #: The request succeeded.
26 | OK = 0
27 |
28 | #: The request failed with an application error.
29 | FAILED = 1
30 |
--------------------------------------------------------------------------------
/tchannel/sync/__init__.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 |
23 | from .client import TChannel # noqa
24 |
--------------------------------------------------------------------------------
/tchannel/sync/singleton.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import (
22 | absolute_import, division, print_function, unicode_literals
23 | )
24 |
25 | from threading import local
26 |
27 | from tchannel.singleton import TChannel as TChannelSingleton
28 | from .client import TChannel as SyncTChannel
29 |
30 |
31 | class TChannel(TChannelSingleton):
32 |
33 | tchannel_cls = SyncTChannel
34 |
35 | local = local()
36 | local.tchannel = None
37 |
38 | prepared = False
39 | args = None
40 | kwargs = None
41 |
42 | @classmethod
43 | def get_instance(cls):
44 | """Get a configured, thread-safe, singleton TChannel instance.
45 |
46 | :returns: tchannel.sync.TChannel
47 | """
48 | return super(TChannel, cls).get_instance()
49 |
--------------------------------------------------------------------------------
/tchannel/testing/__init__.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | __all__ = ['vcr']
22 |
--------------------------------------------------------------------------------
/tchannel/testing/vcr/__init__.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | """
22 | VCR
23 | ===
24 |
25 | ``tchannel.testing.vcr`` provides VCR-like functionality for TChannel. Its
26 | API is heavily inspired by the `vcrpy `_
27 | library.
28 |
29 | This allows recording TChannel requests and their responses into YAML files
30 | during integration tests and replaying those recorded responses when the tests
31 | are run next time.
32 |
33 | The simplest way to use this is with the :py:func:`use_cassette` function.
34 |
35 | .. autofunction:: use_cassette
36 |
37 | Configuration
38 | -------------
39 |
40 | .. py:data:: tchannel.testing.vcr.DEFAULT_MATCHERS
41 |
42 | A tuple containing the default list of matchers used by
43 | :py:func:`tchannel.testing.vcr.use_cassette`.
44 |
45 | Record Modes
46 | ~~~~~~~~~~~~
47 |
48 | .. autoclass:: RecordMode
49 |
50 | """
51 |
52 | from __future__ import absolute_import
53 |
54 | from .config import use_cassette
55 | from .record_modes import RecordMode
56 | from .cassette import DEFAULT_MATCHERS
57 |
58 |
59 | __all__ = ['use_cassette', 'RecordMode', 'DEFAULT_MATCHERS']
60 |
--------------------------------------------------------------------------------
/tchannel/testing/vcr/exceptions.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 |
23 |
24 | class VCRError(Exception):
25 | "Base class for exceptions raised by the VCR library."
26 |
27 |
28 | class RequestNotFoundError(VCRError):
29 | "Raised when a request doesn't have a recorded response."
30 |
31 |
32 | class UnsupportedVersionError(VCRError):
33 | "Raised when the version of a recording is not supported."
34 |
--------------------------------------------------------------------------------
/tchannel/testing/vcr/proxy.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 | import os
23 | import sys
24 |
25 | from tchannel import thrift
26 |
27 | base = os.path.dirname(__file__)
28 |
29 | proxy = thrift.load(
30 | path=os.path.join(base, 'proxy.thrift'),
31 | service='proxy-server',
32 | )
33 |
34 | sys.modules[__name__] = proxy
35 |
--------------------------------------------------------------------------------
/tchannel/testing/vcr/proxy.thrift:
--------------------------------------------------------------------------------
1 | typedef string UUID;
2 |
3 | enum StatusCode {
4 | SUCCESS = 0
5 | FAILURE = 1
6 | }
7 |
8 | enum ArgScheme {
9 | RAW
10 | JSON
11 | THRIFT
12 | }
13 |
14 | struct TransportHeader {
15 | 1: required binary key
16 | 2: required binary value
17 | }
18 |
19 | struct Request {
20 | 1: required string serviceName
21 | 2: required string endpoint
22 | 3: optional binary headers = ""
23 | 4: required binary body
24 |
25 | /**
26 | * Remote hostport to which the request will be sent.
27 | *
28 | * If specified, the request will be made to this host only.
29 | */
30 | 5: optional binary hostPort = ""
31 | 6: optional ArgScheme argScheme = ArgScheme.RAW
32 | 7: optional list transportHeaders = []
33 |
34 | /**
35 | * List of known peers of the TChannel at the time the request was made.
36 | *
37 | * This MUST be specified if hostPort wasn't. The request will be sent to
38 | * a random peer from the list.
39 | */
40 | 8: optional list knownPeers = []
41 | // TODO: retry flags
42 | // TODO: timeout
43 | // TODO: tracing information
44 | }
45 |
46 | struct Response {
47 | 1: required StatusCode code
48 | 2: optional binary headers = ""
49 | 3: required binary body
50 | }
51 |
52 | /**
53 | * Raised when the record mode for a cassette prevents recording new
54 | * interactions for it.
55 | */
56 | exception CannotRecordInteractionsError {
57 | 1: optional string message
58 | }
59 |
60 | /**
61 | * Raised when the remote service throws a protocol error.
62 | */
63 | exception RemoteServiceError {
64 | 1: required byte code
65 | 2: required string message
66 | 3: optional string traceback
67 | }
68 |
69 | /**
70 | * Raised when both, hostPort and knownPeers were empty so the system couldn't
71 | * make a request.
72 | */
73 | exception NoPeersAvailableError {
74 | 1: required string message
75 | }
76 |
77 | /**
78 | * A generic error for VCR exceptions not covered elsewhere.
79 | */
80 | exception VCRServiceError {
81 | 1: optional string message
82 | }
83 |
84 |
85 | /**
86 | * The VCRProxy service is responsible for forwarding requests to the remote
87 | * server, recording the interactions, and replaying responses for known
88 | * requests.
89 | */
90 | service VCRProxy {
91 | /**
92 | * Send the given request through the system.
93 | *
94 | * If the request is known, replay its response. Otherwise, forward it to
95 | * the remote server and return the remote server's response.
96 | */
97 | Response send(
98 | 1: Request request,
99 | ) throws (
100 | /**
101 | * Thrown if the request was unrecognized and the record mode for the
102 | * current cassette disallows recording new interactions.
103 | */
104 | 1: CannotRecordInteractionsError cannotRecord,
105 | 2: RemoteServiceError remoteServiceError,
106 | 3: VCRServiceError serviceError,
107 | 4: NoPeersAvailableError noPeersAvailable,
108 | );
109 | }
110 |
--------------------------------------------------------------------------------
/tchannel/testing/vcr/yaml.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 |
23 | import yaml
24 |
25 |
26 | def load(s):
27 | return yaml.load(s)
28 |
29 |
30 | def dump(d):
31 | return yaml.dump(d, default_flow_style=False)
32 |
33 |
34 | __all__ = ['load', 'dump']
35 |
--------------------------------------------------------------------------------
/tchannel/thrift/__init__.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 |
23 | from .client import client_for # noqa
24 | from .module import thrift_request_builder # noqa
25 | from .server import register # noqa
26 | from .rw import load # noqa
27 |
--------------------------------------------------------------------------------
/tchannel/thrift/reflection.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 |
23 | import six
24 | import inspect
25 |
26 |
27 | def get_service_methods(iface):
28 | """Get a list of methods defined in the interface for a Thrift service.
29 |
30 | :param iface:
31 | The Thrift-generated Iface class defining the interface for the
32 | service.
33 | :returns:
34 | A set containing names of the methods defined for the service.
35 | """
36 |
37 | if six.PY3:
38 | methods = [func
39 | for func in dir(iface)
40 | if callable(getattr(iface,
41 | func)) and not func.startswith("__")]
42 | return set(methods)
43 |
44 | methods = inspect.getmembers(iface, predicate=inspect.ismethod)
45 |
46 | return set(
47 | name for (name, method) in methods if not name.startswith('__')
48 | )
49 |
50 |
51 | def get_module_name(module):
52 | name = module.__name__.rsplit('.', 1)[-1]
53 |
54 | return name
55 |
--------------------------------------------------------------------------------
/tchannel/tornado/__init__.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 |
23 | from .tchannel import TChannel # noqa
24 | from .dispatch import RequestDispatcher # noqa
25 | from .request import Request # noqa
26 | from .response import Response # noqa
27 |
--------------------------------------------------------------------------------
/tchannel/tornado/util.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 | import tornado
23 | import tornado.gen
24 |
25 | from ..errors import TChannelError
26 |
27 |
28 | @tornado.gen.coroutine
29 | def get_arg(context, index):
30 | """get value from arg stream in async way"""
31 | if index < len(context.argstreams):
32 | arg = b""
33 | chunk = yield context.argstreams[index].read()
34 | while chunk:
35 | arg += chunk
36 | chunk = yield context.argstreams[index].read()
37 |
38 | raise tornado.gen.Return(arg)
39 | else:
40 | raise TChannelError()
41 |
--------------------------------------------------------------------------------
/tchannel/transport.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import (
22 | absolute_import, division, print_function, unicode_literals
23 | )
24 |
25 | CALLER_NAME = "cn"
26 | CLAIM_AT_START = "cas"
27 | CLAIM_AT_FINISH = "caf"
28 | FAILURE_DOMAIN = "fd"
29 | RETRY_FLAGS = "re"
30 | ROUTING_DELEGATE = "rd"
31 | SCHEME = "as"
32 | SHARD_KEY = "sk"
33 | SPECULATIVE_EXE = "se"
34 |
--------------------------------------------------------------------------------
/tchannel/zipkin/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/uber/tchannel-python/248741146cb25f32fc3a1a3fabfb13d1d6b62355/tchannel/zipkin/__init__.py
--------------------------------------------------------------------------------
/tchannel/zipkin/zipkin_trace.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 |
23 | from tchannel.event import EventHook
24 |
25 |
26 | class ZipkinTraceHook(EventHook):
27 | """
28 | .. deprecated:: 0.27.0
29 | Deprecated no-op hook kept only for backwards compatibility.
30 | """
31 |
32 | DEFAULT_RATE = 0.01
33 |
34 | def __init__(self, tchannel=None, dst=None, sample_rate=None):
35 | pass
36 |
37 | def before_send_request(self, request):
38 | pass
39 |
40 | def before_receive_request(self, request):
41 | pass
42 |
43 | def after_send_response(self, response):
44 | pass
45 |
46 | def after_receive_response(self, request, response):
47 | pass
48 |
49 | def after_receive_error(self, request, error):
50 | pass
51 |
52 | def after_send_error(self, error):
53 | pass
54 |
--------------------------------------------------------------------------------
/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/uber/tchannel-python/248741146cb25f32fc3a1a3fabfb13d1d6b62355/tests/__init__.py
--------------------------------------------------------------------------------
/tests/conftest.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 |
23 | import pytest
24 | import threadloop
25 |
26 | from .mock_server import MockServer
27 | from .util import get_thrift_service_module
28 |
29 |
30 | class _MockConnection(object):
31 | def __init__(self):
32 | self.buff = bytearray()
33 | self.remote_host = "0.0.0.0"
34 | self.remote_host_port = "0"
35 | self.closed = False
36 |
37 | def write(self, payload, callback=None):
38 | self.buff.extend(payload)
39 |
40 | def getvalue(self):
41 | return self.buff
42 |
43 | def set_outbound_pending_change_callback(self, cb):
44 | pass
45 |
46 | def set_close_callback(self, cb):
47 | pass
48 |
49 |
50 | @pytest.yield_fixture(autouse=True)
51 | def _reduce_ad_jitter():
52 | from tchannel.tornado import hyperbahn
53 | # For all tests, reduce jitter to 0.5 seconds so they don't take too long
54 | # to run.
55 | original = hyperbahn.DEFAULT_INTERVAL_MAX_JITTER_SECS
56 | try:
57 | hyperbahn.DEFAULT_INTERVAL_MAX_JITTER_SECS = 0.5
58 | yield
59 | finally:
60 | hyperbahn.DEFAULT_INTERVAL_MAX_JITTER_SECS = original
61 |
62 |
63 | @pytest.fixture
64 | def connection():
65 | """Make a mock connection."""
66 | return _MockConnection()
67 |
68 |
69 | @pytest.yield_fixture
70 | def mock_server(io_loop):
71 | with MockServer() as server:
72 | yield server
73 |
74 |
75 | @pytest.yield_fixture
76 | def thrift_service(tmpdir):
77 | with get_thrift_service_module(tmpdir, True) as m:
78 | yield m
79 |
80 |
81 | @pytest.yield_fixture
82 | def loop():
83 | tl = threadloop.ThreadLoop()
84 | tl.start()
85 | yield tl
86 | tl.stop()
87 |
--------------------------------------------------------------------------------
/tests/data/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/uber/tchannel-python/248741146cb25f32fc3a1a3fabfb13d1d6b62355/tests/data/__init__.py
--------------------------------------------------------------------------------
/tests/data/generated/ThriftTest/__init__.py:
--------------------------------------------------------------------------------
1 | __all__ = ['ttypes', 'constants', 'ThriftTest', 'SecondService']
2 |
--------------------------------------------------------------------------------
/tests/data/generated/ThriftTest/constants.py:
--------------------------------------------------------------------------------
1 | #
2 | # Autogenerated by Thrift Compiler (0.12.0)
3 | #
4 | # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
5 | #
6 | # options string: py:new_style,slots,dynamic
7 | #
8 |
9 | from thrift.Thrift import TType, TMessageType, TFrozenDict, TException, TApplicationException
10 | from thrift.protocol.TProtocol import TProtocolException
11 | from thrift.TRecursive import fix_spec
12 |
13 | import sys
14 | from .ttypes import *
15 | myNumberz = 1
16 |
--------------------------------------------------------------------------------
/tests/data/generated/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/uber/tchannel-python/248741146cb25f32fc3a1a3fabfb13d1d6b62355/tests/data/generated/__init__.py
--------------------------------------------------------------------------------
/tests/data/hosts.json:
--------------------------------------------------------------------------------
1 | ["127.0.0.1:21300", "127.0.0.1:21301", "127.0.0.1:21302", "127.0.0.1:21303", "127.0.0.1:21304", "127.0.0.1:21305"]
--------------------------------------------------------------------------------
/tests/integration/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/uber/tchannel-python/248741146cb25f32fc3a1a3fabfb13d1d6b62355/tests/integration/__init__.py
--------------------------------------------------------------------------------
/tests/integration/json/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/uber/tchannel-python/248741146cb25f32fc3a1a3fabfb13d1d6b62355/tests/integration/json/__init__.py
--------------------------------------------------------------------------------
/tests/integration/thrift/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/uber/tchannel-python/248741146cb25f32fc3a1a3fabfb13d1d6b62355/tests/integration/thrift/__init__.py
--------------------------------------------------------------------------------
/tests/integration/tornado/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/uber/tchannel-python/248741146cb25f32fc3a1a3fabfb13d1d6b62355/tests/integration/tornado/__init__.py
--------------------------------------------------------------------------------
/tests/messages/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/uber/tchannel-python/248741146cb25f32fc3a1a3fabfb13d1d6b62355/tests/messages/__init__.py
--------------------------------------------------------------------------------
/tests/messages/test_common.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 |
23 | import random
24 |
25 | import pytest
26 |
27 | from tchannel.io import BytesIO
28 | from tchannel.messages.common import ChecksumType
29 | from tchannel.messages.common import Tracing
30 | from tchannel.messages.common import checksum_rw
31 | from tchannel.messages.common import tracing_rw
32 | from six.moves import range
33 |
34 |
35 | @pytest.mark.parametrize('typ, value', [
36 | (ChecksumType.none, None),
37 | (ChecksumType.crc32, 1234),
38 | (ChecksumType.farm32, 5678),
39 | ])
40 | def test_chucksum_round_trip(typ, value):
41 | buff = checksum_rw.write((typ, value), BytesIO()).getvalue()
42 | assert (typ, value) == checksum_rw.read(BytesIO(buff))
43 |
44 |
45 | @pytest.mark.parametrize('bs, typ, value', [
46 | ([0], ChecksumType.none, None),
47 | ([1, 1, 2, 3, 4], ChecksumType.crc32, 16909060),
48 | ([2, 1, 2, 3, 4], ChecksumType.farm32, 16909060),
49 | ])
50 | def test_checksum_read(bs, typ, value):
51 | assert checksum_rw.read(BytesIO(bytearray(bs))) == (typ, value)
52 |
53 |
54 | def test_tracing_round_trip():
55 | for i in range(100):
56 | t = Tracing(
57 | random.randint(0, 100000),
58 | random.randint(0, 100000),
59 | random.randint(0, 100000),
60 | random.randint(0, 1),
61 | )
62 |
63 | buff = tracing_rw.write(t, BytesIO()).getvalue()
64 | assert t == tracing_rw.read(BytesIO(buff))
65 |
66 |
67 | @pytest.mark.parametrize('tracing, bs', [
68 | (Tracing(1, 2, 3, 0), [
69 | 0, 0, 0, 0, 0, 0, 0, 1, # span_id:8
70 | 0, 0, 0, 0, 0, 0, 0, 2, # parent_id:8
71 | 0, 0, 0, 0, 0, 0, 0, 3, # trace_id:8
72 | 0, # traceflags:1
73 | ])
74 | ])
75 | def test_tracing_read(tracing, bs):
76 | assert tracing_rw.read(BytesIO(bytearray(bs))) == tracing
77 |
--------------------------------------------------------------------------------
/tests/schemes/test_raw.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import (
22 | absolute_import, division, print_function, unicode_literals
23 | )
24 |
25 | import pytest
26 |
27 | from tchannel import TChannel, Request, Response, schemes
28 | from tchannel.response import TransportHeaders
29 |
30 |
31 | @pytest.mark.gen_test
32 | @pytest.mark.call
33 | def test_call_should_get_response():
34 |
35 | # Given this test server:
36 |
37 | server = TChannel(name='server')
38 |
39 | @server.raw.register
40 | def endpoint(request):
41 |
42 | assert isinstance(request, Request)
43 | assert request.headers == b'req headers'
44 | assert request.body == b'req body'
45 |
46 | return Response('resp body', headers='resp headers')
47 |
48 | server.listen()
49 |
50 | # Make a call:
51 |
52 | tchannel = TChannel(name='client')
53 |
54 | resp = yield tchannel.raw(
55 | service='server',
56 | endpoint='endpoint',
57 | headers='req headers',
58 | body='req body',
59 | hostport=server.hostport,
60 | )
61 |
62 | # verify response
63 | assert isinstance(resp, Response)
64 | assert resp.headers == b'resp headers'
65 | assert resp.body == b'resp body'
66 |
67 | # verify response transport headers
68 | assert isinstance(resp.transport, TransportHeaders)
69 | assert resp.transport.scheme == schemes.RAW
70 | assert resp.transport.failure_domain is None
71 |
72 |
73 | @pytest.mark.gen_test
74 | @pytest.mark.call
75 | def test_register_should_work_with_different_endpoint():
76 |
77 | # Given this test server:
78 |
79 | server = TChannel(name='server')
80 |
81 | @server.raw.register('foo')
82 | def endpoint(request):
83 | return 'resp body'
84 |
85 | server.listen()
86 |
87 | # Make a call:
88 |
89 | tchannel = TChannel(name='client')
90 |
91 | resp = yield tchannel.raw(
92 | service='server',
93 | endpoint='foo',
94 | hostport=server.hostport,
95 | )
96 |
97 | assert resp.body == b'resp body'
98 |
--------------------------------------------------------------------------------
/tests/serializer/test_serializer_json.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 | import pytest
23 |
24 | from tchannel.serializer.json import JsonSerializer
25 |
26 |
27 | @pytest.mark.parametrize('v1', [
28 | ({}),
29 | ({'a': 'd'}),
30 | ])
31 | def test_header(v1):
32 | serializer = JsonSerializer()
33 | assert v1 == serializer.deserialize_header(
34 | serializer.serialize_header(v1)
35 | )
36 |
37 |
38 | @pytest.mark.parametrize('v1, v2', [
39 | (True, 'true'),
40 | (False, 'false'),
41 | ({}, '{}'),
42 | ({'a': 'd'}, '{"a": "d"}'),
43 | (2, '2'),
44 | (None, 'null'),
45 | ])
46 | def test_body(v1, v2):
47 | serializer = JsonSerializer()
48 | assert v2 == serializer.serialize_body(v1)
49 | assert v1 == serializer.deserialize_body(v2)
50 |
51 |
52 | def test_exception():
53 | serializer = JsonSerializer()
54 |
55 | with pytest.raises(ValueError):
56 | serializer.deserialize_header('{sss')
57 |
58 | with pytest.raises(TypeError):
59 | serializer.serialize_body({"sss"})
60 |
61 | with pytest.raises(ValueError):
62 | serializer.deserialize_body('{sss')
63 |
--------------------------------------------------------------------------------
/tests/serializer/test_serializer_raw.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 |
23 | from tchannel.serializer.raw import RawSerializer
24 |
25 |
26 | def test_header():
27 | serializer = RawSerializer()
28 | obj = object()
29 | assert obj == serializer.serialize_header(obj)
30 | assert obj == serializer.deserialize_header(obj)
31 |
32 |
33 | def test_body():
34 | serializer = RawSerializer()
35 | obj = object()
36 | assert obj == serializer.serialize_body(obj)
37 | assert obj == serializer.deserialize_body(obj)
38 |
--------------------------------------------------------------------------------
/tests/serializer/test_serializer_thrift.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 | import pytest
23 | from tchannel.serializer.thrift import ThriftSerializer
24 | from tests.data.generated.ThriftTest.ThriftTest import (
25 | testStruct_result,
26 | Xtruct,
27 | )
28 |
29 |
30 | @pytest.mark.parametrize('v1', [
31 | ({}),
32 | ({'a': 'd'}),
33 | ])
34 | def test_header(v1):
35 | serializer = ThriftSerializer(None)
36 | assert v1 == serializer.deserialize_header(
37 | serializer.serialize_header(v1)
38 | )
39 |
40 |
41 | def test_body():
42 | result = testStruct_result(Xtruct("s", 0, 1, 2))
43 | serializer = ThriftSerializer(testStruct_result)
44 | assert result == serializer.deserialize_body(
45 | serializer.serialize_body(result)
46 | )
47 |
--------------------------------------------------------------------------------
/tests/sync/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/uber/tchannel-python/248741146cb25f32fc3a1a3fabfb13d1d6b62355/tests/sync/__init__.py
--------------------------------------------------------------------------------
/tests/sync/test_singleton.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import (
22 | absolute_import, division, print_function, unicode_literals
23 | )
24 |
25 | import pytest
26 |
27 | from tchannel.sync.singleton import TChannel
28 | from tchannel.sync import TChannel as SyncTChannel
29 | from tchannel.singleton import TChannel as AsyncSingleton
30 | from tchannel.errors import SingletonNotPreparedError
31 |
32 |
33 | def test_stored_seperately_from_async_singleton():
34 |
35 | TChannel.reset()
36 | AsyncSingleton.reset()
37 |
38 | AsyncSingleton.prepare('async-app')
39 |
40 | with pytest.raises(SingletonNotPreparedError):
41 | TChannel.get_instance()
42 |
43 | TChannel.prepare('sync-app')
44 |
45 | instance = TChannel.get_instance()
46 |
47 | assert isinstance(instance, SyncTChannel)
48 | assert AsyncSingleton.get_instance() is not instance
49 |
--------------------------------------------------------------------------------
/tests/sync/test_thrift.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 |
23 | import pytest
24 |
25 | from tchannel import thrift_request_builder
26 | from tchannel.sync import TChannel
27 |
28 |
29 | @pytest.mark.integration
30 | def test_call(mock_server, thrift_service):
31 |
32 | expected = thrift_service.Item(
33 | key='foo', value=thrift_service.Value(integerValue=42)
34 | )
35 |
36 | mock_server.expect_call(
37 | thrift_service,
38 | 'thrift',
39 | method='getItem',
40 | ).and_result(expected)
41 |
42 | thrift_service = thrift_request_builder(
43 | service='thrift-service',
44 | thrift_module=thrift_service,
45 | hostport=mock_server.hostport,
46 | )
47 |
48 | tchannel = TChannel('test-client')
49 |
50 | future = tchannel.thrift(
51 | thrift_service.getItem('foo')
52 | )
53 | result = future.result()
54 |
55 | assert expected == result.body
56 |
--------------------------------------------------------------------------------
/tests/test_checksum.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 | import mock
23 |
24 | import pytest
25 |
26 | from tchannel import messages
27 | from tchannel import TChannel
28 | from tchannel import thrift
29 | from tchannel.errors import FatalProtocolError
30 | from tchannel.io import BytesIO
31 | from tchannel.messages import CallRequestMessage
32 | from tchannel.messages import ChecksumType
33 | from tchannel.messages.common import generate_checksum
34 | from tchannel.messages.common import verify_checksum
35 |
36 |
37 | @pytest.mark.parametrize('checksum_type', [
38 | (ChecksumType.none),
39 | (ChecksumType.crc32),
40 | (ChecksumType.crc32c),
41 | ])
42 | def test_checksum(checksum_type):
43 | message = CallRequestMessage()
44 | message.checksum = (checksum_type, None)
45 | generate_checksum(message)
46 | payload = messages.RW[message.message_type].write(
47 | message, BytesIO()
48 | ).getvalue()
49 |
50 | msg = messages.RW[message.message_type].read(BytesIO(payload))
51 | assert verify_checksum(msg)
52 |
53 |
54 | @pytest.mark.gen_test
55 | def test_default_checksum_type():
56 | server = TChannel("server")
57 | server.listen()
58 | with mock.patch(
59 | 'tchannel.messages.common.compute_checksum', autospec=True,
60 | ) as mock_compute_checksum:
61 | client = TChannel("client")
62 | service = thrift.load(
63 | path='tchannel/health/meta.thrift',
64 | service='health_test_server',
65 | hostport=server.hostport,
66 | )
67 | with pytest.raises(FatalProtocolError):
68 | yield client.thrift(service.Meta.health())
69 |
70 | mock_compute_checksum.assert_called_with(
71 | ChecksumType.crc32c, mock.ANY, mock.ANY,
72 | )
73 |
--------------------------------------------------------------------------------
/tests/test_context.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 |
23 | import pytest
24 | from tchannel import context
25 | from tornado import gen
26 |
27 |
28 | @pytest.mark.gen_test
29 | def test_deprecated_context_provider():
30 | """
31 | Test that the deprecated RequestContextProvider() can still propagate
32 | the request context across coroutines.
33 | """
34 | provider = context.RequestContextProvider()
35 |
36 | @gen.coroutine
37 | def _get_context():
38 | res = provider.get_current_context().parent_tracing
39 | raise gen.Return(res)
40 |
41 | with provider.request_context(parent_tracing='Farnsworth'):
42 | res = _get_context()
43 |
44 | res = yield res
45 | assert res == 'Farnsworth'
46 |
--------------------------------------------------------------------------------
/tests/test_frame.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 |
23 | import pytest
24 |
25 | from tchannel import messages
26 | from tchannel.frame import Frame
27 | from tchannel.frame import FrameHeader
28 | from tchannel.frame import frame_rw
29 | from tchannel.io import BytesIO
30 | from tchannel.messages import PingRequestMessage
31 | from tchannel.messages.types import Types
32 |
33 |
34 | class _FakeMessage(object):
35 | message_type = 0x30
36 |
37 | def serialize(self, out):
38 | """Serialize 0-bytes to ``out``."""
39 | return
40 |
41 |
42 | @pytest.fixture
43 | def dummy_frame():
44 | return bytearray([
45 | 0, 16, # Size
46 | 0, # type
47 | 0, # reserved
48 | 0, 0, 0, 1, # ID
49 | 0, 0, 0, 0, 0, 0, 0, 0 # reserved padding
50 | ])
51 |
52 |
53 | def test_frame_header_width():
54 | assert frame_rw.width() == 16
55 |
56 |
57 | def test_empty_payload(connection):
58 | """Verify size is set properly for an empty message."""
59 |
60 | message_id = 42
61 |
62 | frame = Frame(
63 | header=FrameHeader(
64 | message_id=message_id,
65 | message_type=0x30
66 | ),
67 | payload=""
68 | )
69 |
70 | frame_rw.write(frame, connection)
71 | assert connection.getvalue() == bytearray([
72 | 0, 16, # size:2
73 | 0x30, # type:1
74 | 0, # reserved:1
75 | 0, 0, 0, 42, # id:4
76 | 0, 0, 0, 0, 0, 0, 0, 0 # padding:8
77 | ])
78 |
79 |
80 | def test_decode_empty_buffer():
81 | """Verify we can parse zero size frame."""
82 | assert frame_rw.read(BytesIO(b'\x00\x00\x00\x00')) is None
83 |
84 |
85 | def test_decode_with_message_length(dummy_frame):
86 | """Verify we can pre-flight a message size."""
87 | dummy_frame[2] = Types.PING_REQ
88 | f = frame_rw.read(
89 | BytesIO(dummy_frame[2:]), size=len(dummy_frame)
90 | )
91 | message_rw = messages.RW[f.header.message_type]
92 | message_rw.read(BytesIO(f.payload)) == PingRequestMessage()
93 |
--------------------------------------------------------------------------------
/tests/test_future.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import (
22 | absolute_import, unicode_literals, division, print_function
23 | )
24 |
25 | from tornado import gen
26 |
27 | import pytest
28 |
29 | from tchannel._future import fail_to
30 |
31 |
32 | def test_fail_to_no_failure():
33 | answer = gen.Future()
34 |
35 | @fail_to(answer)
36 | def f():
37 | return 42
38 |
39 | assert f() == 42
40 | assert answer.running()
41 |
42 |
43 | @pytest.mark.gen_test
44 | def test_fail_to_failure():
45 | answer = gen.Future()
46 |
47 | @fail_to(answer)
48 | def f():
49 | raise GreatSadness
50 |
51 | assert f() is None
52 | with pytest.raises(GreatSadness):
53 | yield answer
54 |
55 |
56 | @pytest.mark.gen_test
57 | @pytest.mark.gen_test
58 | def test_fail_to_failure_in_coroutine():
59 | answer = gen.Future()
60 |
61 | @fail_to(answer)
62 | @gen.coroutine
63 | def f():
64 | raise GreatSadness
65 |
66 | with pytest.raises(GreatSadness):
67 | yield f()
68 | assert answer.running()
69 |
70 |
71 | class GreatSadness(Exception):
72 | pass
73 |
--------------------------------------------------------------------------------
/tests/test_health.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 |
23 | import pytest
24 |
25 | from tchannel import TChannel, thrift
26 | from tchannel.health import Meta
27 | from tchannel.health import HealthStatus
28 |
29 |
30 | @pytest.mark.gen_test
31 | def test_default_health():
32 | server = TChannel("health_test_server")
33 | server.listen()
34 |
35 | client = TChannel("health_test_client")
36 |
37 | service = thrift.load(
38 | path='tchannel/health/meta.thrift',
39 | service='health_test_server',
40 | hostport=server.hostport,
41 | )
42 |
43 | resp = yield client.thrift(service.Meta.health())
44 | assert resp.body.ok is True
45 | assert resp.body.message is None
46 |
47 |
48 | @pytest.mark.gen_test
49 | def test_user_health():
50 | server = TChannel("health_test_server")
51 |
52 | @server.thrift.register(Meta, method="health")
53 | def user_health(request):
54 | return HealthStatus(ok=False, message="from me")
55 |
56 | server.listen()
57 |
58 | client = TChannel("health_test_client")
59 | service = thrift.load(
60 | path='tchannel/health/meta.thrift',
61 | service='health_test_server',
62 | hostport=server.hostport,
63 | )
64 |
65 | resp = yield client.thrift(service.Meta.health())
66 | assert resp.body.ok is False
67 | assert resp.body.message == "from me"
68 |
--------------------------------------------------------------------------------
/tests/test_peer_strategy.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 |
23 | import sys
24 |
25 | import pytest
26 | from tchannel import TChannel
27 | from tchannel.peer_strategy import PreferIncomingCalculator
28 | from tchannel.tornado.connection import TornadoConnection
29 | from tchannel.tornado.connection import INCOMING
30 | from tchannel.tornado.peer import Peer
31 |
32 |
33 | @pytest.mark.gen_test
34 | def test_get_rank_no_connection():
35 | server = TChannel('server')
36 | server.listen()
37 | peer = Peer(TChannel('test'), '10.10.101.21:230')
38 | calculator = PreferIncomingCalculator()
39 | assert sys.maxsize == calculator.get_rank(peer)
40 |
41 |
42 | @pytest.mark.gen_test
43 | def test_get_rank_with_outgoing():
44 | server = TChannel('server')
45 | server.listen()
46 | connection = yield TornadoConnection.outgoing(server.hostport)
47 |
48 | peer = Peer(TChannel('test'), '10.10.101.21:230')
49 | calculator = PreferIncomingCalculator()
50 | peer.register_outgoing_conn(connection)
51 | assert PreferIncomingCalculator.TIERS[1] == calculator.get_rank(peer)
52 |
53 |
54 | @pytest.mark.gen_test
55 | def test_get_rank_with_imcoming():
56 | server = TChannel('server')
57 | server.listen()
58 | connection = yield TornadoConnection.outgoing(server.hostport)
59 | connection.direction = INCOMING
60 | peer = Peer(TChannel('test'), '10.10.101.21:230')
61 | calculator = PreferIncomingCalculator()
62 | peer.register_incoming_conn(connection)
63 | assert sys.maxsize != calculator.get_rank(peer)
64 |
--------------------------------------------------------------------------------
/tests/test_singleton.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import (
22 | absolute_import, division, print_function, unicode_literals
23 | )
24 |
25 | import pytest
26 |
27 | from tchannel import TChannel as AsyncTChannel
28 | from tchannel.singleton import TChannel
29 | from tchannel.errors import SingletonNotPreparedError
30 |
31 |
32 | def test_get_instance_is_singleton():
33 |
34 | TChannel.reset()
35 | TChannel.prepare('my-app')
36 |
37 | assert TChannel.get_instance() is TChannel.get_instance()
38 |
39 |
40 | def test_must_call_prepare_before_get_instance():
41 |
42 | TChannel.reset()
43 |
44 | with pytest.raises(SingletonNotPreparedError):
45 | TChannel.get_instance()
46 |
47 |
48 | def test_get_instance_returns_configured_tchannel():
49 |
50 | TChannel.reset()
51 | TChannel.prepare('my-app')
52 |
53 | tchannel = TChannel.get_instance()
54 |
55 | assert isinstance(tchannel, AsyncTChannel)
56 | assert tchannel.name == 'my-app'
57 |
--------------------------------------------------------------------------------
/tests/test_stream.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 |
23 | import os
24 |
25 | import pytest
26 |
27 | from tchannel.errors import UnexpectedError
28 | from tchannel.errors import TChannelError
29 | from tchannel.tornado import Response
30 | from tchannel.tornado.stream import InMemStream
31 | from tchannel.tornado.stream import PipeStream
32 |
33 |
34 | @pytest.mark.gen_test
35 | def test_InMemStream():
36 | stream = InMemStream()
37 | yield stream.write("1")
38 | yield stream.write("2")
39 | buf = yield stream.read()
40 | assert buf == b"12"
41 |
42 | yield stream.write("3")
43 | buf = yield stream.read()
44 | assert buf == b"3"
45 |
46 | # check internal stream buffer.
47 | assert len(stream._stream) == 0
48 |
49 | stream.close()
50 | with pytest.raises(UnexpectedError):
51 | yield stream.write("4")
52 |
53 |
54 | @pytest.mark.gen_test
55 | def test_PipeStream():
56 | r, w = os.pipe()
57 | stream = PipeStream(r, w, auto_close=True)
58 | yield stream.write(b"1")
59 | yield stream.write(b"2")
60 | buf = yield stream.read()
61 | assert buf == b"12"
62 |
63 | yield stream.write(b"3")
64 | buf = yield stream.read()
65 | assert buf == b"3"
66 |
67 | stream.close()
68 | with pytest.raises(UnexpectedError):
69 | yield stream.write(b"4")
70 |
71 |
72 | @pytest.mark.gen_test
73 | def test_response_exception():
74 | resp = Response()
75 | yield resp.write_body("aaa")
76 |
77 | with pytest.raises(UnexpectedError):
78 | yield resp.write_header("aaa")
79 |
80 | resp.flush()
81 | with pytest.raises(TChannelError):
82 | yield resp.write_body("aaaa")
83 |
84 |
85 | @pytest.mark.gen_test
86 | def test_error_during_stream(io_loop):
87 | stream = InMemStream()
88 | try:
89 | 1 / 0
90 | except Exception as e:
91 | stream.set_exception(e)
92 |
93 | with pytest.raises(ZeroDivisionError):
94 | yield stream.read()
95 |
96 | with pytest.raises(ZeroDivisionError):
97 | yield stream.write("a")
98 |
--------------------------------------------------------------------------------
/tests/test_types.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 |
23 | from tchannel.messages.types import Types
24 |
25 |
26 | def test_type_sanity():
27 | """Simple check to make sure types are importable."""
28 | assert Types.INIT_REQ == 1
29 |
--------------------------------------------------------------------------------
/tests/testing/vcr/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/uber/tchannel-python/248741146cb25f32fc3a1a3fabfb13d1d6b62355/tests/testing/vcr/__init__.py
--------------------------------------------------------------------------------
/tests/testing/vcr/integration/data/old_with_tracing.yaml:
--------------------------------------------------------------------------------
1 | interactions:
2 | - request:
3 | argScheme: 1
4 | body: '"world"'
5 | endpoint: !!python/unicode 'hello'
6 | headers: '{"$tracing$uber-trace-id": "ca18ff55ca2d44ac:ca18ff55ca2d44ac:0:1"}'
7 | hostPort: localhost:54324
8 | knownPeers: []
9 | serviceName: !!python/unicode 'hello_service'
10 | transportHeaders:
11 | - key: as
12 | value: json
13 | - key: cn
14 | value: client
15 | response:
16 | body: '"world"'
17 | code: 0
18 | headers: '{}'
19 | version: 1
20 |
--------------------------------------------------------------------------------
/tests/testing/vcr/integration/data/old_without_tracing.yaml:
--------------------------------------------------------------------------------
1 | interactions:
2 | - request:
3 | argScheme: 1
4 | body: '"world"'
5 | endpoint: !!python/unicode 'hello'
6 | headers: '{}'
7 | hostPort: localhost:58493
8 | knownPeers: []
9 | serviceName: !!python/unicode 'hello_service'
10 | transportHeaders:
11 | - key: as
12 | value: json
13 | - key: cn
14 | value: client
15 | response:
16 | body: '"world"'
17 | code: 0
18 | headers: '{}'
19 | version: 1
20 |
--------------------------------------------------------------------------------
/tests/testing/vcr/strategies.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 |
23 | from hypothesis.strategies import (
24 | binary,
25 | builds,
26 | lists,
27 | sampled_from,
28 | text,
29 | )
30 |
31 | from tchannel.testing.vcr import proxy
32 |
33 | arg_schemes = sampled_from(proxy.ArgScheme.values)
34 |
35 | transport_headers = builds(
36 | proxy.TransportHeader,
37 | key=binary(),
38 | value=binary(),
39 | )
40 |
41 |
42 | requests = builds(
43 | proxy.Request,
44 | serviceName=text(),
45 | hostPort=sampled_from(('localhost', '')),
46 | endpoint=text(min_size=1),
47 | headers=binary(),
48 | body=binary(),
49 | argScheme=arg_schemes,
50 | transportHeaders=lists(transport_headers),
51 | )
52 |
53 |
54 | responses = builds(
55 | proxy.Response,
56 | code=sampled_from(proxy.StatusCode.values),
57 | headers=binary(),
58 | body=binary(),
59 | )
60 |
--------------------------------------------------------------------------------
/tests/testing/vcr/test_patcher.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 |
23 | from tchannel.tornado import TChannel
24 | from tchannel.testing.vcr.patch import Patcher
25 | from tchannel.testing.vcr.patch import PatchedClientOperation
26 |
27 |
28 | def test_patching_as_context_manager():
29 | chan = TChannel('client')
30 | with Patcher('localhost:4040'):
31 | ops = chan.request(service='foo')
32 | assert isinstance(ops, PatchedClientOperation)
33 | assert ops.vcr_hostport == 'localhost:4040'
34 |
35 |
36 | def test_patching_as_decorator():
37 | chan = TChannel('client')
38 |
39 | @Patcher('localhost:4040')
40 | def f():
41 | ops = chan.request(service='foo')
42 | assert isinstance(ops, PatchedClientOperation)
43 | assert ops.vcr_hostport == 'localhost:4040'
44 |
45 | f()
46 |
--------------------------------------------------------------------------------
/tests/thrift/test_module.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 | from __future__ import division
23 | from __future__ import print_function
24 | from __future__ import unicode_literals
25 |
26 | import inspect
27 |
28 | import pytest
29 | import six
30 |
31 | from tchannel import thrift_request_builder
32 | from tchannel.thrift.module import ThriftRequest
33 | from tchannel.thrift.module import ThriftRequestMaker
34 | from tests.data.generated.ThriftTest import ThriftTest
35 |
36 |
37 | @pytest.mark.skipif(six.PY3, reason='Deprecated')
38 | @pytest.mark.call
39 | def test_from_thrift_class_should_return_request_maker():
40 |
41 | maker = thrift_request_builder('thrift_test', ThriftTest)
42 |
43 | assert isinstance(maker, ThriftRequestMaker)
44 |
45 |
46 | @pytest.mark.skipif(six.PY3, reason='Deprecated')
47 | @pytest.mark.call
48 | def test_maker_should_have_thrift_iface_methods():
49 |
50 | maker = thrift_request_builder('thrift_test', ThriftTest)
51 |
52 | # extract list of maker methods
53 | maker_methods = [
54 | m[0] for m in
55 | inspect.getmembers(maker, predicate=inspect.ismethod)
56 | ]
57 |
58 | # extract list of iface methods
59 | iface_methods = [
60 | m[0] for m in
61 | inspect.getmembers(ThriftTest.Iface, predicate=inspect.ismethod)
62 | ]
63 |
64 | # verify all of iface_methods exist in maker_methods
65 | assert set(iface_methods) < set(maker_methods)
66 |
67 |
68 | @pytest.mark.skipif(six.PY3, reason='Deprecated')
69 | @pytest.mark.call
70 | def test_request_maker_should_return_request():
71 |
72 | maker = thrift_request_builder('thrift_test', ThriftTest)
73 |
74 | request = maker.testString('hi')
75 |
76 | assert isinstance(request, ThriftRequest)
77 | assert request.service == 'thrift_test'
78 | assert request.endpoint == 'ThriftTest::testString'
79 | assert request.result_type == ThriftTest.testString_result
80 | assert request.call_args == ThriftTest.testString_args(thing='hi')
81 |
--------------------------------------------------------------------------------
/tests/thrift/test_multiple_services.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import (
22 | absolute_import, print_function, unicode_literals, division
23 | )
24 |
25 | import pytest
26 |
27 | from tchannel import TChannel, thrift
28 |
29 |
30 | @pytest.mark.gen_test
31 | def test_inherited_method_names(tmpdir):
32 | thrift_file = tmpdir.join('service.thrift')
33 | thrift_file.write('''
34 | service Base { string hello() }
35 | service Foo extends Base {}
36 | service Bar extends Base {}
37 | ''')
38 |
39 | service = thrift.load(str(thrift_file), 'myservice')
40 |
41 | server = TChannel('server')
42 |
43 | @server.thrift.register(service.Foo, method='hello')
44 | def foo_hello(request):
45 | return 'foo'
46 |
47 | @server.thrift.register(service.Bar, method='hello')
48 | def bar_hello(request):
49 | return 'bar'
50 |
51 | server.listen()
52 |
53 | client = TChannel('client')
54 |
55 | res = yield client.thrift(service.Foo.hello(), hostport=server.hostport)
56 | assert res.body == 'foo'
57 |
58 | res = yield client.thrift(service.Bar.hello(), hostport=server.hostport)
59 | assert res.body == 'bar'
60 |
--------------------------------------------------------------------------------
/tests/thrift/test_reflection.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 |
23 | from tchannel.thrift.reflection import get_service_methods
24 |
25 |
26 | def test_get_service_methods():
27 |
28 | class Iface(object):
29 |
30 | def __init__(self):
31 | pass
32 |
33 | def hello(self):
34 | pass
35 |
36 | def world(self, foo):
37 | pass
38 |
39 | assert set(['hello', 'world']) == get_service_methods(Iface)
40 |
--------------------------------------------------------------------------------
/tests/tornado/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/uber/tchannel-python/248741146cb25f32fc3a1a3fabfb13d1d6b62355/tests/tornado/__init__.py
--------------------------------------------------------------------------------
/tests/tornado/conftest.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 |
23 | import socket
24 |
25 | import pytest
26 | import tornado.iostream
27 |
28 | from tchannel.tornado.connection import StreamConnection
29 |
30 |
31 | @pytest.yield_fixture
32 | def tornado_pair(io_loop):
33 | server, client = socket.socketpair()
34 |
35 | server_stream = tornado.iostream.IOStream(server)
36 | client_stream = tornado.iostream.IOStream(client)
37 |
38 | server_conn = StreamConnection(server_stream)
39 | client_conn = StreamConnection(client_stream)
40 |
41 | try:
42 | yield server_conn, client_conn
43 | finally:
44 | server_stream.close()
45 | client_stream.close()
46 |
--------------------------------------------------------------------------------
/tests/tornado/test_request.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 |
23 | from tchannel.messages.common import FlagsType
24 | from tchannel.tornado.request import Request
25 | from tchannel.tornado.request import TransportMetadata
26 |
27 |
28 | def test_transport_metadata_creation():
29 | request = Request(
30 | id=42,
31 | flags=FlagsType.fragment,
32 | ttl=100,
33 | service='some_service',
34 | headers={'cn': 'another_service', 'as': 'thrift'}
35 | )
36 |
37 | meta = TransportMetadata.from_request(request)
38 | assert 42 == meta.id
39 | assert FlagsType.fragment == meta.flags
40 | assert 100 == meta.ttl
41 | assert 'some_service' == meta.service
42 | assert {'cn': 'another_service', 'as': 'thrift'} == meta.headers
43 |
--------------------------------------------------------------------------------
/tests/tornado/test_tchannel.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import absolute_import
22 |
23 | import pytest
24 | import socket
25 |
26 | from tchannel.errors import AlreadyListeningError
27 | from tchannel.tornado import TChannel
28 | from six.moves import range
29 |
30 |
31 | @pytest.fixture
32 | def tchannel():
33 | return TChannel(name='test')
34 |
35 |
36 | @pytest.mark.gen_test
37 | def test_peer_caching(tchannel):
38 | "Connections are long-lived and should not be recreated."""
39 | peer = tchannel.peers.get("localhost:4040")
40 | assert tchannel.peers.get("localhost:4040") is peer
41 |
42 |
43 | def test_known_peers():
44 | peers = ["localhost:%d" % port for port in range(4040, 4101)]
45 | tchannel = TChannel('test', known_peers=peers)
46 |
47 | for peer in peers:
48 | assert tchannel.peers.lookup(peer)
49 |
50 |
51 | def test_is_listening_should_return_false_when_listen_not_called(tchannel):
52 |
53 | assert tchannel.is_listening() is False
54 |
55 |
56 | def test_is_listening_should_return_true_when_listen_called(tchannel):
57 |
58 | tchannel.listen()
59 |
60 | assert tchannel.is_listening() is True
61 |
62 |
63 | def test_should_error_if_call_listen_twice(tchannel):
64 |
65 | tchannel.listen()
66 |
67 | with pytest.raises(AlreadyListeningError):
68 | tchannel.listen()
69 |
70 |
71 | def test_close_stops_listening():
72 | server = TChannel(name='server')
73 | server.listen()
74 |
75 | host = server.host
76 | port = server.port
77 |
78 | # Can connect
79 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
80 | sock.connect((host, port))
81 | sock.close()
82 |
83 | server.close()
84 |
85 | # Can't connect
86 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
87 |
88 | with pytest.raises(socket.error):
89 | sock.connect((host, port))
90 |
--------------------------------------------------------------------------------
/tests/tornado/test_tombstone.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Uber Technologies, Inc.
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 |
21 | from __future__ import (
22 | absolute_import, unicode_literals, print_function, division
23 | )
24 |
25 | import pytest
26 | from tornado import gen
27 |
28 | from tchannel.tornado.tombstone import Cemetery
29 |
30 |
31 | @pytest.mark.gen_test
32 | def test_add_and_forget():
33 | cem = Cemetery(ttl_offset_secs=0.01)
34 | cem.add(1, 0.01)
35 | cem.add(2, 0.05)
36 |
37 | assert 1 in cem
38 | assert 2 in cem
39 |
40 | yield gen.sleep(0.020)
41 |
42 | assert 1 not in cem
43 | assert 2 in cem
44 |
45 |
46 | @pytest.mark.gen_test
47 | def test_add_and_explicit_forget():
48 | cem = Cemetery(ttl_offset_secs=0.01)
49 | cem.add(1, 0.05)
50 |
51 | yield gen.sleep(0.04)
52 |
53 | assert 1 in cem
54 | cem.forget(1)
55 | assert 1 not in cem
56 |
57 |
58 | @pytest.mark.gen_test
59 | def test_max_ttl():
60 | cem = Cemetery(max_ttl_secs=0.05)
61 | cem.add(1, 0.2)
62 |
63 | assert 1 in cem
64 | yield gen.sleep(0.05)
65 | assert 1 not in cem
66 |
67 |
68 | @pytest.mark.gen_test
69 | def test_clear():
70 | cem = Cemetery()
71 | cem.add(1, 0.1)
72 | cem.add(2, 0.2)
73 |
74 | assert 1 in cem
75 | assert 2 in cem
76 |
77 | cem.clear()
78 |
79 | assert 1 not in cem
80 | assert 2 not in cem
81 |
--------------------------------------------------------------------------------
/tox.ini:
--------------------------------------------------------------------------------
1 | [tox]
2 | envlist = py34,py35,py36,py37,cover,flake8,docs
3 |
4 |
5 | [testenv]
6 | deps =
7 | -rrequirements-test.txt
8 | whitelist_externals = /usr/bin/make
9 | commands =
10 | pytest --cov-report=term-missing --cov tchannel --cov-report=xml -v {posargs}
11 | basepython =
12 | py27: python2.7
13 | py34: python3.4
14 | py35: python3.5
15 | py36: python3.6
16 | py37: python3.7
17 | pypy: pypy
18 |
19 |
20 | [testenv:flake8]
21 | basepython = python
22 | commands = make lint
23 |
24 |
25 | [testenv:cover]
26 | basepython = python
27 | commands =
28 | py.test --cov tchannel --cov-report=xml --cov-report=term-missing {posargs}
29 |
30 | [testenv:benchmark]
31 | basepython = python
32 | commands = make benchmark
33 |
34 | [testenv:docs]
35 | basepython = python
36 | deps =
37 | futures
38 | -rrequirements-docs.txt
39 | tchannel[vcr]
40 | commands = make docs
41 |
--------------------------------------------------------------------------------