├── .flake8 ├── .gitattributes ├── .github ├── dependabot.yaml └── workflows │ ├── release.yaml │ └── test.yml ├── .gitignore ├── .pre-commit-config.yaml ├── .readthedocs.yaml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── MANIFEST.in ├── README.md ├── RELEASE.md ├── docs ├── Makefile ├── make.bat ├── requirements.txt └── source │ ├── _static │ └── images │ │ └── logo │ │ ├── favicon.ico │ │ └── logo.png │ ├── api │ └── index.md │ ├── changelog.md │ ├── conf.py │ ├── consul.md │ ├── details.md │ ├── etcd.md │ ├── file.md │ ├── https.md │ ├── index.md │ ├── install.md │ └── redis.md ├── examples ├── README.md ├── jupyterhub_config_etcd.py ├── jupyterhub_config_redis.py └── jupyterhub_config_toml.py ├── jupyterhub_traefik_proxy ├── __init__.py ├── consul.py ├── etcd.py ├── fileprovider.py ├── install.py ├── kv_proxy.py ├── proxy.py ├── redis.py ├── toml.py └── traefik_utils.py ├── performance ├── ProxyPerformance.ipynb ├── README.md ├── __init__.py ├── bootstrap-vm.sh ├── check_perf.py ├── dummy_http_server.py ├── perf_utils.py ├── requirements.txt ├── results │ ├── chp-http_throughput_large.csv │ ├── chp-http_throughput_small.csv │ ├── chp-methods.csv │ ├── chp-ws_throughput_large.csv │ ├── chp-ws_throughput_small.csv │ ├── consul-methods.csv │ ├── etcd-methods.csv │ ├── file-http_throughput_large.csv │ ├── file-http_throughput_small.csv │ ├── file-methods.csv │ ├── file-ws_throughput_large.csv │ ├── file-ws_throughput_small.csv │ ├── redis-methods.csv │ └── v1 │ │ ├── chp_methods_concurrent.csv │ │ ├── chp_methods_sequential.csv │ │ ├── consul_methods_concurrent.csv │ │ ├── consul_methods_sequential.csv │ │ ├── etcd_methods_concurrent.csv │ │ ├── etcd_methods_sequential.csv │ │ ├── http_throughput_large.csv │ │ ├── http_throughput_small.csv │ │ ├── patched_consul_methods_concurrent.csv │ │ ├── patched_consul_methods_sequential.csv │ │ ├── toml_methods_concurrent.csv │ │ ├── toml_methods_sequential.csv │ │ └── ws_throughput.csv └── run_benchmark.sh ├── pyproject.toml ├── requirements.txt ├── setup.cfg ├── setup.py └── tests ├── __init__.py ├── config_files ├── consul_config.json └── etcd │ ├── ca.crt │ ├── etcd.crt │ └── etcd.key ├── conftest.py ├── dummy_http_server.py ├── test_deprecations.py ├── test_dummy_http_server.py ├── test_installer.py ├── test_kv.py ├── test_proxy.py ├── test_traefik_api_auth.py ├── test_traefik_utils.py └── utils.py /.flake8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/.flake8 -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | jupyterhub_traefik_proxy/_version.py export-subst 2 | -------------------------------------------------------------------------------- /.github/dependabot.yaml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/.github/dependabot.yaml -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/.github/workflows/release.yaml -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/.github/workflows/test.yml -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/.gitignore -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/.pre-commit-config.yaml -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/.readthedocs.yaml -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/CODE_OF_CONDUCT.md -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/CONTRIBUTING.md -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/LICENSE -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/MANIFEST.in -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/README.md -------------------------------------------------------------------------------- /RELEASE.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/RELEASE.md -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/docs/Makefile -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/docs/make.bat -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/docs/requirements.txt -------------------------------------------------------------------------------- /docs/source/_static/images/logo/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/docs/source/_static/images/logo/favicon.ico -------------------------------------------------------------------------------- /docs/source/_static/images/logo/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/docs/source/_static/images/logo/logo.png -------------------------------------------------------------------------------- /docs/source/api/index.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/docs/source/api/index.md -------------------------------------------------------------------------------- /docs/source/changelog.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/docs/source/changelog.md -------------------------------------------------------------------------------- /docs/source/conf.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/docs/source/conf.py -------------------------------------------------------------------------------- /docs/source/consul.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/docs/source/consul.md -------------------------------------------------------------------------------- /docs/source/details.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/docs/source/details.md -------------------------------------------------------------------------------- /docs/source/etcd.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/docs/source/etcd.md -------------------------------------------------------------------------------- /docs/source/file.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/docs/source/file.md -------------------------------------------------------------------------------- /docs/source/https.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/docs/source/https.md -------------------------------------------------------------------------------- /docs/source/index.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/docs/source/index.md -------------------------------------------------------------------------------- /docs/source/install.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/docs/source/install.md -------------------------------------------------------------------------------- /docs/source/redis.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/docs/source/redis.md -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/examples/README.md -------------------------------------------------------------------------------- /examples/jupyterhub_config_etcd.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/examples/jupyterhub_config_etcd.py -------------------------------------------------------------------------------- /examples/jupyterhub_config_redis.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/examples/jupyterhub_config_redis.py -------------------------------------------------------------------------------- /examples/jupyterhub_config_toml.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/examples/jupyterhub_config_toml.py -------------------------------------------------------------------------------- /jupyterhub_traefik_proxy/__init__.py: -------------------------------------------------------------------------------- 1 | """Traefik implementation of the JupyterHub proxy API""" 2 | 3 | __version__ = "2.2.0.dev" 4 | -------------------------------------------------------------------------------- /jupyterhub_traefik_proxy/consul.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/jupyterhub_traefik_proxy/consul.py -------------------------------------------------------------------------------- /jupyterhub_traefik_proxy/etcd.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/jupyterhub_traefik_proxy/etcd.py -------------------------------------------------------------------------------- /jupyterhub_traefik_proxy/fileprovider.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/jupyterhub_traefik_proxy/fileprovider.py -------------------------------------------------------------------------------- /jupyterhub_traefik_proxy/install.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/jupyterhub_traefik_proxy/install.py -------------------------------------------------------------------------------- /jupyterhub_traefik_proxy/kv_proxy.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/jupyterhub_traefik_proxy/kv_proxy.py -------------------------------------------------------------------------------- /jupyterhub_traefik_proxy/proxy.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/jupyterhub_traefik_proxy/proxy.py -------------------------------------------------------------------------------- /jupyterhub_traefik_proxy/redis.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/jupyterhub_traefik_proxy/redis.py -------------------------------------------------------------------------------- /jupyterhub_traefik_proxy/toml.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/jupyterhub_traefik_proxy/toml.py -------------------------------------------------------------------------------- /jupyterhub_traefik_proxy/traefik_utils.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/jupyterhub_traefik_proxy/traefik_utils.py -------------------------------------------------------------------------------- /performance/ProxyPerformance.ipynb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/performance/ProxyPerformance.ipynb -------------------------------------------------------------------------------- /performance/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/performance/README.md -------------------------------------------------------------------------------- /performance/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /performance/bootstrap-vm.sh: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/performance/bootstrap-vm.sh -------------------------------------------------------------------------------- /performance/check_perf.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/performance/check_perf.py -------------------------------------------------------------------------------- /performance/dummy_http_server.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/performance/dummy_http_server.py -------------------------------------------------------------------------------- /performance/perf_utils.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/performance/perf_utils.py -------------------------------------------------------------------------------- /performance/requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/performance/requirements.txt -------------------------------------------------------------------------------- /performance/results/chp-http_throughput_large.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/performance/results/chp-http_throughput_large.csv -------------------------------------------------------------------------------- /performance/results/chp-http_throughput_small.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/performance/results/chp-http_throughput_small.csv -------------------------------------------------------------------------------- /performance/results/chp-methods.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/performance/results/chp-methods.csv -------------------------------------------------------------------------------- /performance/results/chp-ws_throughput_large.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/performance/results/chp-ws_throughput_large.csv -------------------------------------------------------------------------------- /performance/results/chp-ws_throughput_small.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/performance/results/chp-ws_throughput_small.csv -------------------------------------------------------------------------------- /performance/results/consul-methods.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/performance/results/consul-methods.csv -------------------------------------------------------------------------------- /performance/results/etcd-methods.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/performance/results/etcd-methods.csv -------------------------------------------------------------------------------- /performance/results/file-http_throughput_large.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/performance/results/file-http_throughput_large.csv -------------------------------------------------------------------------------- /performance/results/file-http_throughput_small.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/performance/results/file-http_throughput_small.csv -------------------------------------------------------------------------------- /performance/results/file-methods.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/performance/results/file-methods.csv -------------------------------------------------------------------------------- /performance/results/file-ws_throughput_large.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/performance/results/file-ws_throughput_large.csv -------------------------------------------------------------------------------- /performance/results/file-ws_throughput_small.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/performance/results/file-ws_throughput_small.csv -------------------------------------------------------------------------------- /performance/results/redis-methods.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/performance/results/redis-methods.csv -------------------------------------------------------------------------------- /performance/results/v1/chp_methods_concurrent.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/performance/results/v1/chp_methods_concurrent.csv -------------------------------------------------------------------------------- /performance/results/v1/chp_methods_sequential.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/performance/results/v1/chp_methods_sequential.csv -------------------------------------------------------------------------------- /performance/results/v1/consul_methods_concurrent.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/performance/results/v1/consul_methods_concurrent.csv -------------------------------------------------------------------------------- /performance/results/v1/consul_methods_sequential.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/performance/results/v1/consul_methods_sequential.csv -------------------------------------------------------------------------------- /performance/results/v1/etcd_methods_concurrent.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/performance/results/v1/etcd_methods_concurrent.csv -------------------------------------------------------------------------------- /performance/results/v1/etcd_methods_sequential.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/performance/results/v1/etcd_methods_sequential.csv -------------------------------------------------------------------------------- /performance/results/v1/http_throughput_large.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/performance/results/v1/http_throughput_large.csv -------------------------------------------------------------------------------- /performance/results/v1/http_throughput_small.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/performance/results/v1/http_throughput_small.csv -------------------------------------------------------------------------------- /performance/results/v1/patched_consul_methods_concurrent.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/performance/results/v1/patched_consul_methods_concurrent.csv -------------------------------------------------------------------------------- /performance/results/v1/patched_consul_methods_sequential.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/performance/results/v1/patched_consul_methods_sequential.csv -------------------------------------------------------------------------------- /performance/results/v1/toml_methods_concurrent.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/performance/results/v1/toml_methods_concurrent.csv -------------------------------------------------------------------------------- /performance/results/v1/toml_methods_sequential.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/performance/results/v1/toml_methods_sequential.csv -------------------------------------------------------------------------------- /performance/results/v1/ws_throughput.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/performance/results/v1/ws_throughput.csv -------------------------------------------------------------------------------- /performance/run_benchmark.sh: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/performance/run_benchmark.sh -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/pyproject.toml -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | aiohttp 2 | bcrypt 3 | escapism 4 | jupyterhub>=0.9 5 | toml 6 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/setup.cfg -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/setup.py -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/config_files/consul_config.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/tests/config_files/consul_config.json -------------------------------------------------------------------------------- /tests/config_files/etcd/ca.crt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/tests/config_files/etcd/ca.crt -------------------------------------------------------------------------------- /tests/config_files/etcd/etcd.crt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/tests/config_files/etcd/etcd.crt -------------------------------------------------------------------------------- /tests/config_files/etcd/etcd.key: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/tests/config_files/etcd/etcd.key -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/tests/conftest.py -------------------------------------------------------------------------------- /tests/dummy_http_server.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/tests/dummy_http_server.py -------------------------------------------------------------------------------- /tests/test_deprecations.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/tests/test_deprecations.py -------------------------------------------------------------------------------- /tests/test_dummy_http_server.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/tests/test_dummy_http_server.py -------------------------------------------------------------------------------- /tests/test_installer.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/tests/test_installer.py -------------------------------------------------------------------------------- /tests/test_kv.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/tests/test_kv.py -------------------------------------------------------------------------------- /tests/test_proxy.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/tests/test_proxy.py -------------------------------------------------------------------------------- /tests/test_traefik_api_auth.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/tests/test_traefik_api_auth.py -------------------------------------------------------------------------------- /tests/test_traefik_utils.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/tests/test_traefik_utils.py -------------------------------------------------------------------------------- /tests/utils.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyterhub/traefik-proxy/HEAD/tests/utils.py --------------------------------------------------------------------------------