├── .coveragerc ├── .gitignore ├── .travis.yml ├── LICENSE ├── MANIFEST.in ├── README.rst ├── TODO.md ├── appveyor.yml ├── appveyor ├── build.py ├── install-pypy.py ├── install.ps1 ├── run_with_env.cmd └── wheel_fix │ ├── bdist_wheel.py │ └── metadata.py ├── cffi_declarations.c ├── cffi_source.c ├── cffi_template.py ├── dev-requirements.txt ├── doc ├── build-html.sh └── source │ ├── conf.py │ ├── dns.rst │ ├── error.rst │ ├── handle.rst │ ├── handles │ ├── async.rst │ ├── check.rst │ ├── fs_event.rst │ ├── fs_poll.rst │ ├── idle.rst │ ├── pipe.rst │ ├── poll.rst │ ├── prepare.rst │ ├── process.rst │ ├── sig.rst │ ├── stream.rst │ ├── tcp.rst │ ├── timer.rst │ ├── tty.rst │ └── udp.rst │ ├── index.rst │ ├── loop.rst │ └── request.rst ├── examples ├── benchmark_echo.py └── benchmark_http.py ├── setup.py ├── tests ├── common.py ├── data │ ├── program_dump_env.py │ ├── program_endless_loop.py │ ├── program_hello.py │ ├── server.crt │ └── server.key ├── test_abstract.py ├── test_async.py ├── test_check.py ├── test_dns.py ├── test_error.py ├── test_fs_event.py ├── test_fs_poll.py ├── test_gc.py ├── test_handle.py ├── test_idle.py ├── test_loop.py ├── test_misc.py ├── test_ping_pong.py ├── test_pipe.py ├── test_poll.py ├── test_prepare.py ├── test_process.py ├── test_request.py ├── test_secure.py ├── test_signal.py ├── test_stream.py ├── test_tcp.py ├── test_timer.py ├── test_tty.py └── test_udp.py ├── tox.ini ├── uv ├── __init__.py ├── abstract.py ├── base.py ├── common.py ├── dns.py ├── error.py ├── fs.py ├── handle.py ├── handles │ ├── __init__.py │ ├── async.py │ ├── check.py │ ├── fs_event.py │ ├── fs_poll.py │ ├── idle.py │ ├── pipe.py │ ├── poll.py │ ├── prepare.py │ ├── process.py │ ├── signal.py │ ├── stream.py │ ├── tcp.py │ ├── timer.py │ ├── tty.py │ └── udp.py ├── helpers │ ├── __init__.py │ ├── mock.py │ └── tracer.py ├── library.py ├── loop.py ├── metadata.py ├── misc.py ├── request.py └── secure.py └── uvcffi └── .keep /.coveragerc: -------------------------------------------------------------------------------- 1 | [report] 2 | exclude_lines = 3 | pragma: no cover 4 | def __repr__ 5 | if code != error.StatusCodes.SUCCESS 6 | raise NotImplementedError() 7 | omit = 8 | uv/helpers/* -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # IDE 2 | .idea 3 | 4 | # Python 5 | *.pyc 6 | *.pyd 7 | *.so 8 | 9 | __pycache__ 10 | 11 | build 12 | dist 13 | deps 14 | 15 | uvcffi/__init__.py 16 | 17 | MANIFEST 18 | .tox 19 | 20 | .coverage 21 | 22 | _uvcffi* 23 | 24 | playground 25 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | sudo: false 3 | matrix: 4 | include: 5 | - python: 2.7 6 | env: TOX_ENV=py27 7 | - python: 3.3 8 | env: TOX_ENV=py33 9 | - python: 3.4 10 | env: TOX_ENV=py34 11 | - python: 3.5 12 | env: TOX_ENV=py35 13 | - python: pypy 14 | env: TOX_ENV=pypy 15 | - python: pypy3 16 | env: TOX_ENV=pypy3 17 | install: 18 | - pip install cffi 19 | - pip install tox 20 | - pip install coveralls 21 | script: 22 | - tox -e $TOX_ENV 23 | after_success: 24 | - coveralls 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.rst 2 | include cffi_declarations.c cffi_source.c cffi_template.py 3 | 4 | recursive-include deps * 5 | recursive-include tests * 6 | 7 | recursive-include doc * 8 | recursive-exclude doc/build * -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Python »libuv« CFFI Wrapper 2 | =========================== 3 | 4 | |pypi| |unix_build| |windows_build| |coverage| |docs| 5 | 6 | This package aims to provide an object oriented CFFI based wrapper around the libuv 7 | asynchronous IO library. It supports all handles of libuv as well as filesystem 8 | operations, dns utility functions and miscellaneous utilities. 9 | 10 | State 11 | ===== 12 | 13 | :handles: stable API, well tested, completely documented 14 | :dns: stable API, well tested, completely documented 15 | :fs: planning 16 | :misc: planning 17 | :ssl: planning 18 | 19 | Features 20 | ======== 21 | - full featured event loop backed by epoll, kqueue, IOCP and events ports 22 | - asynchronous TCP and UDP sockets 23 | - asynchronous SSL sockets (based on Python's SSL module) 24 | - asynchronous DNS resolution 25 | - asynchronous file and file system operations 26 | - asynchronous file system events 27 | - cross platform ANSI escape code controlled TTY 28 | - IPC with socket sharing, using UNIX domain sockets or named pipes (Windows) 29 | - child processes and signal handling 30 | - cross platform memory, CPU and network interface information 31 | - timer and high resolution clock 32 | - supported Python interpreters and versions: 33 | 34 | - **CPython**: 2.7, 3.3, 3.4, 3.5 35 | - **PyPy**: 4.0 (Windows and Linux) 36 | - **PyPy3**: 2.4.0, 2.7.0-alpha0 (Linux Only) 37 | 38 | - PyCharm type hinting support 39 | - PEP 3151 compatible exceptions 40 | 41 | 42 | .. |pypi| image:: https://img.shields.io/pypi/v/uv.svg?style=flat-square&label=latest%20version 43 | :target: https://pypi.python.org/pypi/uv 44 | 45 | .. |unix_build| image:: https://img.shields.io/travis/koehlma/uv/master.svg?style=flat-square&label=unix%20build 46 | :target: https://travis-ci.org/koehlma/uv 47 | 48 | .. |windows_build| image:: https://img.shields.io/appveyor/ci/koehlma/uv.svg?style=flat-square&label=windows%20build 49 | :target: https://ci.appveyor.com/project/koehlma/uv 50 | 51 | .. |docs| image:: https://readthedocs.org/projects/uv/badge/?version=master&style=flat-square 52 | :target: https://uv.readthedocs.org/en/master/ 53 | 54 | .. |coverage| image:: https://img.shields.io/coveralls/koehlma/uv/master.svg?style=flat-square 55 | :target: https://coveralls.io/github/koehlma/uv?branch=master 56 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | # TODO 2 | 3 | - [x] better error handling (exceptions for every status code) 4 | - [ ] auto destroy on exception in constructor 5 | - [x] rename FSMonitor to FSEvent (FSEvent → FSEvents) 6 | - [ ] better docstrings 7 | - [ ] more and better unittests 8 | - [ ] complete support of all libuv features 9 | - [x] excepthook instead of contextmanager 10 | - [ ] process: fix create pipe __repr__ writable boolean 11 | - [ ] better name for cross platform wrappers (or even better - remove them) -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | global: 3 | CMD_IN_ENV: "cmd /E:ON /V:ON /C .\\appveyor\\run_with_env.cmd" 4 | 5 | matrix: 6 | - PYTHON: "C:\\Python27" 7 | PYTHON_VERSION: "2.7.x" 8 | PYTHON_ARCH: "32" 9 | 10 | - PYTHON: "C:\\Python27-x64" 11 | PYTHON_VERSION: "2.7.x" 12 | PYTHON_ARCH: "64" 13 | 14 | - PYTHON: "C:\\Python33" 15 | PYTHON_VERSION: "3.3.x" 16 | PYTHON_ARCH: "32" 17 | 18 | - PYTHON: "C:\\Python33-x64" 19 | PYTHON_VERSION: "3.3.x" 20 | PYTHON_ARCH: "64" 21 | 22 | - PYTHON: "C:\\Python34" 23 | PYTHON_VERSION: "3.4.x" 24 | PYTHON_ARCH: "32" 25 | 26 | - PYTHON: "C:\\Python34-x64" 27 | PYTHON_VERSION: "3.4.x" 28 | PYTHON_ARCH: "64" 29 | 30 | - PYTHON: "C:\\Python35" 31 | PYTHON_VERSION: "3.5.x" 32 | PYTHON_ARCH: "32" 33 | 34 | - PYTHON: "C:\\Python35-x64" 35 | PYTHON_VERSION: "3.5.x" 36 | PYTHON_ARCH: "64" 37 | 38 | - PYTHON: "C:\\PyPy-401" 39 | PYTHON_PYPY: "pypy" 40 | PYTHON_PYPY_VERSION: "4.0.1" 41 | PYTHON_VERSION: "2.7.10" 42 | PYTHON_ARCH: "32" 43 | 44 | # skipping – we are getting some strange errors here 45 | #- PYTHON: "C:\\PyPy3-240" 46 | # PYTHON_PYPY: "pypy3" 47 | # PYTHON_PYPY_VERSION: "2.4.0" 48 | # PYTHON_VERSION: "3.2.5" 49 | # PYTHON_ARCH: "32" 50 | 51 | 52 | install: 53 | - ECHO "Filesystem root:" 54 | - ps: "ls \"C:/\"" 55 | 56 | - ECHO "Installed SDKs:" 57 | - ps: "ls \"C:/Program Files/Microsoft SDKs/Windows\"" 58 | 59 | - "C:\\Python35\\python.exe appveyor\\install-pypy.py" 60 | 61 | - ps: if (-not(Test-Path($env:PYTHON))) { & appveyor\install.ps1 } 62 | 63 | - "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PYTHON%\\bin;%PATH%" 64 | - "python --version" 65 | - "python -c \"import struct; print(struct.calcsize('P') * 8)\"" 66 | - "%CMD_IN_ENV% pip install -r dev-requirements.txt" 67 | 68 | build_script: 69 | - "SET APPVEYOR=True" 70 | - "%CMD_IN_ENV% python setup.py build" 71 | 72 | test_script: 73 | - "%CMD_IN_ENV% python setup.py nosetests --verbosity=2" 74 | 75 | after_test: 76 | - "C:\\Python35\\python.exe appveyor\\build.py" 77 | - ps: "ls dist" 78 | 79 | artifacts: 80 | - path: dist\* 81 | -------------------------------------------------------------------------------- /appveyor/build.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | 16 | import os 17 | import os.path 18 | import subprocess 19 | 20 | PYTHON = os.environ.get('PYTHON', None) 21 | PYTHON_PYPY = os.environ.get('PYTHON_PYPY', None) 22 | 23 | python_exe = os.path.join(PYTHON, 'python.exe') 24 | 25 | __dir__ = os.path.dirname(__file__) 26 | 27 | print(python_exe) 28 | 29 | if PYTHON_PYPY: 30 | import shutil 31 | shutil.copy(os.path.join(__dir__, 'wheel_fix', 'bdist_wheel.py'), 32 | os.path.join(PYTHON, 'site-packages', 'wheel')) 33 | shutil.copy(os.path.join(__dir__, 'wheel_fix', 'metadata.py'), 34 | os.path.join(PYTHON, 'site-packages', 'wheel')) 35 | 36 | subprocess.call(['%CMD_IN_ENV%', python_exe, 'setup.py', 'bdist_wheel'], shell=True) 37 | subprocess.call(['%CMD_IN_ENV%', python_exe, 'setup.py', 'bdist_wininst'], shell=True) 38 | 39 | if not PYTHON_PYPY: 40 | subprocess.call(['%CMD_IN_ENV%', python_exe, 'setup.py', 'bdist_msi'], shell=True) 41 | -------------------------------------------------------------------------------- /appveyor/install-pypy.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | 16 | import os 17 | import subprocess 18 | import sys 19 | import tarfile 20 | import urllib.request 21 | import zipfile 22 | 23 | try: 24 | os.mkdir('deps') 25 | except OSError: 26 | pass 27 | 28 | PYTHON = os.environ.get('PYTHON', None) 29 | PYTHON_PYPY = os.environ.get('PYTHON_PYPY', None) 30 | PYTHON_PYPY_VERSION = os.environ.get('PYTHON_PYPY_VERSION', None) 31 | PYTHON_ARCH = os.environ.get('PYTHON_ARCH', None) 32 | 33 | if not PYTHON_PYPY: sys.exit(0) 34 | if os.path.exists(os.path.join(PYTHON, 'python.exe')): sys.exit(0) 35 | 36 | URL = 'https://bitbucket.org/pypy/pypy/downloads/{}-{}-win32.zip' 37 | 38 | url = URL.format(PYTHON_PYPY, PYTHON_PYPY_VERSION) 39 | 40 | print(url) 41 | 42 | response = urllib.request.urlopen(url) 43 | with open('deps\\pypy.zip', 'wb') as pypy_zip: 44 | pypy_zip.write(response.read()) 45 | 46 | pypy_zip = zipfile.ZipFile('deps\\pypy.zip') 47 | pypy_zip.extractall('C:\\') 48 | 49 | os.rename('C:\\{}-{}-win32'.format(PYTHON_PYPY, PYTHON_PYPY_VERSION), PYTHON) 50 | try: 51 | os.unlink('C:\\pypy.zip') 52 | except OSError: 53 | pass 54 | os.link(os.path.join(PYTHON, 'pypy.exe'), os.path.join(PYTHON, 'python.exe')) 55 | 56 | EZ_SETUP = 'https://bootstrap.pypa.io/ez_setup.py' 57 | 58 | response = urllib.request.urlopen(EZ_SETUP) 59 | with open(os.path.join('deps', 'ez_setup.py'), 'wb') as get_pip: 60 | get_pip.write(response.read()) 61 | 62 | ez_setup = os.path.join('deps', 'ez_setup.py') 63 | subprocess.call([os.path.join(PYTHON, 'python.exe'), ez_setup]) 64 | 65 | PIP_URL = 'https://pypi.python.org/packages/source/p/pip/pip-7.1.2.tar.gz' 66 | 67 | response = urllib.request.urlopen(PIP_URL) 68 | with open(os.path.join('deps', 'pip.tar.gz'), 'wb') as get_pip: 69 | get_pip.write(response.read()) 70 | 71 | pip_tar = tarfile.open(os.path.join('deps', 'pip.tar.gz'), 'r:gz') 72 | pip_tar.extractall(PYTHON) 73 | 74 | pip_setup = os.path.join(PYTHON, 'pip-7.1.2', 'setup.py') 75 | 76 | subprocess.call([os.path.join(PYTHON, 'python.exe'), pip_setup, 'install'], 77 | cwd=os.path.join(PYTHON, 'pip-7.1.2')) 78 | -------------------------------------------------------------------------------- /appveyor/install.ps1: -------------------------------------------------------------------------------- 1 | # Sample script to install Python and pip under Windows 2 | # Authors: Olivier Grisel, Jonathan Helmus, Kyle Kastner, and Alex Willmer 3 | # License: CC0 1.0 Universal: http://creativecommons.org/publicdomain/zero/1.0/ 4 | 5 | $MINICONDA_URL = "http://repo.continuum.io/miniconda/" 6 | $BASE_URL = "https://www.python.org/ftp/python/" 7 | $GET_PIP_URL = "https://bootstrap.pypa.io/get-pip.py" 8 | $GET_PIP_PATH = "C:\get-pip.py" 9 | 10 | $PYTHON_PRERELEASE_REGEX = @" 11 | (?x) 12 | (?\d+) 13 | \. 14 | (?\d+) 15 | \. 16 | (?\d+) 17 | (?[a-z]{1,2}\d+) 18 | "@ 19 | 20 | 21 | function Download ($filename, $url) { 22 | $webclient = New-Object System.Net.WebClient 23 | 24 | $basedir = $pwd.Path + "\" 25 | $filepath = $basedir + $filename 26 | if (Test-Path $filename) { 27 | Write-Host "Reusing" $filepath 28 | return $filepath 29 | } 30 | 31 | # Download and retry up to 3 times in case of network transient errors. 32 | Write-Host "Downloading" $filename "from" $url 33 | $retry_attempts = 2 34 | for ($i = 0; $i -lt $retry_attempts; $i++) { 35 | try { 36 | $webclient.DownloadFile($url, $filepath) 37 | break 38 | } 39 | Catch [Exception]{ 40 | Start-Sleepypyp 1 41 | } 42 | } 43 | if (Test-Path $filepath) { 44 | Write-Host "File saved at" $filepath 45 | } else { 46 | # Retry once to get the error message if any at the last try 47 | $webclient.DownloadFile($url, $filepath) 48 | } 49 | return $filepath 50 | } 51 | 52 | 53 | function ParsePythonVersion ($python_version) { 54 | if ($python_version -match $PYTHON_PRERELEASE_REGEX) { 55 | return ([int]$matches.major, [int]$matches.minor, [int]$matches.micro, 56 | $matches.prerelease) 57 | } 58 | $version_obj = [version]$python_version 59 | return ($version_obj.major, $version_obj.minor, $version_obj.build, "") 60 | } 61 | 62 | 63 | function DownloadPython ($python_version, $platform_suffix) { 64 | $major, $minor, $micro, $prerelease = ParsePythonVersion $python_version 65 | 66 | if (($major -le 2 -and $micro -eq 0) ` 67 | -or ($major -eq 3 -and $minor -le 2 -and $micro -eq 0) ` 68 | ) { 69 | $dir = "$major.$minor" 70 | $python_version = "$major.$minor$prerelease" 71 | } else { 72 | $dir = "$major.$minor.$micro" 73 | } 74 | 75 | if ($prerelease) { 76 | if (($major -le 2) ` 77 | -or ($major -eq 3 -and $minor -eq 1) ` 78 | -or ($major -eq 3 -and $minor -eq 2) ` 79 | -or ($major -eq 3 -and $minor -eq 3) ` 80 | ) { 81 | $dir = "$dir/prev" 82 | } 83 | } 84 | 85 | if (($major -le 2) -or ($major -le 3 -and $minor -le 4)) { 86 | $ext = "msi" 87 | if ($platform_suffix) { 88 | $platform_suffix = ".$platform_suffix" 89 | } 90 | } else { 91 | $ext = "exe" 92 | if ($platform_suffix) { 93 | $platform_suffix = "-$platform_suffix" 94 | } 95 | } 96 | 97 | $filename = "python-$python_version$platform_suffix.$ext" 98 | $url = "$BASE_URL$dir/$filename" 99 | $filepath = Download $filename $url 100 | return $filepath 101 | } 102 | 103 | 104 | function InstallPython ($python_version, $architecture, $python_home) { 105 | Write-Host "Installing Python" $python_version "for" $architecture "bit architecture to" $python_home 106 | if (Test-Path $python_home) { 107 | Write-Host $python_home "already exists, skipping." 108 | return $false 109 | } 110 | if ($architecture -eq "32") { 111 | $platform_suffix = "" 112 | } else { 113 | $platform_suffix = "amd64" 114 | } 115 | $installer_path = DownloadPython $python_version $platform_suffix 116 | $installer_ext = [System.IO.Path]::GetExtension($installer_path) 117 | Write-Host "Installing $installer_path to $python_home" 118 | $install_log = $python_home + ".log" 119 | if ($installer_ext -eq '.msi') { 120 | InstallPythonMSI $installer_path $python_home $install_log 121 | } else { 122 | InstallPythonEXE $installer_path $python_home $install_log 123 | } 124 | if (Test-Path $python_home) { 125 | Write-Host "Python $python_version ($architecture) installation complete" 126 | } else { 127 | Write-Host "Failed to install Python in $python_home" 128 | Get-Content -Path $install_log 129 | Exit 1 130 | } 131 | } 132 | 133 | 134 | function InstallPythonEXE ($exepath, $python_home, $install_log) { 135 | $install_args = "/quiet InstallAllUsers=1 TargetDir=$python_home" 136 | RunCommand $exepath $install_args 137 | } 138 | 139 | 140 | function InstallPythonMSI ($msipath, $python_home, $install_log) { 141 | $install_args = "/qn /log $install_log /i $msipath TARGETDIR=$python_home" 142 | $uninstall_args = "/qn /x $msipath" 143 | RunCommand "msiexec.exe" $install_args 144 | if (-not(Test-Path $python_home)) { 145 | Write-Host "Python seems to be installed else-where, reinstalling." 146 | RunCommand "msiexec.exe" $uninstall_args 147 | RunCommand "msiexec.exe" $install_args 148 | } 149 | } 150 | 151 | function RunCommand ($command, $command_args) { 152 | Write-Host $command $command_args 153 | Start-Process -FilePath $command -ArgumentList $command_args -Wait -Passthru 154 | } 155 | 156 | 157 | function InstallPip ($python_home) { 158 | $pip_path = $python_home + "\Scripts\pip.exe" 159 | $python_path = $python_home + "\python.exe" 160 | if (-not(Test-Path $pip_path)) { 161 | Write-Host "Installing pip..." 162 | $webclient = New-Object System.Net.WebClient 163 | $webclient.DownloadFile($GET_PIP_URL, $GET_PIP_PATH) 164 | Write-Host "Executing:" $python_path $GET_PIP_PATH 165 | & $python_path $GET_PIP_PATH 166 | } else { 167 | Write-Host "pip already installed." 168 | } 169 | } 170 | 171 | 172 | function DownloadMiniconda ($python_version, $platform_suffix) { 173 | if ($python_version -eq "3.4") { 174 | $filename = "Miniconda3-3.5.5-Windows-" + $platform_suffix + ".exe" 175 | } else { 176 | $filename = "Miniconda-3.5.5-Windows-" + $platform_suffix + ".exe" 177 | } 178 | $url = $MINICONDA_URL + $filename 179 | $filepath = Download $filename $url 180 | return $filepath 181 | } 182 | 183 | 184 | function InstallMiniconda ($python_version, $architecture, $python_home) { 185 | Write-Host "Installing Python" $python_version "for" $architecture "bit architecture to" $python_home 186 | if (Test-Path $python_home) { 187 | Write-Host $python_home "already exists, skipping." 188 | return $false 189 | } 190 | if ($architecture -eq "32") { 191 | $platform_suffix = "x86" 192 | } else { 193 | $platform_suffix = "x86_64" 194 | } 195 | $filepath = DownloadMiniconda $python_version $platform_suffix 196 | Write-Host "Installing" $filepath "to" $python_home 197 | $install_log = $python_home + ".log" 198 | $args = "/S /D=$python_home" 199 | Write-Host $filepath $args 200 | Start-Process -FilePath $filepath -ArgumentList $args -Wait -Passthru 201 | if (Test-Path $python_home) { 202 | Write-Host "Python $python_version ($architecture) installation complete" 203 | } else { 204 | Write-Host "Failed to install Python in $python_home" 205 | Get-Content -Path $install_log 206 | Exit 1 207 | } 208 | } 209 | 210 | 211 | function InstallMinicondaPip ($python_home) { 212 | $pip_path = $python_home + "\Scripts\pip.exe" 213 | $conda_path = $python_home + "\Scripts\conda.exe" 214 | if (-not(Test-Path $pip_path)) { 215 | Write-Host "Installing pip..." 216 | $args = "install --yes pip" 217 | Write-Host $conda_path $args 218 | Start-Process -FilePath "$conda_path" -ArgumentList $args -Wait -Passthru 219 | } else { 220 | Write-Host "pip already installed." 221 | } 222 | } 223 | 224 | function main () { 225 | if ($env:PYTHON_PYPY -contains "pypy*") { 226 | InstallPython $env:PYTHON_VERSION $env:PYTHON_ARCH $env:PYTHON 227 | InstallPip $env:PYTHON 228 | } 229 | } 230 | 231 | main 232 | -------------------------------------------------------------------------------- /appveyor/run_with_env.cmd: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | SET COMMAND_TO_RUN=%* 4 | SET WIN_SDK_ROOT=C:\Program Files\Microsoft SDKs\Windows 5 | 6 | SET SET_SDK=Y 7 | SET MAJOR_PYTHON_VERSION=%PYTHON_VERSION:~0,1% 8 | IF "%PYTHON_VERSION:~3,1%" == "." ( 9 | SET MINOR_PYTHON_VERSION=%PYTHON_VERSION:~2,1% 10 | ) ELSE ( 11 | SET MINOR_PYTHON_VERSION=%PYTHON_VERSION:~2,2% 12 | ) 13 | 14 | IF %MAJOR_PYTHON_VERSION% == 2 ( 15 | SET WINDOWS_SDK_VERSION="v7.0" 16 | ) ELSE IF %MAJOR_PYTHON_VERSION% == 3 ( 17 | SET WINDOWS_SDK_VERSION="v7.1" 18 | IF %MINOR_PYTHON_VERSION% GTR 4 ( 19 | SET SET_SDK=N 20 | ) 21 | ) ELSE ( 22 | ECHO Unsupported Python version: "%MAJOR_PYTHON_VERSION%" 23 | EXIT 1 24 | ) 25 | 26 | IF "%PYTHON_PYPY:~0,4%" == "pypy" ( 27 | SET SET_SDK=Y 28 | SET WINDOWS_SDK_VERSION="v7.0" 29 | ) 30 | 31 | IF %SET_SDK% == Y ( 32 | IF "%PYTHON_ARCH%" == "64" ( 33 | ECHO Configuring Windows SDK %WINDOWS_SDK_VERSION% for Python %MAJOR_PYTHON_VERSION% on a 64 bit architecture 34 | SET DISTUTILS_USE_SDK=1 35 | SET MSSdk=1 36 | "%WIN_SDK_ROOT%\%WINDOWS_SDK_VERSION%\Setup\WindowsSdkVer.exe" -q -version:%WINDOWS_SDK_VERSION% 37 | "%WIN_SDK_ROOT%\%WINDOWS_SDK_VERSION%\Bin\SetEnv.cmd" /x64 /release 38 | ECHO Executing: %COMMAND_TO_RUN% 39 | call %COMMAND_TO_RUN% || EXIT 1 40 | ) ELSE ( 41 | ECHO Configuring Windows SDK %WINDOWS_SDK_VERSION% for Python %MAJOR_PYTHON_VERSION% on a 32 bit architecture 42 | SET DISTUTILS_USE_SDK=1 43 | SET MSSdk=1 44 | "%WIN_SDK_ROOT%\%WINDOWS_SDK_VERSION%\Setup\WindowsSdkVer.exe" -q -version:%WINDOWS_SDK_VERSION% 45 | "%WIN_SDK_ROOT%\%WINDOWS_SDK_VERSION%\Bin\SetEnv.cmd" /x86 /release 46 | ECHO Executing: %COMMAND_TO_RUN% 47 | call %COMMAND_TO_RUN% || EXIT 1 48 | ) 49 | ) ELSE ( 50 | ECHO Using default MSVC build environment for 32 bit architecture 51 | ECHO Executing: %COMMAND_TO_RUN% 52 | call %COMMAND_TO_RUN% || EXIT 1 53 | ) -------------------------------------------------------------------------------- /cffi_source.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Maximilian Koehl 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under 5 | * the terms of the GNU Lesser General Public License version 3 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 10 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License along 13 | * with this program. If not, see . 14 | */ 15 | 16 | #include 17 | 18 | /* Python */ 19 | const char* PYTHON_UV_CFFI_VERSION = "__version__"; 20 | 21 | 22 | /* Cross-Platform */ 23 | typedef struct { 24 | uint64_t flowinfo; 25 | uint64_t scope_id; 26 | } cross_ipv6_additional; 27 | 28 | cross_ipv6_additional cross_get_ipv6_additional(struct sockaddr_in6* addr) { 29 | cross_ipv6_additional result; 30 | result.flowinfo = (uint64_t) addr->sin6_flowinfo; 31 | result.scope_id = (uint64_t) addr->sin6_scope_id; 32 | return result; 33 | } 34 | 35 | void cross_set_ipv6_additional(struct sockaddr_in6* addr, uint64_t flowinfo, uint64_t scope_id) { 36 | addr->sin6_flowinfo = flowinfo; 37 | addr->sin6_scope_id = scope_id; 38 | } 39 | 40 | 41 | 42 | int cross_uv_poll_init_socket(uv_loop_t* loop, uv_poll_t* poll, int fd) { 43 | return uv_poll_init_socket(loop, poll, (uv_os_sock_t) fd); 44 | } 45 | uv_handle_type cross_uv_guess_handle(int fd) { 46 | return uv_guess_handle((uv_file) fd); 47 | } 48 | int cross_uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, int fd, int readable) { 49 | return uv_tty_init(loop, tty, (uv_file) fd, readable); 50 | } 51 | 52 | int cross_uv_pipe_open(uv_pipe_t* pipe, int fd) { 53 | return uv_pipe_open(pipe, (uv_file) fd); 54 | } 55 | int cross_uv_tcp_open(uv_tcp_t* tcp, int fd) { 56 | return uv_tcp_open(tcp, (uv_os_sock_t) fd); 57 | } 58 | int cross_uv_udp_open(uv_udp_t* udp, int fd) { 59 | return uv_udp_open(udp, (uv_os_sock_t) fd); 60 | } 61 | 62 | 63 | void cross_set_process_uid_gid(uv_process_options_t* options, int uid, int gid) { 64 | options->uid = (uv_uid_t) uid; 65 | options->gid = (uv_gid_t) gid; 66 | } 67 | 68 | int cross_uv_fs_close(uv_loop_t* loop, uv_fs_t* request, int fd, uv_fs_cb callback) { 69 | return uv_fs_close(loop, request, (uv_file) fd, callback); 70 | } 71 | 72 | void py_uv_buf_set(uv_buf_t* buffer, char* base, unsigned long length) { 73 | buffer->base = base; 74 | buffer->len = length; 75 | } 76 | char* py_uv_buf_get(uv_buf_t* buffer, unsigned long* length) { 77 | *length = buffer->len; 78 | return buffer->base; 79 | } 80 | 81 | struct sockaddr* interface_address_get_address(uv_interface_address_t* interface_address) { 82 | return (struct sockaddr*) &interface_address->address.address4; 83 | } 84 | struct sockaddr* interface_address_get_netmask(uv_interface_address_t* interface_address) { 85 | return (struct sockaddr*) &interface_address->netmask.netmask4; 86 | } -------------------------------------------------------------------------------- /cffi_template.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | 16 | __version__ = '{version}' 17 | __project__ = 'Python libuv CFFI Bindings' 18 | __author__ = 'Maximilian Köhl' 19 | __email__ = 'mail@koehlma.de' 20 | 21 | import cffi 22 | 23 | declarations = ''' 24 | {declarations} 25 | ''' 26 | 27 | source = ''' 28 | {source} 29 | ''' 30 | 31 | try: 32 | from _uvcffi import ffi, lib 33 | except ImportError: 34 | ffi = cffi.FFI() 35 | ffi.cdef(declarations) 36 | try: 37 | ffi.set_source('_uvcffi', source, libraries=['uv']) 38 | ffi.compile() 39 | from _uvcffi import ffi, lib 40 | except AttributeError or ImportError: 41 | lib = ffi.verify(source, modulename='_uvcffi', libraries=['uv']) 42 | -------------------------------------------------------------------------------- /dev-requirements.txt: -------------------------------------------------------------------------------- 1 | wheel 2 | cffi 3 | nose -------------------------------------------------------------------------------- /doc/build-html.sh: -------------------------------------------------------------------------------- 1 | #/bin/bash 2 | 3 | sphinx-build -b html source build/html -------------------------------------------------------------------------------- /doc/source/conf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | # Copyright (C) 2016, Maximilian Köhl 5 | # 6 | # This program is free software: you can redistribute it and/or modify it under 7 | # the terms of the GNU Lesser General Public License version 3 as published by 8 | # the Free Software Foundation. 9 | # 10 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 11 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 12 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU Lesser General Public License along 15 | # with this program. If not, see . 16 | 17 | import os 18 | import re 19 | import sys 20 | 21 | __dir__ = os.path.dirname(__file__) 22 | sys.path.insert(0, os.path.join(__dir__, '..', '..')) 23 | os.environ['PYTHON_MOCK_LIBUV'] = 'True' 24 | 25 | import uv 26 | 27 | extensions = [ 28 | 'sphinx.ext.autodoc', 29 | 'sphinx.ext.doctest', 30 | 'sphinx.ext.intersphinx', 31 | 'sphinx.ext.todo', 32 | 'sphinx.ext.mathjax', 33 | 'sphinx.ext.viewcode', 34 | ] 35 | 36 | templates_path = ['_templates'] 37 | 38 | source_suffix = '.rst' 39 | 40 | master_doc = 'index' 41 | 42 | project = 'Python libuv CFFI Bindings' 43 | copyright = '2015, Maximilian Köhl' 44 | author = 'Maximilian Köhl' 45 | 46 | version = uv.__version__ 47 | release = uv.__version__ 48 | 49 | language = None 50 | 51 | exclude_patterns = [] 52 | 53 | pygments_style = 'sphinx' 54 | 55 | todo_include_todos = False 56 | 57 | html_static_path = ['_static'] 58 | 59 | intersphinx_mapping = {'python': ('https://docs.python.org/3.5', None)} 60 | 61 | callable_type = re.compile(r':type\s*(?P[a-z_]+)?:\s*' 62 | r'(\(+([a-zA-Z_.]+,\s*)*([a-zA-Z_.]+)\)' 63 | r'\s*->\s*[a-zA-Z_.]+\s*\)*\s*\|?\s*)+') 64 | 65 | argument_types = re.compile(r'\((([a-zA-Z_.]+,\s*)*([a-zA-Z_.]+))\)') 66 | return_type = re.compile(r'->\s*([a-zA-Z_.]+)') 67 | 68 | 69 | def convert_callable_types(app, what, name, obj, options, lines): 70 | docstring = '\n'.join(lines) 71 | match = callable_type.search(docstring) 72 | while match: 73 | if match.group('name'): 74 | replacement = ':type %s: Callable[[' % match.group('name') 75 | else: 76 | replacement = ':type: Callable[[' 77 | replacement += argument_types.search(match.group(0)).group(1) 78 | replacement += '], ' + return_type.search(match.group(0)).group(1) 79 | replacement += ']' 80 | docstring = docstring[:match.start()] + replacement + docstring[match.end():] 81 | match = callable_type.search(docstring) 82 | lines[:] = docstring.splitlines() 83 | 84 | 85 | def setup(app): 86 | app.connect('autodoc-process-docstring', convert_callable_types) 87 | -------------------------------------------------------------------------------- /doc/source/dns.rst: -------------------------------------------------------------------------------- 1 | .. _dns: 2 | 3 | .. currentmodule:: uv 4 | 5 | DNS utilities 6 | ============= 7 | 8 | .. autofunction:: uv.getaddrinfo 9 | 10 | .. autofunction:: uv.getnameinfo 11 | 12 | 13 | Requests 14 | -------- 15 | 16 | .. autoclass:: uv.dns.GetAddrInfo 17 | :members: 18 | :member-order: bysource 19 | :exclude-members: populate 20 | 21 | .. autoclass:: uv.dns.GetNameInfo 22 | :members: 23 | :member-order: bysource 24 | 25 | 26 | Data Structures 27 | --------------- 28 | 29 | .. autoclass:: uv.Address 30 | :members: 31 | :member-order: bysource 32 | 33 | .. autoclass:: uv.Address4 34 | :members: 35 | :member-order: bysource 36 | 37 | .. autoclass:: uv.Address6 38 | :members: 39 | :member-order: bysource 40 | 41 | .. autoclass:: uv.AddrInfo 42 | :members: 43 | :member-order: bysource 44 | 45 | .. autoclass:: uv.NameInfo 46 | :members: 47 | :member-order: bysource 48 | 49 | 50 | Enumerations 51 | ------------ 52 | 53 | .. autoclass:: uv.AddressFamilies 54 | :members: 55 | :member-order: bysource 56 | 57 | .. autoclass:: uv.SocketTypes 58 | :members: 59 | :member-order: bysource 60 | 61 | .. autoclass:: uv.SocketProtocols 62 | :members: 63 | :member-order: bysource 64 | -------------------------------------------------------------------------------- /doc/source/error.rst: -------------------------------------------------------------------------------- 1 | .. _Errors: 2 | 3 | .. currentmodule:: uv 4 | 5 | Errors -- exceptions and error handling 6 | ======================================= 7 | 8 | .. automodule:: uv.error 9 | :members: 10 | :member-order: bysource 11 | -------------------------------------------------------------------------------- /doc/source/handle.rst: -------------------------------------------------------------------------------- 1 | .. _Handle: 2 | 3 | .. currentmodule:: uv 4 | 5 | Handle -- handle base classes 6 | ============================= 7 | 8 | .. autoclass:: uv.Handle 9 | :members: 10 | :member-order: bysource 11 | 12 | .. autoclass:: uv.UVHandle 13 | :members: 14 | :member-order: bysource 15 | -------------------------------------------------------------------------------- /doc/source/handles/async.rst: -------------------------------------------------------------------------------- 1 | .. _Async: 2 | 3 | .. currentmodule:: uv 4 | 5 | :class:`Async` -- async handle 6 | ============================== 7 | 8 | .. autoclass:: uv.Async 9 | :members: 10 | :member-order: bysource 11 | 12 | -------------------------------------------------------------------------------- /doc/source/handles/check.rst: -------------------------------------------------------------------------------- 1 | .. _Check: 2 | 3 | .. currentmodule:: uv 4 | 5 | :class:`Check` -- check handle 6 | ============================== 7 | 8 | .. autoclass:: uv.Check 9 | :members: 10 | :member-order: bysource 11 | 12 | -------------------------------------------------------------------------------- /doc/source/handles/fs_event.rst: -------------------------------------------------------------------------------- 1 | .. _FSEvent: 2 | 3 | .. currentmodule:: uv 4 | 5 | :class:`FSEvent` -- fs event handle 6 | =================================== 7 | 8 | 9 | .. autoclass:: uv.FSEvent 10 | :members: 11 | :member-order: bysource 12 | 13 | .. autoclass:: uv.FSEvents 14 | :members: 15 | :member-order: bysource 16 | 17 | .. autoclass:: uv.FSEventFlags 18 | :members: 19 | :member-order: bysource 20 | -------------------------------------------------------------------------------- /doc/source/handles/fs_poll.rst: -------------------------------------------------------------------------------- 1 | .. _FSPoll: 2 | 3 | .. currentmodule:: uv 4 | 5 | :class:`FSPoll` -- fs poll handle 6 | ================================= 7 | 8 | .. autoclass:: uv.FSPoll 9 | :members: 10 | :member-order: bysource 11 | -------------------------------------------------------------------------------- /doc/source/handles/idle.rst: -------------------------------------------------------------------------------- 1 | .. _Idle: 2 | 3 | .. currentmodule:: uv 4 | 5 | :class:`Idle` -- idle handle 6 | ============================ 7 | 8 | .. autoclass:: uv.Idle 9 | :members: 10 | :member-order: bysource 11 | 12 | -------------------------------------------------------------------------------- /doc/source/handles/pipe.rst: -------------------------------------------------------------------------------- 1 | .. _Pipe: 2 | 3 | .. currentmodule:: uv 4 | 5 | :class:`Pipe` -- pipe handle 6 | ============================ 7 | 8 | .. autoclass:: uv.Pipe 9 | :members: 10 | :member-order: bysource 11 | 12 | .. autoclass:: uv.PipeConnectRequest 13 | :members: 14 | :member-order: bysource 15 | -------------------------------------------------------------------------------- /doc/source/handles/poll.rst: -------------------------------------------------------------------------------- 1 | .. _Poll: 2 | 3 | .. currentmodule:: uv 4 | 5 | :class:`Poll` -- poll handle 6 | ============================ 7 | 8 | .. autoclass:: uv.Poll 9 | :members: 10 | :member-order: bysource 11 | 12 | .. autoclass:: uv.PollEvent 13 | :members: 14 | -------------------------------------------------------------------------------- /doc/source/handles/prepare.rst: -------------------------------------------------------------------------------- 1 | .. _Prepare: 2 | 3 | .. currentmodule:: uv 4 | 5 | :class:`Prepare` -- poll handle 6 | =============================== 7 | 8 | .. autoclass:: uv.Prepare 9 | :members: 10 | :member-order: bysource 11 | -------------------------------------------------------------------------------- /doc/source/handles/process.rst: -------------------------------------------------------------------------------- 1 | .. _Process: 2 | 3 | .. currentmodule:: uv 4 | 5 | :class:`Process` -- process handle 6 | ================================== 7 | 8 | .. autoclass:: uv.Process 9 | :members: 10 | :member-order: bysource 11 | 12 | .. autoclass:: uv.CreatePipe 13 | :members: 14 | :member-order: bysource 15 | 16 | .. autodata:: uv.PIPE 17 | 18 | .. autoclass:: uv.ProcessFlags 19 | :members: 20 | :member-order: bysource 21 | 22 | .. autodata:: uv.process.STDIN 23 | .. autodata:: uv.process.STDOUT 24 | .. autodata:: uv.process.STDERR 25 | 26 | .. autofunction:: uv.process.disable_stdio_inheritance 27 | -------------------------------------------------------------------------------- /doc/source/handles/sig.rst: -------------------------------------------------------------------------------- 1 | .. _Signal: 2 | 3 | .. currentmodule:: uv 4 | 5 | :class:`Signal` -- signal handle 6 | ================================ 7 | 8 | .. autoclass:: uv.Signal 9 | :members: 10 | :member-order: bysource 11 | 12 | .. autoclass:: uv.Signals 13 | :members: 14 | :member-order: bysource 15 | -------------------------------------------------------------------------------- /doc/source/handles/stream.rst: -------------------------------------------------------------------------------- 1 | .. _Stream: 2 | 3 | .. currentmodule:: uv 4 | 5 | :class:`Stream` -- stream handle 6 | ================================ 7 | 8 | .. autoclass:: uv.Stream 9 | :members: 10 | :member-order: bysource 11 | 12 | .. autoclass:: uv.UVStream 13 | :members: 14 | :member-order: bysource 15 | 16 | 17 | .. autoclass:: uv.ConnectRequest 18 | :members: 19 | :member-order: bysource 20 | 21 | .. autoclass:: uv.WriteRequest 22 | :members: 23 | :member-order: bysource 24 | 25 | .. autoclass:: uv.ShutdownRequest 26 | :members: 27 | :member-order: bysource 28 | -------------------------------------------------------------------------------- /doc/source/handles/tcp.rst: -------------------------------------------------------------------------------- 1 | .. _TCP: 2 | 3 | .. currentmodule:: uv 4 | 5 | :class:`TCP` -- TCP handle 6 | ========================== 7 | 8 | .. autoclass:: uv.TCP 9 | :members: 10 | :member-order: bysource 11 | 12 | .. autoclass:: uv.TCPFlags 13 | :members: 14 | :member-order: bysource 15 | -------------------------------------------------------------------------------- /doc/source/handles/timer.rst: -------------------------------------------------------------------------------- 1 | .. _Timer: 2 | 3 | .. currentmodule:: uv 4 | 5 | :class:`Timer` -- timer handle 6 | ============================== 7 | 8 | .. autoclass:: uv.Timer 9 | :members: 10 | :member-order: bysource 11 | -------------------------------------------------------------------------------- /doc/source/handles/tty.rst: -------------------------------------------------------------------------------- 1 | .. _TTY: 2 | 3 | .. currentmodule:: uv 4 | 5 | :class:`TTY` -- TTY handle 6 | ========================== 7 | 8 | .. autoclass:: uv.TTY 9 | :members: 10 | :member-order: bysource 11 | 12 | .. autoclass:: uv.TTYMode 13 | :members: 14 | :member-order: bysource 15 | 16 | .. autoclass:: uv.ConsoleSize 17 | :members: 18 | :member-order: bysource 19 | 20 | .. autofunction:: uv.tty.reset_mode -------------------------------------------------------------------------------- /doc/source/handles/udp.rst: -------------------------------------------------------------------------------- 1 | .. _UDP: 2 | 3 | .. currentmodule:: uv 4 | 5 | :class:`UDP` -- UDP handle 6 | ========================== 7 | 8 | .. autoclass:: uv.UDP 9 | :members: 10 | :member-order: bysource 11 | 12 | .. autoclass:: uv.UDPFlags 13 | :members: 14 | :member-order: bysource 15 | 16 | .. autoclass:: uv.UDPMembership 17 | :members: 18 | :member-order: bysource 19 | 20 | .. autoclass:: uv.UDPSendRequest 21 | :members: 22 | :member-order: bysource 23 | -------------------------------------------------------------------------------- /doc/source/index.rst: -------------------------------------------------------------------------------- 1 | .. currentmodule:: uv 2 | 3 | Python libuv CFFI Bindings's documentation! 4 | =========================================== 5 | 6 | Contents: 7 | --------- 8 | 9 | .. toctree:: 10 | :maxdepth: 3 11 | :titlesonly: 12 | 13 | error 14 | 15 | loop 16 | 17 | handle 18 | 19 | handles/async 20 | handles/check 21 | handles/idle 22 | handles/pipe 23 | handles/poll 24 | handles/prepare 25 | handles/process 26 | handles/sig 27 | handles/timer 28 | handles/stream 29 | handles/tcp 30 | handles/tty 31 | handles/udp 32 | 33 | handles/fs_event 34 | handles/fs_poll 35 | 36 | request 37 | 38 | dns 39 | 40 | 41 | Indices and tables 42 | ================== 43 | 44 | * :ref:`genindex` 45 | * :ref:`modindex` 46 | * :ref:`search` 47 | 48 | -------------------------------------------------------------------------------- /doc/source/loop.rst: -------------------------------------------------------------------------------- 1 | .. _Loop: 2 | 3 | .. currentmodule:: uv 4 | 5 | Loop -- event loop 6 | ================== 7 | 8 | .. autoclass:: uv.Loop 9 | :members: 10 | :member-order: bysource 11 | 12 | .. autoclass:: uv.RunModes 13 | :members: 14 | :member-order: bysource 15 | 16 | .. autoclass:: uv.loop.Allocator 17 | :members: 18 | :member-order: bysource 19 | 20 | .. autoclass:: uv.loop.DefaultAllocator 21 | :members: 22 | :member-order: bysource 23 | -------------------------------------------------------------------------------- /doc/source/request.rst: -------------------------------------------------------------------------------- 1 | .. _Request: 2 | 3 | .. currentmodule:: uv 4 | 5 | Request -- request base classes 6 | =============================== 7 | 8 | .. autoclass:: uv.Request 9 | :members: 10 | :member-order: bysource 11 | 12 | .. autoclass:: uv.UVRequest 13 | :members: 14 | :member-order: bysource 15 | -------------------------------------------------------------------------------- /examples/benchmark_echo.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | 16 | import uv 17 | 18 | 19 | def on_shutdown(request, _): 20 | request.stream.close() 21 | 22 | 23 | def on_read(stream, status, data): 24 | if status is not uv.StatusCodes.SUCCESS: stream.close() 25 | if data: stream.write(data) 26 | 27 | 28 | def on_connection(server, _): 29 | connection = server.accept() 30 | connection.read_start(on_read=on_read) 31 | 32 | 33 | def on_quit(sigint, _): 34 | sigint.loop.close_all_handles() 35 | 36 | 37 | def main(): 38 | loop = uv.Loop.get_current() 39 | 40 | server = uv.TCP() 41 | server.bind(('0.0.0.0', 4444)) 42 | server.listen(20, on_connection=on_connection) 43 | 44 | sigint = uv.Signal() 45 | sigint.start(uv.Signals.SIGINT, on_signal=on_quit) 46 | 47 | loop.run() 48 | 49 | 50 | if __name__ == '__main__': 51 | main() 52 | -------------------------------------------------------------------------------- /examples/benchmark_http.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | 16 | import uv 17 | 18 | RESPONSE = (b'HTTP/1.1 200 OK\r\n' 19 | b'Content-Type: text/plain\r\n' 20 | b'Content-Length: 13\r\n\r\n' 21 | b'Hello World!\n') 22 | 23 | 24 | def on_shutdown(request, _): 25 | request.stream.close() 26 | 27 | 28 | def on_read(stream, status, data): 29 | if status != uv.StatusCodes.SUCCESS: 30 | stream.close() 31 | data = data.strip() 32 | if not data: 33 | return 34 | stream.write(RESPONSE) 35 | stream.shutdown(on_shutdown) 36 | 37 | 38 | def on_connection(server, _): 39 | connection = server.accept() 40 | connection.read_start(on_read=on_read) 41 | 42 | 43 | def on_quit(sigint, _): 44 | sigint.loop.close_all_handles() 45 | 46 | 47 | def main(): 48 | loop = uv.Loop.get_current() 49 | 50 | server = uv.TCP() 51 | server.bind(('0.0.0.0', 4444)) 52 | server.listen(1000, on_connection=on_connection) 53 | 54 | sigint = uv.Signal() 55 | sigint.start(uv.Signals.SIGINT, on_signal=on_quit) 56 | 57 | loop.run() 58 | 59 | 60 | if __name__ == '__main__': 61 | main() 62 | -------------------------------------------------------------------------------- /tests/common.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | 16 | from __future__ import print_function, unicode_literals, division, absolute_import 17 | 18 | import contextlib 19 | import os 20 | import os.path 21 | import platform 22 | import sys 23 | import unittest 24 | 25 | import uv 26 | 27 | 28 | __dir__ = os.path.dirname(__file__) 29 | 30 | 31 | def resolve_path(relative_path): 32 | return os.path.join(__dir__, 'data', relative_path) 33 | 34 | 35 | PY2_RERAISE = ''' 36 | def reraise(exc_type, exc_value, exc_traceback): 37 | raise exc_type, exc_value, exc_traceback 38 | ''' 39 | 40 | if uv.common.is_py2: 41 | exec(PY2_RERAISE) 42 | else: 43 | def reraise(_, exc_value, exc_traceback): 44 | raise exc_value.with_traceback(exc_traceback) 45 | 46 | 47 | if uv.common.is_win32: 48 | TEST_PIPE1 = r'\\?\pipe\python-uv-test1' 49 | TEST_PIPE2 = r'\\?\pipe\python-uv-test2' 50 | else: 51 | TEST_PIPE1 = '/tmp/python-uv-test1' 52 | TEST_PIPE2 = '/tmp/python-uv-test2' 53 | 54 | BAD_PIPE = '/path/to/unix/socket/that/really/should/not/be/there' 55 | 56 | TEST_IPV4 = '127.0.0.1' 57 | TEST_IPV6 = '::1' 58 | 59 | TEST_PORT1 = 12345 60 | TEST_PORT2 = 12346 61 | 62 | for pipe_name in (TEST_PIPE1, TEST_PIPE2): 63 | try: 64 | os.remove(pipe_name) 65 | except Exception: 66 | pass 67 | 68 | 69 | def skip_interpreter(*implementations): 70 | def decorator(obj): 71 | return obj 72 | if platform.python_implementation().lower() in implementations: 73 | return unittest.skip('test is not available on the current interpreter') 74 | return decorator 75 | 76 | 77 | sys_platform = 'linux' if sys.platform.startswith('linux') else sys.platform 78 | 79 | 80 | def skip_platform(*platforms): 81 | def decorator(obj): 82 | return obj 83 | if sys_platform in platforms: 84 | return unittest.skip('test is not available on the current platform') 85 | return decorator 86 | 87 | 88 | def skip_pypy(lt_version): 89 | def decorator(obj): 90 | return obj 91 | if not uv.common.is_pypy: 92 | return decorator 93 | if sys.pypy_version_info < lt_version: 94 | return unittest.skip('test is not available on the current pypy version') 95 | return decorator 96 | 97 | 98 | class TestLoop(uv.Loop): 99 | def __init__(self): 100 | super(TestLoop, self).__init__() 101 | 102 | def run(self, mode=uv.RunModes.DEFAULT): 103 | self.exc_type = None 104 | result = super(TestLoop, self).run(mode) 105 | if self.exc_type is not None: 106 | reraise(self.exc_type, self.exc_value, self.exc_traceback) 107 | return result 108 | 109 | 110 | class TestCase(unittest.TestCase): 111 | def setUp(self): 112 | self.loop = TestLoop() 113 | self.set_up() 114 | 115 | def tearDown(self): 116 | self.tear_down() 117 | if not self.loop.closed: 118 | self.loop.close_all_handles(uv.common.dummy_callback) 119 | self.loop.run() 120 | self.loop.close() 121 | 122 | def set_up(self): 123 | pass 124 | 125 | def tear_down(self): 126 | pass 127 | 128 | @contextlib.contextmanager 129 | def should_raise(self, expected): 130 | try: 131 | yield 132 | except Exception as exception: 133 | if not isinstance(exception, expected): 134 | import sys 135 | exc_type, exc_value, exc_traceback = sys.exc_info() 136 | reraise(exc_type, exc_value, exc_traceback) 137 | else: 138 | msg = 'exception %s should have been raised' % str(expected) 139 | self.assert_false(True, msg=msg) 140 | 141 | assert_true = unittest.TestCase.assertTrue 142 | assert_false = unittest.TestCase.assertFalse 143 | 144 | assert_raises = unittest.TestCase.assertRaises 145 | 146 | assert_equal = unittest.TestCase.assertEqual 147 | assert_not_equal = unittest.TestCase.assertNotEqual 148 | assert_greater = unittest.TestCase.assertGreater 149 | assert_greater_equal = unittest.TestCase.assertGreaterEqual 150 | assert_less = unittest.TestCase.assertLess 151 | assert_less_equal = unittest.TestCase.assertLessEqual 152 | 153 | assert_in = unittest.TestCase.assertIn 154 | 155 | assert_is = unittest.TestCase.assertIs 156 | assert_is_not = unittest.TestCase.assertIsNot 157 | assert_is_instance = unittest.TestCase.assertIsInstance 158 | assert_is_none = unittest.TestCase.assertIsNone 159 | assert_is_not_none = unittest.TestCase.assertIsNotNone 160 | -------------------------------------------------------------------------------- /tests/data/program_dump_env.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from __future__ import absolute_import, division, print_function, unicode_literals 4 | 5 | import os 6 | import json 7 | 8 | env = dict(os.environ) 9 | env['cwd'] = os.getcwd() 10 | print(json.dumps(env)) 11 | -------------------------------------------------------------------------------- /tests/data/program_endless_loop.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from __future__ import absolute_import, division, print_function, unicode_literals 4 | 5 | import time 6 | 7 | while True: 8 | time.sleep(10) 9 | -------------------------------------------------------------------------------- /tests/data/program_hello.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from __future__ import absolute_import, division, print_function, unicode_literals 4 | 5 | import sys 6 | 7 | print('hello') 8 | sys.stdout.flush() 9 | raise SystemExit(1) 10 | -------------------------------------------------------------------------------- /tests/data/server.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIB3TCCAUYCCQDBTWiBYs4NyDANBgkqhkiG9w0BAQsFADAzMQswCQYDVQQGEwJE 3 | RTEQMA4GA1UECAwHR2VybWFueTESMBAGA1UECgwJVW5pdHRlc3RzMB4XDTE2MDIw 4 | OTE2MDE1NFoXDTI2MDIwNjE2MDE1NFowMzELMAkGA1UEBhMCREUxEDAOBgNVBAgM 5 | B0dlcm1hbnkxEjAQBgNVBAoMCVVuaXR0ZXN0czCBnzANBgkqhkiG9w0BAQEFAAOB 6 | jQAwgYkCgYEAueYnat3uJ4M8/8zjocMo0fTiRMbSi0up++PFQn3G/Ul6SiOYPBLA 7 | DMHBiwfNx6AsZo0/uka6KaLt78IK4MrSwc1C3pxyCXIK9z9Wb3MHrPEzsn7lycHn 8 | DCX8IKHBJ/Pv2S8qa7UOuCFyQ/8+D6g6EOCoZ86W1NxcEQ0U8pb8Pj8CAwEAATAN 9 | BgkqhkiG9w0BAQsFAAOBgQA+cQF/5rWc+lzmfnrEETtGLpEroTPYzSYTcwcuWRMA 10 | r2wIWDbuwbH0NyRh6D4K1+wptuKh8N0uc6Z53kUwF16FA5jYUnFf+alI6gJ6/KoZ 11 | N3Pbqwc+4Moa2sqKkugZ1NxhMXTPOZljbfAhIuLOPTsydKqQZLXAKNa+pWbKfk/q 12 | nA== 13 | -----END CERTIFICATE----- 14 | -------------------------------------------------------------------------------- /tests/data/server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIICXQIBAAKBgQC55idq3e4ngzz/zOOhwyjR9OJExtKLS6n748VCfcb9SXpKI5g8 3 | EsAMwcGLB83HoCxmjT+6Rropou3vwgrgytLBzULenHIJcgr3P1Zvcwes8TOyfuXJ 4 | wecMJfwgocEn8+/ZLyprtQ64IXJD/z4PqDoQ4KhnzpbU3FwRDRTylvw+PwIDAQAB 5 | AoGAOI1klmUXSxvolaTd5tr48w49O1EEwe1ts7wxyS18OJd+xUkYycWmePsEhugD 6 | 2OPIPhZgOanEfmAbZCGyr3nkLR4pFFirqUy7Xn6oVavHhzXVGi0bq+LyDTIOBiHb 7 | JkNja2NKyVao8XVkBd0uhMA9LnJ1S7XEDYcXuj2wzInSXBECQQDcNNxJP+8ZCTvb 8 | RoLfvtvl912deTxe+4v9mLdIutQFWwtZ4aj2qJHKiEfMfjVKaqqSA0L3z4ehQJCn 9 | qWAcZR4XAkEA2B202VOxpEGLzceg0LKfOy2KDNbsMT20DrerCoBzqqjMqxnijoAj 10 | JpogjALk0lnWJvIbfYcR60MivtPl21TiGQJBAMqcKIONO+WBRX3AJvLPnWy+aXhW 11 | 8GFEec3gEePtYt96obsVkm1BNMNxGP7isQ3DjyXBsoBohpar3W0mvrPhpgkCQCwV 12 | +0NuIEnKmTNUKskH9xWCV6wA19O/q5BpBToyM+Y3YvDwadf56bHBNSx6lqUWcGon 13 | b1YvDIe6fUcCQNx6N1kCQQDH6tAzRDA1T9Hhkeo9gF/VvA0UFc5tQpnUgxB782Ug 14 | tZAuTJNzRjCPpNylFxLwBsUTQH/f/5CPwuX/w+4aytGp 15 | -----END RSA PRIVATE KEY----- 16 | -------------------------------------------------------------------------------- /tests/test_abstract.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | 16 | from __future__ import print_function, unicode_literals, division, absolute_import 17 | 18 | from common import TestCase 19 | 20 | import uv 21 | 22 | 23 | class TestAbstract(TestCase): 24 | def test_subclasses(self): 25 | self.assert_true(issubclass(uv.UVHandle, uv.Handle)) 26 | self.assert_true(issubclass(uv.Async, uv.Handle)) 27 | self.assert_true(issubclass(uv.UVStream, uv.Stream)) 28 | self.assert_true(issubclass(uv.Pipe, uv.Stream)) 29 | -------------------------------------------------------------------------------- /tests/test_async.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | 16 | from __future__ import print_function, unicode_literals, division, absolute_import 17 | 18 | import threading 19 | 20 | from common import TestCase 21 | 22 | import uv 23 | 24 | 25 | class TestAsync(TestCase): 26 | def test_async(self): 27 | def thread(): 28 | while True: 29 | with self.lock: 30 | if self.async_callback_called == 3: 31 | break 32 | self.async.send() 33 | 34 | def on_closed(_): 35 | self.close_callback_called += 1 36 | 37 | def on_wakeup(async): 38 | with self.lock: 39 | self.async_callback_called += 1 40 | if self.async_callback_called == 3: 41 | async.close(on_closed=on_closed) 42 | self.assert_equal(self.loop_thread, threading.current_thread) 43 | 44 | def on_prepare(prepare): 45 | threading.Thread(target=thread).start() 46 | prepare.close(on_closed=on_closed) 47 | 48 | self.async_callback_called = 0 49 | self.close_callback_called = 0 50 | self.lock = threading.RLock() 51 | 52 | self.loop_thread = threading.current_thread 53 | 54 | self.async = uv.Async(on_wakeup=on_wakeup) 55 | 56 | self.prepare = uv.Prepare(on_prepare=on_prepare) 57 | self.prepare.start() 58 | 59 | self.loop.run() 60 | 61 | self.assert_equal(self.async_callback_called, 3) 62 | self.assert_equal(self.close_callback_called, 2) 63 | 64 | def test_closed(self): 65 | self.async = uv.Async() 66 | self.async.close() 67 | 68 | self.assert_raises(uv.ClosedHandleError, self.async.send) 69 | -------------------------------------------------------------------------------- /tests/test_check.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | 16 | from __future__ import print_function, unicode_literals, division, absolute_import 17 | 18 | from common import TestCase 19 | 20 | import uv 21 | 22 | 23 | class TestCheck(TestCase): 24 | def test_check(self): 25 | def on_check(check): 26 | self.on_check_called += 1 27 | if self.on_timeout_called > 5: check.close() 28 | 29 | def on_timeout(timer): 30 | self.on_timeout_called += 1 31 | if self.on_timeout_called > 5: timer.close() 32 | 33 | self.on_check_called = 0 34 | self.on_timeout_called = 0 35 | 36 | self.check = uv.Check(on_check=on_check) 37 | self.check.start() 38 | 39 | self.timer = uv.Timer(on_timeout=on_timeout) 40 | self.timer.start(5, repeat=5) 41 | 42 | self.loop.run() 43 | 44 | self.assert_less_equal(self.on_timeout_called, self.on_check_called) 45 | 46 | def test_check_stop(self): 47 | self.check = uv.Check() 48 | self.check.start() 49 | self.check.stop() 50 | self.loop.run() 51 | 52 | def test_closed(self): 53 | self.check = uv.Check() 54 | self.check.close() 55 | 56 | self.assert_raises(uv.ClosedHandleError, self.check.start) 57 | self.assert_is(self.check.stop(), None) 58 | -------------------------------------------------------------------------------- /tests/test_dns.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | 16 | from __future__ import print_function, unicode_literals, division, absolute_import 17 | 18 | import common 19 | 20 | import uv 21 | 22 | 23 | class TestDNS(common.TestCase): 24 | def test_dns_sync(self): 25 | self.assert_true(uv.getaddrinfo('localhost', 80)) 26 | self.assert_equal(uv.getnameinfo('127.0.0.1', 80).service, 'http') 27 | 28 | def test_dns_async(self): 29 | def got_addrinfo(request, code, addrinfo): 30 | self.assert_true(addrinfo) 31 | 32 | def got_nameinfo(request, code, hostname, service): 33 | self.assert_equal(service, 'http') 34 | 35 | uv.getaddrinfo('localhost', 80, callback=got_addrinfo) 36 | uv.getnameinfo('127.0.0.1', 80, callback=got_nameinfo) 37 | 38 | self.loop.run() 39 | 40 | def test_structures(self): 41 | address6 = uv.Address6(common.TEST_IPV6, common.TEST_PORT1, 42, 442) 42 | self.assert_equal(address6.host, common.TEST_IPV6) 43 | self.assert_equal(address6.port, common.TEST_PORT1) 44 | self.assert_equal(address6.flowinfo, 42) 45 | self.assert_equal(address6.scope_id, 442) 46 | 47 | nameinfo = uv.NameInfo('localhost', 'http') 48 | self.assert_equal(nameinfo.hostname, 'localhost') 49 | self.assert_equal(nameinfo.service, 'http') 50 | 51 | addrinfo = uv.AddrInfo(0, 1, 2, None, address6) 52 | self.assert_equal(addrinfo.family, 0) 53 | self.assert_equal(addrinfo.socktype, 1) 54 | self.assert_equal(addrinfo.protocol, 2) 55 | self.assert_is(addrinfo.canonname, None) 56 | self.assert_equal(addrinfo.address, address6) 57 | -------------------------------------------------------------------------------- /tests/test_error.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | 16 | from __future__ import print_function, unicode_literals, division, absolute_import 17 | 18 | import errno 19 | 20 | import common 21 | 22 | import uv 23 | 24 | 25 | class TestError(common.TestCase): 26 | def test_code_get(self): 27 | self.assert_equal(uv.StatusCodes.get(0), uv.StatusCodes.SUCCESS) 28 | for code in uv.StatusCodes: 29 | self.assert_equal(uv.StatusCodes.get(code), int(code)) 30 | self.assert_equal(uv.StatusCodes.get(42), 42) 31 | eagain_exception = uv.StatusCodes.EAGAIN.exception 32 | self.assert_is(eagain_exception, uv.error.TemporaryUnavailableError) 33 | 34 | def test_raise(self): 35 | def test1(): 36 | raise uv.UVError(int(uv.StatusCodes.EAGAIN)) 37 | 38 | def test2(): 39 | raise uv.UVError(42) 40 | 41 | self.assert_raises(uv.error.TemporaryUnavailableError, test1) 42 | self.assert_raises(uv.UVError, test2) 43 | 44 | def test_unknown(self): 45 | self.assert_equal(uv.UVError(42).name, 'UNKNOWN') 46 | 47 | def test_name(self): 48 | self.assert_equal(uv.UVError(int(uv.StatusCodes.EAGAIN)).name, 'EAGAIN') 49 | 50 | def test_os_error_number(self): 51 | status_code = uv.StatusCodes.from_error_number(errno.EAGAIN) 52 | self.assert_is(status_code, uv.StatusCodes.EAGAIN) 53 | -------------------------------------------------------------------------------- /tests/test_fs_event.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | 16 | from __future__ import print_function, unicode_literals, division, absolute_import 17 | 18 | import os 19 | import os.path 20 | import tempfile 21 | 22 | import common 23 | 24 | import uv 25 | 26 | 27 | @common.skip_platform('win32') 28 | class TestFSEvent(common.TestCase): 29 | def test_fs_event_change(self): 30 | def on_event(fs_event, status, name, events): 31 | self.assert_equal(status, uv.error.StatusCodes.SUCCESS) 32 | self.assert_equal(name, os.path.basename(self.temp_file.name)) 33 | self.assert_equal(events, uv.FSEvents.CHANGE) 34 | fs_event.close() 35 | 36 | def on_timeout(timer): 37 | self.temp_file.write(b'x') 38 | self.temp_file.flush() 39 | timer.close() 40 | 41 | self.fs_event = uv.FSEvent(on_event=on_event) 42 | self.timer = uv.Timer(on_timeout=on_timeout) 43 | 44 | with tempfile.NamedTemporaryFile() as temp_file: 45 | self.temp_file = temp_file 46 | self.fs_event.path = temp_file.name 47 | self.fs_event.start() 48 | self.timer.start(20) 49 | self.loop.run() 50 | 51 | def test_fs_event_rename(self): 52 | def on_event(fs_event, status, name, events): 53 | self.assert_equal(status, uv.error.StatusCodes.SUCCESS) 54 | self.assert_equal(name, os.path.basename(self.temp_file.name)) 55 | self.assert_equal(events, uv.FSEvents.RENAME) 56 | fs_event.close() 57 | 58 | def on_timeout(timer): 59 | os.rename(self.temp_file.name, self.temp_file.name + '-new-name') 60 | timer.close() 61 | 62 | self.fs_event = uv.FSEvent(on_event=on_event) 63 | self.timer = uv.Timer(on_timeout=on_timeout) 64 | 65 | with tempfile.NamedTemporaryFile() as temp_file: 66 | self.temp_file = temp_file 67 | self.fs_event.path = temp_file.name 68 | self.fs_event.start() 69 | self.timer.start(20) 70 | self.loop.run() 71 | os.rename(self.temp_file.name + '-new-name', self.temp_file.name) 72 | 73 | def test_fs_event_stop(self): 74 | self.fs_event = uv.FSEvent() 75 | 76 | with tempfile.NamedTemporaryFile() as temp_file: 77 | self.fs_event.path = temp_file.name 78 | self.fs_event.start() 79 | self.fs_event.stop() 80 | self.loop.run() 81 | 82 | def test_closed(self): 83 | self.fs_event = uv.FSEvent() 84 | self.fs_event.close() 85 | 86 | self.assert_raises(uv.ClosedHandleError, self.fs_event.start) 87 | self.assert_is(self.fs_event.stop(), None) 88 | 89 | def test_path_none(self): 90 | self.fs_event = uv.FSEvent() 91 | 92 | self.assert_raises(uv.error.ArgumentError, self.fs_event.start) 93 | -------------------------------------------------------------------------------- /tests/test_fs_poll.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | 16 | from __future__ import print_function, unicode_literals, division, absolute_import 17 | 18 | import os 19 | import tempfile 20 | 21 | from common import TestCase 22 | 23 | import uv 24 | 25 | U_TIME = (4200, 4200) 26 | 27 | 28 | class TestFSPoll(TestCase): 29 | def test_fs_poll_change(self): 30 | def on_change(fs_poll, status, previous_stat, current_stat): 31 | self.assert_equal(status, uv.error.StatusCodes.SUCCESS) 32 | self.assert_not_equal(previous_stat.mtim, current_stat.mtim) 33 | fs_poll.close() 34 | 35 | def on_timeout(timer): 36 | os.utime(self.temp_file.name, U_TIME) 37 | timer.close() 38 | 39 | self.fs_poll = uv.FSPoll(interval=2000, on_change=on_change) 40 | self.timer = uv.Timer(on_timeout=on_timeout) 41 | 42 | with tempfile.NamedTemporaryFile() as temp_file: 43 | self.temp_file = temp_file 44 | self.fs_poll.path = temp_file.name 45 | self.fs_poll.start() 46 | self.timer.start(1000) 47 | self.loop.run() 48 | 49 | def test_fs_poll_stop(self): 50 | self.fs_poll = uv.FSPoll() 51 | 52 | with tempfile.NamedTemporaryFile() as temp_file: 53 | self.fs_poll.path = temp_file.name 54 | self.fs_poll.start() 55 | self.fs_poll.stop() 56 | self.loop.run() 57 | 58 | def test_closed(self): 59 | self.fs_poll = uv.FSPoll() 60 | self.fs_poll.close() 61 | 62 | self.assert_raises(uv.ClosedHandleError, self.fs_poll.start) 63 | self.assert_is(self.fs_poll.stop(), None) 64 | 65 | def test_path_none(self): 66 | self.fs_poll = uv.FSPoll() 67 | 68 | self.assert_raises(uv.error.ArgumentError, self.fs_poll.start) 69 | -------------------------------------------------------------------------------- /tests/test_gc.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | 16 | from __future__ import print_function, unicode_literals, division, absolute_import 17 | 18 | import gc 19 | import weakref 20 | 21 | import common 22 | 23 | import uv 24 | 25 | 26 | @common.skip_interpreter('pypy') 27 | class TestGC(common.TestCase): 28 | def set_up(self): 29 | gc.disable() 30 | gc.collect() 31 | 32 | def tear_down(self): 33 | gc.enable() 34 | 35 | def test_gc_loop(self): 36 | loop = uv.Loop() 37 | weak_loop = weakref.ref(loop) 38 | base_loop = weakref.ref(loop.base_loop) 39 | del loop 40 | gc.collect() 41 | self.assert_not_equal(weak_loop(), None) 42 | self.assert_not_equal(base_loop(), None) 43 | uv.Loop._thread_locals.loop = None 44 | gc.collect() 45 | self.assert_equal(weak_loop(), None) 46 | self.assert_equal(base_loop(), None) 47 | 48 | def test_gc_handle(self): 49 | weak_handle = weakref.ref(uv.Prepare()) 50 | gc.collect() 51 | self.assert_equal(weak_handle(), None) 52 | prepare = uv.Prepare() 53 | prepare.start() 54 | weak_handle = weakref.ref(prepare) 55 | del prepare 56 | gc.collect() 57 | self.assert_not_equal(weak_handle(), None) 58 | 59 | def test_gc_pending(self): 60 | loop = uv.Loop() 61 | client = uv.Pipe(loop=loop) 62 | client.connect(common.TEST_PIPE1) 63 | client.write(b'hello') 64 | 65 | weak_client = weakref.ref(client) 66 | 67 | self.loop.make_current() 68 | del loop 69 | del client 70 | 71 | gc.collect() 72 | 73 | self.assert_is(weak_client(), None) 74 | 75 | def test_gc_loop_close(self): 76 | client = uv.Pipe() 77 | client.connect(common.TEST_PIPE1).clear_pending() 78 | client.clear_pending() 79 | 80 | weak_client = weakref.ref(client) 81 | 82 | del client 83 | 84 | gc.collect() 85 | 86 | self.assert_is(weak_client(), None) 87 | 88 | self.loop.close() 89 | -------------------------------------------------------------------------------- /tests/test_handle.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | 16 | from __future__ import print_function, unicode_literals, division, absolute_import 17 | 18 | import common 19 | 20 | import uv 21 | 22 | 23 | class TestHandle(common.TestCase): 24 | def test_double_close(self): 25 | self.prepare = uv.Prepare() 26 | self.prepare.close() 27 | self.prepare.close() 28 | 29 | def test_active(self): 30 | self.async = uv.Async() 31 | self.assert_true(self.async.active) 32 | 33 | def test_referencing(self): 34 | self.async = uv.Async() 35 | self.assert_true(self.async.referenced) 36 | self.async.referenced = False 37 | self.assert_false(self.async.referenced) 38 | self.async.reference() 39 | self.assert_true(self.async.referenced) 40 | self.async.dereference() 41 | self.assert_false(self.async.referenced) 42 | 43 | def test_buffer_size(self): 44 | self.tcp = uv.TCP() 45 | self.tcp.bind((common.TEST_IPV4, common.TEST_PORT1)) 46 | self.tcp.send_buffer_size = 262144 47 | self.assert_equal(self.tcp.send_buffer_size, 262144) 48 | self.tcp.receive_buffer_size = 262144 49 | self.assert_equal(self.tcp.receive_buffer_size, 262144) 50 | 51 | def test_fileno(self): 52 | self.tcp = uv.TCP() 53 | self.tcp.bind((common.TEST_IPV4, common.TEST_PORT1)) 54 | self.assert_is_instance(self.tcp.fileno(), int) 55 | 56 | def test_closed(self): 57 | self.tcp = uv.TCP() 58 | self.tcp.close() 59 | self.assert_true(self.tcp.closing) 60 | with self.should_raise(uv.ClosedHandleError): 61 | self.tcp.referenced = True 62 | with self.should_raise(uv.ClosedHandleError): 63 | self.tcp.referenced = False 64 | with self.should_raise(uv.ClosedHandleError): 65 | send_buffer_size = self.tcp.send_buffer_size 66 | with self.should_raise(uv.ClosedHandleError): 67 | self.tcp.send_buffer_size = 42 68 | with self.should_raise(uv.ClosedHandleError): 69 | receive_buffer_size = self.tcp.receive_buffer_size 70 | with self.should_raise(uv.ClosedHandleError): 71 | self.tcp.receive_buffer_size = 42 72 | self.assert_raises(uv.ClosedHandleError, self.tcp.fileno) 73 | self.assert_is(self.tcp.close(), None) 74 | self.loop.run() 75 | self.assert_true(self.tcp.closed) 76 | self.assert_false(self.tcp.active) 77 | self.assert_false(self.tcp.referenced) 78 | -------------------------------------------------------------------------------- /tests/test_idle.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | 16 | from __future__ import print_function, unicode_literals, division, absolute_import 17 | 18 | from common import TestCase 19 | 20 | import uv 21 | 22 | 23 | class TestIdle(TestCase): 24 | def test_idle(self): 25 | def on_idle(idle): 26 | self.on_idle_called += 1 27 | if self.on_idle_called > 5: 28 | idle.stop() 29 | 30 | self.on_idle_called = 0 31 | 32 | self.idle = uv.Idle(on_idle=on_idle) 33 | self.idle.start() 34 | 35 | self.loop.run() 36 | 37 | self.assert_equal(self.on_idle_called, 6) 38 | 39 | def test_closed(self): 40 | self.idle = uv.Idle() 41 | self.idle.close() 42 | 43 | self.assert_raises(uv.ClosedHandleError, self.idle.start) 44 | self.assert_is(self.idle.stop(), None) 45 | -------------------------------------------------------------------------------- /tests/test_misc.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | 16 | from __future__ import print_function, unicode_literals, division, absolute_import 17 | 18 | import common 19 | 20 | import uv 21 | 22 | 23 | class TestMisc(common.TestCase): 24 | def test_interface_addresses(self): 25 | self.assert_true(bool(uv.misc.interface_addresses())) 26 | -------------------------------------------------------------------------------- /tests/test_ping_pong.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | 16 | from __future__ import print_function, unicode_literals, division, absolute_import 17 | 18 | import common 19 | 20 | import uv 21 | 22 | 23 | class TestPingPong(common.TestCase): 24 | def on_shutdown(self, request, status): 25 | request.stream.close() 26 | 27 | def on_read(self, stream, status, data): 28 | if status is not uv.StatusCodes.SUCCESS: 29 | stream.shutdown(on_shutdown=self.on_shutdown) 30 | return 31 | self.buffer.append(data) 32 | if b''.join(self.buffer) == b'PING': 33 | del self.buffer[:] 34 | self.pong_counter += 1 35 | self.assert_is(stream, self.client) 36 | stream.write(b'PONG') 37 | elif b''.join(self.buffer) == b'PONG': 38 | del self.buffer[:] 39 | if self.ping_counter < 5: 40 | stream.write(b'PING') 41 | self.ping_counter += 1 42 | else: 43 | stream.shutdown(on_shutdown=self.on_shutdown) 44 | self.server.close() 45 | 46 | def on_connect(self, request, status): 47 | self.assert_equal(status, uv.StatusCodes.SUCCESS) 48 | request.stream.read_start(on_read=self.on_read) 49 | 50 | def on_connection(self, stream, status): 51 | self.assert_equal(status, uv.StatusCodes.SUCCESS) 52 | connection = stream.accept() 53 | connection.read_start(on_read=self.on_read) 54 | connection.write(b'PING') 55 | 56 | def set_up(self): 57 | self.buffer = [] 58 | 59 | self.ping_counter = 1 60 | self.pong_counter = 0 61 | 62 | def run_ping_pong(self): 63 | self.loop.run() 64 | 65 | self.assert_equal(self.ping_counter, 5) 66 | self.assert_equal(self.pong_counter, 5) 67 | 68 | def test_pipe(self): 69 | self.server = uv.Pipe() 70 | self.server.pending_instances(100) 71 | self.server.bind(common.TEST_PIPE1) 72 | self.server.listen(5, on_connection=self.on_connection) 73 | 74 | self.client = uv.Pipe() 75 | self.client.connect(common.TEST_PIPE1, on_connect=self.on_connect) 76 | 77 | self.run_ping_pong() 78 | 79 | def test_tcp_ipv4(self): 80 | address = (common.TEST_IPV4, common.TEST_PORT1) 81 | 82 | self.server = uv.TCP() 83 | self.server.bind(address) 84 | self.server.listen(5, on_connection=self.on_connection) 85 | 86 | self.client = uv.TCP() 87 | self.client.connect(address, on_connect=self.on_connect) 88 | 89 | self.run_ping_pong() 90 | 91 | def test_tcp_ipv6(self): 92 | address = (common.TEST_IPV6, common.TEST_PORT1) 93 | 94 | self.server = uv.TCP() 95 | self.server.bind(address) 96 | self.server.listen(5, on_connection=self.on_connection) 97 | 98 | self.client = uv.TCP() 99 | self.client.connect(address, on_connect=self.on_connect) 100 | 101 | self.run_ping_pong() 102 | 103 | def test_tcp_ipv4_icp_client(self): 104 | address = (common.TEST_IPV4, common.TEST_PORT1) 105 | 106 | self.server = uv.TCP() 107 | self.server.bind(address) 108 | self.server.listen(5, on_connection=self.on_connection) 109 | 110 | client = uv.TCP() 111 | client.connect(address) 112 | 113 | def on_read(pipe_client, status, data): 114 | if pipe_client.pending_count: 115 | self.client = pipe_client.pending_accept() 116 | self.client.read_start(on_read=self.on_read) 117 | pipe_client.close() 118 | 119 | def on_connection(pipe_server, status): 120 | connection = pipe_server.accept(ipc=True) 121 | connection.write(b'hello', send_stream=client) 122 | connection.close() 123 | pipe_server.close() 124 | 125 | self.pipe_server = uv.Pipe() 126 | self.pipe_server.pending_instances(100) 127 | self.pipe_server.bind(common.TEST_PIPE1) 128 | self.pipe_server.listen(on_connection=on_connection) 129 | 130 | self.pipe_client = uv.Pipe(ipc=True) 131 | self.pipe_client.connect(common.TEST_PIPE1) 132 | self.pipe_client.read_start(on_read=on_read) 133 | 134 | self.loop.run() 135 | 136 | self.run_ping_pong() 137 | 138 | client.close() 139 | self.server.close() 140 | self.client.close() 141 | -------------------------------------------------------------------------------- /tests/test_pipe.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | 16 | from __future__ import print_function, unicode_literals, division, absolute_import 17 | 18 | import socket 19 | 20 | import common 21 | 22 | import uv 23 | 24 | 25 | class TestPipe(common.TestCase): 26 | def test_connect_bad(self): 27 | def on_connect(request, status): 28 | self.assert_not_equal(status, uv.StatusCodes.SUCCESS) 29 | request.stream.close() 30 | 31 | self.pipe = uv.Pipe() 32 | self.pipe.connect(common.BAD_PIPE, on_connect=on_connect) 33 | 34 | self.loop.run() 35 | 36 | def test_sockname(self): 37 | self.pipe = uv.Pipe() 38 | self.pipe.bind(common.TEST_PIPE1) 39 | self.assert_equal(self.pipe.sockname, common.TEST_PIPE1) 40 | 41 | def test_peername(self): 42 | def on_connect(request, status): 43 | self.assert_equal(status, uv.StatusCodes.SUCCESS) 44 | self.assert_equal(request.stream.peername, common.TEST_PIPE1) 45 | request.stream.close() 46 | 47 | def on_connection(handle, status): 48 | self.assert_equal(status, uv.StatusCodes.SUCCESS) 49 | handle.close() 50 | 51 | self.pipe1 = uv.Pipe() 52 | self.pipe1.bind(common.TEST_PIPE1) 53 | self.pipe1.listen(on_connection=on_connection) 54 | 55 | self.pipe2 = uv.Pipe() 56 | self.pipe2.connect(common.TEST_PIPE1, on_connect=on_connect) 57 | 58 | self.loop.run() 59 | 60 | def test_no_pending_accept(self): 61 | self.pipe = uv.Pipe() 62 | self.assert_raises(uv.error.ArgumentError, self.pipe.pending_accept) 63 | 64 | def test_closed(self): 65 | self.pipe = uv.Pipe() 66 | self.pipe.close() 67 | 68 | self.assert_raises(uv.error.ClosedHandleError, self.pipe.open, 0) 69 | self.assert_equal(self.pipe.pending_count, 0) 70 | self.assert_equal(self.pipe.pending_type, None) 71 | self.assert_raises(uv.error.ClosedHandleError, self.pipe.pending_accept) 72 | self.assert_raises(uv.error.ClosedHandleError, self.pipe.pending_instances, 100) 73 | with self.should_raise(uv.error.ClosedHandleError): 74 | sockname = self.pipe.sockname 75 | with self.should_raise(uv.error.ClosedHandleError): 76 | peername = self.pipe.peername 77 | self.assert_raises(uv.error.ClosedHandleError, self.pipe.bind, '') 78 | self.assert_raises(uv.error.ClosedHandleError, self.pipe.connect, '') 79 | 80 | def test_family(self): 81 | self.pipe = uv.Pipe() 82 | if uv.common.is_win32: 83 | self.assert_is(self.pipe.family, None) 84 | else: 85 | self.assert_is(self.pipe.family, socket.AF_UNIX) 86 | 87 | @common.skip_platform('win32') 88 | def test_pipe_open(self): 89 | unix_socket = socket.socket(family=socket.AF_UNIX) 90 | self.pipe = uv.Pipe() 91 | self.pipe.open(unix_socket.fileno()) 92 | self.assert_equal(self.pipe.fileno(), unix_socket.fileno()) 93 | -------------------------------------------------------------------------------- /tests/test_poll.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | 16 | from __future__ import print_function, unicode_literals, division, absolute_import 17 | 18 | import socket 19 | 20 | import common 21 | 22 | import uv 23 | 24 | 25 | class TestPoll(common.TestCase): 26 | def test_poll(self): 27 | def on_shutdown(shutdown_request, _): 28 | """ 29 | :type shutdown_request: 30 | uv.ShutdownRequest 31 | """ 32 | shutdown_request.stream.close() 33 | 34 | def on_connection(server, status): 35 | """ 36 | :type server: 37 | uv.TCP 38 | """ 39 | connection = server.accept() 40 | connection.write(b'hello') 41 | connection.shutdown(on_shutdown=on_shutdown) 42 | server.close() 43 | 44 | self.buffer = b'' 45 | 46 | def on_event(poll_handle, status, events): 47 | if status == uv.StatusCodes.SUCCESS: 48 | self.buffer = self.client.recv(1024) 49 | if self.buffer.startswith(b'hello') or status != uv.StatusCodes.SUCCESS: 50 | poll_handle.stop() 51 | 52 | self.tcp = uv.TCP() 53 | self.tcp.bind((common.TEST_IPV4, common.TEST_PORT1)) 54 | self.tcp.listen(on_connection=on_connection) 55 | 56 | self.client = socket.socket() 57 | self.client.connect((common.TEST_IPV4, common.TEST_PORT1)) 58 | 59 | self.poll = uv.Poll(self.client.fileno(), on_event=on_event) 60 | self.poll.start() 61 | 62 | self.assert_equal(self.poll.fileno(), self.client.fileno()) 63 | 64 | self.loop.run() 65 | 66 | self.assert_equal(self.buffer, b'hello') 67 | 68 | def test_closed(self): 69 | self.client = socket.socket() 70 | self.poll = uv.Poll(self.client.fileno()) 71 | self.poll.close() 72 | 73 | self.assert_raises(uv.ClosedHandleError, self.poll.start) 74 | self.assert_is(self.poll.stop(), None) 75 | -------------------------------------------------------------------------------- /tests/test_prepare.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | 16 | from __future__ import print_function, unicode_literals, division, absolute_import 17 | 18 | from common import TestCase 19 | 20 | import uv 21 | 22 | 23 | class TestPrepare(TestCase): 24 | def test_prepare(self): 25 | self.on_prepare_called = 0 26 | 27 | def on_prepare(prepare_handle): 28 | self.on_prepare_called += 1 29 | prepare_handle.stop() 30 | 31 | self.prepare = uv.Prepare(on_prepare=on_prepare) 32 | self.prepare.start() 33 | 34 | self.loop.run() 35 | 36 | self.assert_equal(self.on_prepare_called, 1) 37 | 38 | def test_closed(self): 39 | self.prepare = uv.Prepare() 40 | self.prepare.close() 41 | 42 | self.assert_raises(uv.ClosedHandleError, self.prepare.start) 43 | self.assert_is(self.prepare.stop(), None) 44 | -------------------------------------------------------------------------------- /tests/test_process.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | 16 | from __future__ import print_function, unicode_literals, division, absolute_import 17 | 18 | import json 19 | import sys 20 | 21 | import common 22 | 23 | import uv 24 | 25 | 26 | PROGRAM_HELLO = common.resolve_path('program_hello.py') 27 | PROGRAM_ENDLESS_LOOP = common.resolve_path('program_endless_loop.py') 28 | PROGRAM_DUMP_ENV = common.resolve_path('program_dump_env.py') 29 | 30 | 31 | class TestProcess(common.TestCase): 32 | def test_process_hello(self): 33 | arguments = [sys.executable, PROGRAM_HELLO] 34 | 35 | self.buffer = b'' 36 | self.returncode = None 37 | 38 | def on_exit(process_handle, returncode, term_signal): 39 | self.returncode = returncode 40 | 41 | def on_read(pipe_handle, status, data): 42 | self.buffer += data 43 | 44 | self.pipe = uv.Pipe() 45 | self.pipe.bind(common.TEST_PIPE1) 46 | 47 | self.process = uv.Process(arguments, stdout=uv.PIPE, stdio=[self.pipe], 48 | on_exit=on_exit) 49 | self.process.stdout.read_start(on_read) 50 | 51 | self.loop.run() 52 | 53 | self.assert_equal(self.buffer.strip(), b'hello') 54 | self.assert_equal(self.returncode, 1) 55 | self.assert_not_equal(self.process.pid, None) 56 | self.assert_raises(uv.error.ProcessLookupError, self.process.kill) 57 | 58 | self.process.close() 59 | 60 | self.assert_raises(uv.ClosedHandleError, self.process.kill) 61 | with self.should_raise(uv.ClosedHandleError): 62 | pid = self.process.pid 63 | 64 | def test_process_endless_loop(self): 65 | arguments = [sys.executable, PROGRAM_ENDLESS_LOOP] 66 | 67 | self.returncode = None 68 | self.term_signal = None 69 | 70 | def on_exit(process_handle, returncode, term_signal): 71 | self.returncode = returncode 72 | self.term_signal = term_signal 73 | 74 | def on_prepare(prepare_handle): 75 | prepare_handle.close() 76 | self.process.kill() 77 | 78 | self.process = uv.Process(arguments, on_exit=on_exit) 79 | self.prepare = uv.Prepare(on_prepare=on_prepare) 80 | self.prepare.start() 81 | 82 | self.loop.run() 83 | 84 | self.assert_is_not(self.returncode, None) 85 | self.assert_is_not(self.term_signal, None) 86 | 87 | def test_process_dump_env(self): 88 | arguments = [sys.executable, PROGRAM_DUMP_ENV] 89 | 90 | self.buffer = b'' 91 | self.returncode = None 92 | 93 | def on_exit(process_handle, returncode, term_signal): 94 | self.returncode = returncode 95 | 96 | def on_read(pipe_handle, status, data): 97 | self.buffer += data 98 | 99 | env = {'hello': 'world'} 100 | self.process = uv.Process(arguments, env=env, stdout=uv.PIPE, on_exit=on_exit, 101 | cwd=common.resolve_path('')) 102 | self.process.stdout.read_start(on_read) 103 | 104 | self.loop.run() 105 | 106 | self.assert_equal(self.returncode, 0) 107 | self.assert_not_equal(self.process.pid, None) 108 | 109 | result = json.loads(self.buffer.decode()) 110 | self.assert_equal(result['hello'], 'world') 111 | self.assert_true(result['cwd'].endswith('tests/data')) 112 | 113 | def test_unknown_file(self): 114 | arguments = [sys.executable, PROGRAM_HELLO] 115 | self.assert_raises(uv.error.ArgumentError, uv.Process, arguments, stdout='abc') 116 | -------------------------------------------------------------------------------- /tests/test_request.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | 16 | from __future__ import print_function, unicode_literals, division, absolute_import 17 | 18 | import common 19 | 20 | import uv 21 | 22 | 23 | class TestRequest(common.TestCase): 24 | def test_closed_loop(self): 25 | self.loop.close() 26 | 27 | self.assert_raises(uv.ClosedLoopError, uv.dns.getaddrinfo, 28 | 'localhost', 80, loop=self.loop) 29 | 30 | def test_type(self): 31 | self.pipe = uv.Pipe() 32 | request = self.pipe.connect(common.BAD_PIPE) 33 | self.assert_is(request.type, uv.ConnectRequest) 34 | 35 | def test_cancel(self): 36 | self.tcp = uv.TCP() 37 | self.assert_raises(uv.error.ArgumentError, 38 | self.tcp.connect(('127.0.0.1', 80)).cancel) 39 | 40 | -------------------------------------------------------------------------------- /tests/test_secure.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | 16 | from __future__ import print_function, unicode_literals, division, absolute_import 17 | 18 | import common 19 | 20 | import uv 21 | 22 | 23 | KEYFILE = common.resolve_path('server.key') 24 | CERTFILE = common.resolve_path('server.crt') 25 | 26 | 27 | @common.skip_interpreter('pypy') 28 | class TestSecure(common.TestCase): 29 | def test_secure(self): 30 | def on_client_handshake(handshake_request, status, ssl_error): 31 | import sys 32 | print('client handshake:', status, file=sys.stderr) 33 | self.client.close() 34 | 35 | def on_connect(connect_request, status): 36 | self.client.handshake(on_handshake=on_client_handshake) 37 | import sys 38 | print('client connection:', status, file=sys.stderr) 39 | 40 | def on_server_handshake(handshake_request, status, ssl_error): 41 | import sys 42 | print('handshake server:', status, file=sys.stderr) 43 | self.server.close() 44 | 45 | def on_connection(secure_server, status): 46 | connection = secure_server.accept(keyfile=KEYFILE, certfile=CERTFILE) 47 | connection.handshake(on_handshake=on_server_handshake) 48 | import sys 49 | print('sever connection:', status, file=sys.stderr) 50 | 51 | self.server = uv.secure.Secure() 52 | self.server.bind((common.TEST_IPV4, common.TEST_PORT2)) 53 | self.server.listen(5, on_connection=on_connection) 54 | 55 | self.client = uv.secure.Secure() 56 | self.client.connect((common.TEST_IPV4, common.TEST_PORT2), on_connect=on_connect) 57 | 58 | try: 59 | self.loop.run() 60 | finally: 61 | self.server._socket.close() 62 | self.client._socket.close() 63 | -------------------------------------------------------------------------------- /tests/test_signal.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | 16 | from __future__ import print_function, unicode_literals, division, absolute_import 17 | 18 | import os 19 | import signal 20 | import threading 21 | 22 | import common 23 | 24 | import uv 25 | 26 | 27 | @common.skip_platform('win32') 28 | class TestSignal(common.TestCase): 29 | def test_signal(self): 30 | self.signum = None 31 | 32 | def on_signal(signal_handle, signum): 33 | self.signum = signum 34 | signal_handle.stop() 35 | 36 | self.signal = uv.Signal(on_signal=on_signal) 37 | self.signal.start(signal.SIGUSR1) 38 | 39 | self.assert_equal(self.signal.signum, signal.SIGUSR1) 40 | 41 | thread = threading.Thread(target=self.loop.run) 42 | thread.start() 43 | 44 | os.kill(os.getpid(), signal.SIGUSR1) 45 | 46 | thread.join() 47 | 48 | self.assert_equal(self.signum, signal.SIGUSR1) 49 | 50 | def test_closed(self): 51 | self.signal = uv.Signal() 52 | self.signal.close() 53 | 54 | try: 55 | signum = self.signal.signum 56 | except uv.ClosedHandleError: 57 | pass 58 | else: 59 | self.assert_true(False) 60 | self.assert_raises(uv.ClosedHandleError, self.signal.start, 2) 61 | self.assert_is(self.signal.stop(), None) -------------------------------------------------------------------------------- /tests/test_stream.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | 16 | from __future__ import print_function, unicode_literals, division, absolute_import 17 | 18 | import common 19 | 20 | import uv 21 | 22 | 23 | class TestStream(common.TestCase): 24 | def test_closed(self): 25 | self.pipe = uv.Pipe() 26 | self.pipe.close() 27 | self.assert_false(self.pipe.readable) 28 | self.assert_false(self.pipe.writable) 29 | self.assert_raises(uv.ClosedHandleError, self.pipe.shutdown) 30 | self.assert_raises(uv.ClosedHandleError, self.pipe.listen) 31 | self.assert_raises(uv.ClosedHandleError, self.pipe.read_start) 32 | self.assert_is(self.pipe.read_stop(), None) 33 | self.assert_raises(uv.ClosedHandleError, self.pipe.write, b'') 34 | self.assert_raises(uv.ClosedHandleError, self.pipe.try_write, b'') 35 | self.assert_raises(uv.ClosedHandleError, self.pipe.accept) 36 | 37 | @common.skip_platform('win32') 38 | def test_try_write(self): 39 | self.buffer = b'' 40 | self.bytes_written = 0 41 | 42 | def on_read(connection, status, data): 43 | self.buffer += data 44 | connection.read_stop() 45 | 46 | def on_connection(pipe_handle, status): 47 | connection = pipe_handle.accept() 48 | connection.read_start(on_read=on_read) 49 | pipe_handle.close() 50 | 51 | def on_timeout(timer): 52 | try: 53 | self.bytes_written = self.client.try_write(b'hello') 54 | except uv.error.TemporaryUnavailableError: 55 | self.server.close() 56 | finally: 57 | timer.close() 58 | 59 | self.server = uv.Pipe() 60 | self.server.bind(common.TEST_PIPE1) 61 | self.server.listen(on_connection=on_connection) 62 | 63 | self.client = uv.Pipe() 64 | self.client.connect(common.TEST_PIPE1) 65 | 66 | self.timer = uv.Timer(on_timeout=on_timeout) 67 | self.timer.start(10) 68 | 69 | self.loop.run() 70 | 71 | self.assert_equal(self.buffer, b'hello'[:self.bytes_written]) 72 | 73 | def test_writable_readable(self): 74 | self.pipe = uv.Pipe() 75 | self.assert_false(self.pipe.readable) 76 | self.assert_false(self.pipe.writable) 77 | -------------------------------------------------------------------------------- /tests/test_tcp.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | 16 | from __future__ import print_function, unicode_literals, division, absolute_import 17 | 18 | import socket 19 | 20 | import common 21 | 22 | import uv 23 | 24 | 25 | class TestTCP(common.TestCase): 26 | def test_closed(self): 27 | self.tcp = uv.TCP() 28 | self.tcp.close() 29 | 30 | self.assert_raises(uv.ClosedHandleError, self.tcp.open, 0) 31 | self.assert_raises(uv.ClosedHandleError, self.tcp.bind, None, 0) 32 | self.assert_raises(uv.ClosedHandleError, self.tcp.connect, (common.TEST_IPV4, 42)) 33 | with self.should_raise(uv.ClosedHandleError): 34 | sockname = self.tcp.sockname 35 | with self.should_raise(uv.ClosedHandleError): 36 | peername = self.tcp.peername 37 | self.assert_raises(uv.ClosedHandleError, self.tcp.set_nodelay, True) 38 | self.assert_raises(uv.ClosedHandleError, self.tcp.set_keepalive, True, 10) 39 | self.assert_raises(uv.ClosedHandleError, self.tcp.set_simultaneous_accepts, True) 40 | 41 | def test_settings(self): 42 | self.tcp = uv.TCP() 43 | self.tcp.set_nodelay(True) 44 | self.tcp.set_keepalive(False, 10) 45 | self.tcp.set_simultaneous_accepts(True) 46 | 47 | def test_open(self): 48 | server = socket.socket() 49 | self.tcp = uv.TCP() 50 | self.tcp.open(server.fileno()) 51 | 52 | def test_family(self): 53 | self.tcp4 = uv.TCP() 54 | self.assert_equal(self.tcp4.family, None) 55 | self.tcp4.bind((common.TEST_IPV4, common.TEST_PORT1)) 56 | self.assert_equal(self.tcp4.family, socket.AF_INET) 57 | 58 | self.tcp6 = uv.TCP() 59 | self.tcp6.bind((common.TEST_IPV6, common.TEST_PORT1)) 60 | self.assert_equal(self.tcp6.family, socket.AF_INET6) 61 | 62 | def test_sockname_peername(self): 63 | address = (common.TEST_IPV4, common.TEST_PORT1) 64 | 65 | def on_connection(server, status): 66 | server.close() 67 | 68 | def on_connect(request, status): 69 | self.assert_equal(request.stream.peername, address) 70 | request.stream.close() 71 | 72 | self.server = uv.TCP() 73 | self.server.bind(address) 74 | self.server.listen(on_connection=on_connection) 75 | self.assert_equal(self.server.sockname, address) 76 | 77 | self.client = uv.TCP() 78 | self.client.connect(address, on_connect=on_connect) 79 | 80 | self.loop.run() 81 | -------------------------------------------------------------------------------- /tests/test_timer.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | 16 | from common import TestCase 17 | 18 | import uv 19 | 20 | 21 | class TestTimer(TestCase): 22 | def test_timer_simple(self): 23 | self.timer_called = 0 24 | 25 | def on_timeout(_): 26 | self.timer_called += 1 27 | 28 | timer = uv.Timer(on_timeout=on_timeout) 29 | timer.start(50) 30 | 31 | self.loop.run() 32 | 33 | self.assert_equal(self.timer_called, 1) 34 | 35 | def test_timer_repeat(self): 36 | self.timer_called = 0 37 | 38 | def on_timeout(t): 39 | self.timer_called += 1 40 | if self.timer_called == 3: t.close() 41 | 42 | timer = uv.Timer(on_timeout=on_timeout) 43 | timer.start(50, repeat=50) 44 | 45 | self.loop.run() 46 | 47 | self.assert_equal(self.timer_called, 3) 48 | 49 | def test_timer_close(self): 50 | self.timer_called = 0 51 | 52 | def on_timeout(_): 53 | self.timer_called += 1 54 | 55 | timer = uv.Timer(on_timeout=on_timeout) 56 | timer.start(50) 57 | timer.close() 58 | 59 | self.loop.run() 60 | 61 | self.assert_equal(self.timer_called, 0) 62 | 63 | def test_timer_reference(self): 64 | self.timer_called = 0 65 | 66 | def on_timeout(_): 67 | self.timer_called += 1 68 | 69 | timer = uv.Timer(on_timeout=on_timeout) 70 | timer.start(50) 71 | timer.dereference() 72 | 73 | self.loop.run() 74 | 75 | self.assert_false(timer.referenced) 76 | self.assert_equal(self.timer_called, 0) 77 | 78 | timer.reference() 79 | 80 | self.loop.run() 81 | 82 | self.assert_true(timer.referenced) 83 | self.assert_equal(self.timer_called, 1) 84 | 85 | def test_timer_pending(self): 86 | self.timer_called = 0 87 | 88 | def on_timeout(timer_handle): 89 | if self.timer_called == 0: 90 | self.assert_false(self.loop.structure_is_pending(timer_handle)) 91 | timer_handle.repeat = 5 92 | timer_handle.again() 93 | self.assert_true(self.loop.structure_is_pending(timer_handle)) 94 | self.timer_called += 1 95 | if self.timer_called >= 5: 96 | timer_handle.stop() 97 | self.assert_false(self.loop.structure_is_pending(timer_handle)) 98 | 99 | self.timer = uv.Timer(on_timeout=on_timeout) 100 | self.assert_false(self.loop.structure_is_pending(self.timer)) 101 | self.timer.start(5) 102 | self.assert_true(self.loop.structure_is_pending(self.timer)) 103 | 104 | self.loop.run() 105 | 106 | self.assert_equal(self.timer_called, 5) 107 | 108 | def test_closed(self): 109 | self.timer = uv.Timer() 110 | self.timer.close() 111 | 112 | try: 113 | repeat = self.timer.repeat 114 | except uv.ClosedHandleError: 115 | pass 116 | else: 117 | self.assert_true(False) 118 | try: 119 | self.timer.repeat = 10 120 | except uv.ClosedHandleError: 121 | pass 122 | else: 123 | self.assert_true(False) 124 | self.assert_raises(uv.ClosedHandleError, self.timer.again) 125 | self.assert_raises(uv.ClosedHandleError, self.timer.start, 10) 126 | self.assert_is(self.timer.stop(), None) 127 | -------------------------------------------------------------------------------- /tests/test_tty.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | 16 | from __future__ import print_function, unicode_literals, division, absolute_import 17 | 18 | import sys 19 | 20 | import common 21 | 22 | import uv 23 | 24 | 25 | class TestTTY(common.TestCase): 26 | @common.skip_platform('win32') 27 | def test_tty(self): 28 | try: 29 | self.tty = uv.TTY(sys.stdin.fileno(), True) 30 | self.assert_true(bool(self.tty.console_size)) 31 | width = self.tty.console_size.width 32 | height = self.tty.console_size.height 33 | self.tty.set_mode(uv.TTYMode.NORMAL) 34 | uv.tty.reset_mode() 35 | self.tty.close() 36 | self.assert_equal(self.tty.console_size, (0, 0)) 37 | self.assert_raises(uv.ClosedHandleError, self.tty.set_mode) 38 | except uv.UVError or ValueError: 39 | pass 40 | -------------------------------------------------------------------------------- /tests/test_udp.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | 16 | from __future__ import print_function, unicode_literals, division, absolute_import 17 | 18 | import socket 19 | 20 | import common 21 | 22 | import uv 23 | 24 | 25 | MULTICAST_ADDRESS = '239.255.0.1' 26 | 27 | 28 | def interface_addresses(): 29 | if uv.common.is_win32: 30 | for interface_address in uv.misc.interface_addresses(): 31 | if len(interface_address) > 2: 32 | continue 33 | yield interface_address.address[0] 34 | else: 35 | yield MULTICAST_ADDRESS 36 | 37 | 38 | @common.skip_pypy((4, 0, 0)) 39 | class TestUDP(common.TestCase): 40 | def test_udp(self): 41 | self.datagram = None 42 | 43 | def on_receive(udp_handle, status, address, data, flags): 44 | self.datagram = data 45 | udp_handle.receive_stop() 46 | 47 | server = socket.socket(type=socket.SOCK_DGRAM) 48 | self.server = uv.UDP(on_receive=on_receive) 49 | self.assert_equal(self.server.family, None) 50 | self.server.open(server.fileno()) 51 | self.assert_equal(self.server.family, server.family) 52 | self.server.bind((common.TEST_IPV4, common.TEST_PORT1)) 53 | self.assert_equal(self.server.sockname, (common.TEST_IPV4, common.TEST_PORT1)) 54 | self.server.receive_start() 55 | 56 | self.client = uv.UDP() 57 | self.client.send(b'hello', (common.TEST_IPV4, common.TEST_PORT1)) 58 | 59 | self.loop.run() 60 | 61 | self.assert_equal(self.datagram, b'hello') 62 | 63 | def test_udp_multicast(self): 64 | self.clients = [] 65 | self.results = [] 66 | 67 | def on_receive(client, status, address, data, flags): 68 | self.results.append(data) 69 | client.receive_stop() 70 | 71 | for address in interface_addresses(): 72 | client = uv.UDP(on_receive=on_receive) 73 | client.bind((address, common.TEST_PORT1)) 74 | client.set_membership(MULTICAST_ADDRESS, uv.UDPMembership.JOIN_GROUP) 75 | client.set_multicast_ttl(10) 76 | client.receive_start() 77 | self.clients.append(client) 78 | 79 | self.server = uv.UDP() 80 | self.server.send(b'hello', (MULTICAST_ADDRESS, common.TEST_PORT1)) 81 | 82 | self.loop.run() 83 | 84 | self.assert_equal(self.results, [b'hello'] * len(self.clients)) 85 | 86 | def test_udp_multicast_loop(self): 87 | self.datagram = None 88 | 89 | def on_receive(client, status, address, data, flags): 90 | self.datagram = data 91 | client.receive_stop() 92 | 93 | self.server = uv.UDP(on_receive=on_receive) 94 | self.server.bind(('0.0.0.0', common.TEST_PORT1)) 95 | self.server.set_multicast_interface('0.0.0.0') 96 | self.server.set_membership(MULTICAST_ADDRESS, uv.UDPMembership.JOIN_GROUP) 97 | self.server.set_multicast_loop(True) 98 | self.server.receive_start() 99 | self.server.send(b'hello', (MULTICAST_ADDRESS, common.TEST_PORT1)) 100 | 101 | self.loop.run() 102 | 103 | self.assert_equal(self.datagram, b'hello') 104 | 105 | def test_udp_broadcast(self): 106 | self.datagram = None 107 | 108 | def on_receive(server, status, address, data, flags): 109 | self.datagram = data 110 | server.close() 111 | 112 | self.server = uv.UDP(on_receive=on_receive) 113 | self.server.bind(('0.0.0.0', common.TEST_PORT1)) 114 | self.server.set_broadcast(True) 115 | self.server.receive_start() 116 | 117 | self.client = uv.UDP() 118 | self.client.bind(('0.0.0.0', 0)) 119 | self.client.set_broadcast(True) 120 | self.client.send(b'hello', ('255.255.255.255', common.TEST_PORT1)) 121 | 122 | self.loop.run() 123 | 124 | self.assert_equal(self.datagram, b'hello') 125 | 126 | def test_udp_try_send(self): 127 | self.datagram = None 128 | 129 | def on_receive(udp_handle, status, address, data, flags): 130 | self.datagram = data 131 | udp_handle.receive_stop() 132 | 133 | def on_timeout(timer): 134 | try: 135 | self.client.try_send(b'hello', (common.TEST_IPV4, common.TEST_PORT1)) 136 | except uv.error.TemporaryUnavailableError: 137 | self.server.close() 138 | self.datagram = b'hello' 139 | 140 | self.server = uv.UDP(on_receive=on_receive) 141 | self.server.bind((common.TEST_IPV4, common.TEST_PORT1)) 142 | self.server.receive_start() 143 | 144 | self.client = uv.UDP() 145 | self.client.bind(('0.0.0.0', 0)) 146 | 147 | self.timer = uv.Timer(on_timeout=on_timeout) 148 | self.timer.start(100) 149 | 150 | self.loop.run() 151 | 152 | self.assert_equal(self.datagram, b'hello') 153 | 154 | def test_udp_closed(self): 155 | self.udp = uv.UDP() 156 | self.udp.close() 157 | self.assert_is(self.udp.family, None) 158 | self.assert_equal(self.udp.sockname, ('0.0.0.0', 0)) 159 | self.assert_raises(uv.ClosedHandleError, self.udp.open, None) 160 | self.assert_raises(uv.ClosedHandleError, self.udp.set_membership, None, None) 161 | self.assert_raises(uv.ClosedHandleError, self.udp.set_multicast_loop, False) 162 | self.assert_raises(uv.ClosedHandleError, self.udp.set_multicast_ttl, 10) 163 | self.assert_raises(uv.ClosedHandleError, self.udp.set_multicast_interface, None) 164 | self.assert_raises(uv.ClosedHandleError, self.udp.set_broadcast, False) 165 | self.assert_raises(uv.ClosedHandleError, self.udp.bind, ('0.0.0.0', 0)) 166 | self.assert_raises(uv.ClosedHandleError, self.udp.send, b'', ('0.0.0.0', 0)) 167 | self.assert_raises(uv.ClosedHandleError, self.udp.try_send, b'', ('0.0.0.0', 0)) 168 | self.assert_raises(uv.ClosedHandleError, self.udp.receive_start) 169 | self.assert_is(self.udp.receive_stop(), None) 170 | 171 | 172 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py27, py33, py34, py35, pypy, pypy3 3 | 4 | [testenv] 5 | passenv = TRAVIS TRAVIS_JOB_ID TRAVIS_BRANCH 6 | deps = 7 | nose 8 | cffi 9 | coveralls 10 | commands = 11 | nosetests -v [] --with-coverage --cover-package=uv 12 | -------------------------------------------------------------------------------- /uv/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | 16 | """ 17 | This package aims to provide an object oriented CFFI based wrapper around 18 | the libuv asynchronous IO library. It supports all handles as well as 19 | filesystem operations, dns utility functions and miscellaneous utilities. 20 | 21 | There are no plans to support the thread pool work scheduling and the 22 | threading and synchronization utilities because Python already provides 23 | nice solutions for those things in the standard library. 24 | 25 | Based on Python's standard library's SSL module this package also provides 26 | support for asynchronous SSL sockets. 27 | 28 | As you may have noticed this package is not totally PEP-8 conform when it 29 | comes to the maximum line length of 79 characters – instead we are using 30 | a maximum line length of 90 characters. This allows us to use longer and 31 | more expressive variable names without ugly line breaking stunts and 32 | overall makes the code more readable. 33 | """ 34 | 35 | from __future__ import print_function, unicode_literals, division, absolute_import 36 | 37 | from .metadata import __version__, __author__, __email__, __project__ 38 | 39 | from .library import version as uv_version 40 | 41 | from .error import UVError, ClosedHandleError, ClosedLoopError, StatusCodes 42 | from .handle import UVHandle 43 | from .loop import RunModes, Loop 44 | from .request import UVRequest 45 | 46 | from . import common, error, loop, handle, request 47 | 48 | from .abstract import Handle, Request, Stream 49 | 50 | from .handles.async import Async 51 | from .handles.check import Check 52 | from .handles.idle import Idle 53 | from .handles.pipe import PipeConnectRequest, Pipe 54 | from .handles.poll import PollEvent, Poll 55 | from .handles.prepare import Prepare 56 | from .handles.process import CreatePipe, PIPE, ProcessFlags, Process 57 | from .handles.signal import Signals, Signal 58 | from .handles.stream import ShutdownRequest, WriteRequest, ConnectRequest, UVStream 59 | from .handles.tcp import TCPFlags, TCPConnectRequest, TCP 60 | from .handles.timer import Timer 61 | from .handles.tty import ConsoleSize, TTYMode, TTY 62 | from .handles.udp import UDPFlags, UDPMembership, UDPSendRequest, UDP 63 | 64 | from .handles.fs_event import FSEvents, FSEventFlags, FSEvent 65 | from .handles.fs_poll import FSPoll 66 | 67 | from .handles import async 68 | from .handles import check 69 | from .handles import idle 70 | from .handles import pipe 71 | from .handles import process 72 | from .handles import signal 73 | from .handles import stream 74 | from .handles import tcp 75 | from .handles import timer 76 | from .handles import tty 77 | from .handles import udp 78 | 79 | from .handles import fs_event 80 | from .handles import fs_poll 81 | 82 | from .dns import (AddressFamilies, SocketTypes, SocketProtocols, Address, Address4, 83 | Address6, AddrInfo, NameInfo, getnameinfo, getaddrinfo) 84 | 85 | from .fs import Stat 86 | 87 | from . import dns 88 | from . import fs 89 | from . import misc 90 | from . import secure 91 | -------------------------------------------------------------------------------- /uv/common.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | 16 | from __future__ import absolute_import, division, print_function, unicode_literals 17 | 18 | import os 19 | import platform 20 | import sys 21 | from collections import OrderedDict 22 | 23 | is_py2 = sys.version_info[0] == 2 24 | is_py3 = sys.version_info[0] == 3 25 | 26 | is_pypy = platform.python_implementation().lower() == 'pypy' 27 | is_cpython = platform.python_implementation().lower() == 'cpython' 28 | 29 | 30 | def with_metaclass(meta, *bases): 31 | class Metaclass(meta): 32 | def __new__(cls, name, _, attributes): 33 | return meta(name, bases, attributes) 34 | return type.__new__(Metaclass, str('Metaclass'), (), {}) 35 | 36 | 37 | class _EnumerationMeta(type): 38 | _members = [] 39 | _value_member_map = {} 40 | 41 | def __prepare__(mcs, *args, **kwargs): 42 | return OrderedDict() 43 | 44 | def __new__(mcs, cls_name, cls_bases, attributes): 45 | members = [(name, value) for name, value in attributes.items() 46 | if not (hasattr(value, '__get__') or hasattr(value, '__set__') or 47 | hasattr(value, '__delete__') or name.startswith('_'))] 48 | for name, value in members: 49 | attributes[name] = None 50 | value_member_map = {} 51 | attributes['_members'] = members 52 | attributes['_value_member_map'] = value_member_map 53 | cls = type.__new__(mcs, cls_name, cls_bases, attributes) 54 | for name, value in members: 55 | instance = super(_EnumerationMeta, mcs).__call__(cls, value) 56 | instance.name = name 57 | instance.value = value 58 | value_member_map[value] = instance 59 | setattr(cls, name, value_member_map[value]) 60 | return cls 61 | 62 | def __call__(cls, value): 63 | try: 64 | return cls._value_member_map[value] 65 | except KeyError: 66 | raise ValueError(value) 67 | 68 | def __iter__(cls): 69 | return iter(cls._value_member_map.values()) 70 | 71 | try: 72 | from enum import IntEnum 73 | except ImportError: 74 | class IntEnum(with_metaclass(_EnumerationMeta, int)): 75 | def __repr__(self): 76 | return '<{self.__class__.__name__}.{self.name}: {self}>'.format(self=self) 77 | 78 | 79 | class Enumeration(IntEnum): 80 | @classmethod 81 | def get(cls, integer): 82 | try: 83 | return cls(integer) 84 | except ValueError: 85 | return integer 86 | 87 | 88 | try: 89 | import builtins 90 | except ImportError: 91 | if isinstance(__builtins__, dict): 92 | class _Builtins(object): 93 | def __getattr__(self, item): 94 | try: 95 | return __builtins__[item] 96 | except KeyError: 97 | raise AttributeError() 98 | 99 | builtins = _Builtins() 100 | else: 101 | builtins = __builtins__ 102 | 103 | 104 | is_posix = os.name == 'posix' 105 | is_nt = os.name == 'nt' 106 | is_linux = sys.platform.startswith('linux') 107 | is_win32 = sys.platform == 'win32' 108 | 109 | 110 | def dummy_callback(*arguments, **keywords): pass 111 | -------------------------------------------------------------------------------- /uv/fs.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | 16 | from __future__ import print_function, unicode_literals, division, absolute_import 17 | 18 | from collections import namedtuple 19 | 20 | from . import common, error, handle, library, request 21 | from .library import ffi, lib 22 | 23 | Timespec = namedtuple('Timespec', ['sec', 'nsec']) 24 | 25 | Stat = namedtuple('Stat', ['dev', 'mode', 'nlink', 'uid', 'gid', 'rdev', 26 | 'ino', 'size', 'blksize', 'blocks', 'flags', 'gen', 27 | 'atim', 'mtim', 'ctim', 'birthtim']) 28 | 29 | Dirent = namedtuple('Dirent', ['name', 'type']) 30 | 31 | requests = set() 32 | 33 | 34 | def unpack_timespec(uv_timespec): 35 | return Timespec(uv_timespec.tv_sec, uv_timespec.tv_nsec) 36 | 37 | 38 | def unpack_stat(uv_stat): 39 | return Stat(uv_stat.st_dev, uv_stat.st_mode, uv_stat.st_nlink, uv_stat.st_uid, 40 | uv_stat.st_gid, uv_stat.st_rdev, uv_stat.st_ino, uv_stat.st_size, 41 | uv_stat.st_blksize, uv_stat.st_blocks, uv_stat.st_flags, uv_stat.st_gen, 42 | unpack_timespec(uv_stat.st_atim), unpack_timespec(uv_stat.st_mtim), 43 | unpack_timespec(uv_stat.st_ctim), unpack_timespec(uv_stat.st_birthtim)) 44 | 45 | 46 | def unpack_dirent(uv_dirent): 47 | return Dirent(ffi.string(uv_dirent.name).decode(), DirentType(uv_dirent.type)) 48 | 49 | 50 | class FSType(common.Enumeration): 51 | UNKNOWN = lib.UV_FS_UNKNOWN 52 | CUSTOM = lib.UV_FS_CUSTOM 53 | OPEN = lib.UV_FS_OPEN 54 | CLOSE = lib.UV_FS_CLOSE 55 | READ = lib.UV_FS_READ 56 | WRITE = lib.UV_FS_WRITE 57 | SENDFILE = lib.UV_FS_SENDFILE 58 | STAT = lib.UV_FS_STAT 59 | LSTAT = lib.UV_FS_LSTAT 60 | FSTAT = lib.UV_FS_FSTAT 61 | FTRUNCATE = lib.UV_FS_FTRUNCATE 62 | UTIME = lib.UV_FS_UTIME 63 | FUTIME = lib.UV_FS_FUTIME 64 | ACCESS = lib.UV_FS_ACCESS 65 | CHMOD = lib.UV_FS_CHMOD 66 | FCHMOD = lib.UV_FS_FCHMOD 67 | FSYNC = lib.UV_FS_FSYNC 68 | FDATASYNC = lib.UV_FS_FDATASYNC 69 | UNLINK = lib.UV_FS_UNLINK 70 | RMDIR = lib.UV_FS_RMDIR 71 | MKDIR = lib.UV_FS_MKDIR 72 | MKDTEMP = lib.UV_FS_MKDTEMP 73 | RENAME = lib.UV_FS_RENAME 74 | SCANDIR = lib.UV_FS_SCANDIR 75 | LINK = lib.UV_FS_LINK 76 | SYMLINK = lib.UV_FS_SYMLINK 77 | READLINK = lib.UV_FS_READLINK 78 | CHOWN = lib.UV_FS_CHOWN 79 | FCHOWN = lib.UV_FS_FCHOWN 80 | 81 | def __call__(self, postprocessor): 82 | self.postprocessor = postprocessor 83 | return postprocessor 84 | 85 | 86 | class DirentType(common.Enumeration): 87 | UNKNOWN = lib.UV_DIRENT_UNKNOWN 88 | FILE = lib.UV_DIRENT_FILE 89 | DIR = lib.UV_DIRENT_DIR 90 | LINK = lib.UV_DIRENT_LINK 91 | FIFO = lib.UV_DIRENT_FIFO 92 | SOCKET = lib.UV_DIRENT_SOCKET 93 | CHAR = lib.UV_DIRENT_CHAR 94 | BLOCK = lib.UV_DIRENT_BLOCK 95 | 96 | 97 | @request.RequestType.FS 98 | class FSRequest(request.UVRequest): 99 | __slots__ = ['uv_fs', 'data', 'callback'] 100 | 101 | def __init__(self, data=None, callback=None, loop=None): 102 | self.uv_fs = ffi.new('uv_fs_t*') 103 | super(FSRequest, self).__init__(self.uv_fs, loop) 104 | self.data = data 105 | self.callback = callback or common.dummy_callback 106 | requests.add(self) 107 | 108 | @property 109 | def result(self): 110 | return self.uv_fs.result 111 | 112 | @property 113 | def ptr(self): 114 | return self.uv_fs.ptr 115 | 116 | @property 117 | def path(self): 118 | return ffi.string(self.uv_fs.path).decode() 119 | 120 | @property 121 | def fs_type(self): 122 | return FSType(self.uv_fs.fs_type) 123 | 124 | @property 125 | def stat(self): 126 | return unpack_stat(self.uv_fs.statbuf) 127 | 128 | 129 | @FSType.CLOSE 130 | def post_close(request): 131 | status = error.StatusCodes.get(request.result) 132 | return [status] 133 | 134 | 135 | 136 | 137 | 138 | @FSType.STAT 139 | def post_stat(request): 140 | status = error.StatusCodes.get(request.result) 141 | return [status, request.stat] 142 | 143 | 144 | @ffi.callback('uv_fs_cb') 145 | def fs_callback(uv_request): 146 | fs_request = library.detach(uv_request) 147 | """ :type: uv.FSRequest """ 148 | try: 149 | fs_request.callback(fs_request, *fs_request.fs_type.postprocessor(fs_request)) 150 | except: 151 | fs_request.loop.handle_exception() 152 | lib.uv_fs_req_cleanup(uv_request) 153 | fs_request.set_closed() 154 | 155 | 156 | def close(fd, callback=None, loop=None): 157 | request = FSRequest(None, callback, loop) 158 | code = lib.cross_uv_fs_close(request.loop.uv_loop, request.uv_fs, fd, fs_callback) 159 | if code != error.StatusCodes.SUCCESS: 160 | raise error.UVError(code) 161 | return request 162 | 163 | 164 | @FSType.OPEN 165 | def post_open(request): 166 | if request.result < 0: status, fd = error.StatusCodes.get(request.result), None 167 | else: status, fd = error.StatusCodes.SUCCESS, request.result 168 | return [status, fd] 169 | 170 | 171 | def open(path, flags, mode=0o777, callback=None, loop=None): 172 | """ 173 | 174 | :param path: 175 | :param flags: 176 | :param mode: 177 | :param callback: callback signature: `(request, status, fd)` 178 | :param loop: 179 | 180 | :type path: str 181 | :type flags: int 182 | :type mode: int 183 | :type callback: (FSRequest, int, int) -> None 184 | 185 | :return: 186 | """ 187 | fs_request = FSRequest(None, callback, loop) 188 | uv_fs = request.uv_fs 189 | c_path = path.encode() 190 | code = lib.uv_fs_open(fs_request.loop.uv_loop, uv_fs, c_path, flags, mode, 191 | fs_callback) 192 | if code != error.StatusCodes.SUCCESS: 193 | raise error.UVError(code) 194 | return request 195 | 196 | 197 | def stat(path, callback=None, loop=None): 198 | fs_request = FSRequest(callback=callback, loop=loop) 199 | code = lib.uv_fs_stat(fs_request.loop.uv_loop, fs_request.uv_fs, path.encode(), 200 | fs_callback) 201 | if code != error.StatusCodes.SUCCESS: 202 | raise error.UVError(code) 203 | return request 204 | 205 | 206 | @handle.HandleTypes.FILE 207 | class File(object): 208 | pass 209 | -------------------------------------------------------------------------------- /uv/handles/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | -------------------------------------------------------------------------------- /uv/handles/async.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | 16 | from __future__ import absolute_import, division, print_function, unicode_literals 17 | 18 | from .. import base, common, error, handle 19 | from ..library import lib 20 | 21 | 22 | @base.handle_callback('uv_async_cb') 23 | def uv_async_cb(async_handle): 24 | """ 25 | :type async_handle: 26 | uv.Async 27 | """ 28 | async_handle.clear_pending() 29 | async_handle.on_wakeup(async_handle) 30 | 31 | 32 | @handle.HandleTypes.ASYNC 33 | class Async(handle.UVHandle): 34 | """ 35 | Async handles are able to wakeup the event loop of another thread 36 | and run the given callback in the event loop's thread. Although the 37 | :func:`uv.Async.send` method is thread-safe the constructor is not. 38 | 39 | To run a callback in the event loop's thread without creating an 40 | :class:`uv.Async` handle use :func:`uv.Loop.call_later`. 41 | 42 | :raises uv.UVError: 43 | error while initializing the handle 44 | 45 | :param loop: 46 | event loop the handle should run on 47 | :param on_wakeup: 48 | callback which should run in the event loop's thread after the 49 | event loop has been woken up 50 | 51 | :type loop: 52 | uv.Loop 53 | :type on_wakeup: 54 | ((uv.Async) -> None) | ((Any, uv.Async) -> None) 55 | """ 56 | 57 | __slots__ = ['uv_async', 'on_wakeup'] 58 | 59 | uv_handle_type = 'uv_async_t*' 60 | uv_handle_init = lib.uv_async_init 61 | 62 | def __init__(self, loop=None, on_wakeup=None): 63 | super(Async, self).__init__(loop, (uv_async_cb, )) 64 | self.uv_async = self.base_handle.uv_object 65 | self.on_wakeup = on_wakeup or common.dummy_callback 66 | """ 67 | Callback which should run in the event loop's thread after the 68 | event loop has been woken up. 69 | 70 | 71 | .. function:: on_wakeup(async_handle) 72 | 73 | :param async_handle: 74 | handle the call originates from 75 | 76 | :type async_handle: 77 | uv.Async 78 | 79 | 80 | :readonly: 81 | False 82 | :type: 83 | ((uv.Async) -> None) | ((Any, uv.Async) -> None) 84 | """ 85 | 86 | def send(self, on_wakeup=None): 87 | """ 88 | Wakeup the event loop and run the callback afterwards. Multiple 89 | calls to this method are coalesced if they happen before the 90 | callback has been called. This means not every call will yield 91 | an execution of the callback. It is safe to call this method 92 | form outside the event loop's thread. 93 | 94 | :raises uv.UVError: 95 | error while trying to wakeup the event loop 96 | :raises uv.ClosedHandleError: 97 | handle has already been closed or is closing 98 | 99 | :param on_wakeup: 100 | callback which should run in the event loop's thread after 101 | the event loop has been woken up (overrides the current 102 | callback if specified) 103 | 104 | :type on_wakeup: 105 | ((uv.Async) -> None) | ((Any, uv.Async) -> None) 106 | """ 107 | if self.closing: 108 | raise error.ClosedHandleError() 109 | self.on_wakeup = on_wakeup or self.on_wakeup 110 | code = lib.uv_async_send(self.uv_async) 111 | if code != error.StatusCodes.SUCCESS: 112 | raise error.UVError(code) 113 | self.set_pending() 114 | 115 | __call__ = send 116 | -------------------------------------------------------------------------------- /uv/handles/check.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | 16 | from __future__ import absolute_import, division, print_function, unicode_literals 17 | 18 | from .. import base, common, error, handle 19 | from ..library import lib 20 | 21 | 22 | @base.handle_callback('uv_check_cb') 23 | def uv_check_cb(check_handle): 24 | """ 25 | :type check_handle: 26 | uv.Check 27 | """ 28 | check_handle.on_check(check_handle) 29 | 30 | 31 | @handle.HandleTypes.CHECK 32 | class Check(handle.UVHandle): 33 | """ 34 | Check handles will run the given callback once per loop iteration, 35 | right after polling for IO after they have been started. 36 | 37 | :raises uv.UVError: 38 | error while initializing the handle 39 | 40 | :param loop: 41 | event loop the handle should run on 42 | :param on_check: 43 | callback which should run right after polling for IO after the 44 | handle has been started 45 | 46 | :type loop: 47 | uv.Loop 48 | :type on_check: 49 | ((uv.Check) -> None) | ((Any, uv.Check) -> None) 50 | """ 51 | 52 | __slots__ = ['uv_check', 'on_check'] 53 | 54 | uv_handle_type = 'uv_check_t*' 55 | uv_handle_init = lib.uv_check_init 56 | 57 | def __init__(self, loop=None, on_check=None): 58 | super(Check, self).__init__(loop) 59 | self.uv_check = self.base_handle.uv_object 60 | self.on_check = on_check or common.dummy_callback 61 | """ 62 | Callback which should run right after polling for IO after the 63 | handle has been started. 64 | 65 | 66 | .. function:: on_check(check_handle) 67 | 68 | :param check_handle: 69 | handle the call originates from 70 | 71 | :type check_handle: 72 | uv.Check 73 | 74 | 75 | :readonly: 76 | False 77 | :type: 78 | ((uv.Check) -> None) | ((Any, uv.Check) -> None) 79 | """ 80 | 81 | def start(self, on_check=None): 82 | """ 83 | Start the handle. The callback will be called once per loop 84 | iteration right after polling for IO from now on. 85 | 86 | :raises uv.UVError: 87 | error while starting the handle 88 | :raises uv.ClosedHandleError: 89 | handle has already been closed or is closing 90 | 91 | :param on_check: 92 | callback which should run right after polling for IO 93 | (overrides the current callback if specified) 94 | 95 | :type on_check: 96 | ((uv.Check) -> None) | ((Any, uv.Check) -> None) 97 | """ 98 | if self.closing: 99 | raise error.ClosedHandleError() 100 | self.on_check = on_check or self.on_check 101 | code = lib.uv_check_start(self.uv_check, uv_check_cb) 102 | if code != error.StatusCodes.SUCCESS: 103 | raise error.UVError(code) 104 | self.set_pending() 105 | 106 | def stop(self): 107 | """ 108 | Stop the handle. The callback will no longer be called. 109 | 110 | :raises uv.UVError: 111 | error while stopping the handle 112 | """ 113 | if self.closing: 114 | return 115 | code = lib.uv_check_stop(self.uv_check) 116 | if code != error.StatusCodes.SUCCESS: 117 | raise error.UVError(code) 118 | self.clear_pending() 119 | 120 | __call__ = start 121 | -------------------------------------------------------------------------------- /uv/handles/fs_event.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | 16 | 17 | from __future__ import absolute_import, division, print_function, unicode_literals 18 | 19 | from .. import base, common, error, handle 20 | from ..library import ffi, lib 21 | 22 | 23 | class FSEventFlags(common.Enumeration): 24 | """ 25 | Flags to configure the behavior of :class:`uv.FSEvent`. 26 | """ 27 | 28 | WATCH_ENTRY = lib.UV_FS_EVENT_WATCH_ENTRY 29 | """ 30 | By default, if the fs event watcher is given a directory 31 | name, it will watch for all events in that directory. This 32 | flag overrides this behavior and makes :class:`uv.FSEvent` 33 | report only changes to the directory entry itself. This flag 34 | does not affect individual files watched. 35 | 36 | .. note:: 37 | This flag is currently not implemented yet on any backend. 38 | 39 | :type: uv.FSEventFlags 40 | """ 41 | 42 | STAT = lib.UV_FS_EVENT_STAT 43 | """ 44 | By default :class:`uv.FSEvent` will try to use a kernel 45 | interface such as inotify or kqueue to detect events. This 46 | may not work on remote filesystems such as NFS mounts. This 47 | flag makes :class:`uv.FSEvent` fall back to calling `stat()` 48 | on a regular interval. 49 | 50 | .. note:: 51 | This flag is currently not implemented yet on any backend. 52 | 53 | :type: uv.FSEventFlags 54 | """ 55 | 56 | RECURSIVE = lib.UV_FS_EVENT_RECURSIVE 57 | """ 58 | By default, if the fs event watcher is given a directory name, 59 | it will not watch for events in its subdirectories. This flag 60 | overrides this behavior and makes :class:`uv.FSEvent` report 61 | also changes in subdirectories. 62 | 63 | .. note:: 64 | Currently the only supported platforms are OSX and Windows. 65 | 66 | :type: uv.FSEventFlags 67 | """ 68 | 69 | 70 | class FSEvents(common.Enumeration): 71 | """ 72 | Events reported by :class:`uv.FSEvent` on filesystem changes. 73 | """ 74 | 75 | RENAME = lib.UV_RENAME 76 | """ 77 | File has been renamed or deleted. If the file has been deleted it 78 | is necessary (at least on Linux) to restart the corresponding 79 | watcher even if the file has been directly recreated. 80 | 81 | :type: uv.FSEvents 82 | """ 83 | 84 | CHANGE = lib.UV_CHANGE 85 | """ 86 | File has been changed. 87 | 88 | :type: uv.FSEvents 89 | """ 90 | 91 | 92 | @base.handle_callback('uv_fs_event_cb') 93 | def uv_fs_event_cb(fs_event_handle, c_filename, events, status): 94 | """ 95 | :type fs_event_handle: 96 | uv.FSEvent 97 | :type c_filename: 98 | ffi.CData[char*] 99 | :type events: 100 | int 101 | :type status: 102 | int 103 | """ 104 | filename = ffi.string(c_filename).decode() 105 | code = error.StatusCodes.get(status) 106 | fs_event_handle.on_event(fs_event_handle, code, filename, events) 107 | 108 | 109 | @handle.HandleTypes.FS_EVENT 110 | class FSEvent(handle.UVHandle): 111 | """ 112 | FS event handles monitor a given filesystem path for changes 113 | including renaming und deletion after they have been started. 114 | This handle uses the best backend available for this job on 115 | each platform. 116 | 117 | :raises uv.UVError: 118 | error while initializing the handle 119 | 120 | :param path: 121 | directory or filename to monitor 122 | :param flags: 123 | flags to be used for monitoring 124 | :param loop: 125 | event loop the handle should run on 126 | :param on_event: 127 | callback which should be called on filesystem events after 128 | the handle has been started 129 | 130 | :type path: 131 | unicode 132 | :type flags: 133 | int 134 | :type loop: 135 | uv.Loop 136 | :type on_event: 137 | ((uv.FSEvent, uv.StatusCode, unicode, int) -> None) | 138 | ((Any, uv.FSEvent, uv.StatusCode, unicode, int) -> None) 139 | """ 140 | 141 | __slots__ = ['uv_fs_event', 'on_event', 'flags', 'path'] 142 | 143 | uv_handle_type = 'uv_fs_event_t*' 144 | uv_handle_init = lib.uv_fs_event_init 145 | 146 | def __init__(self, path=None, flags=0, loop=None, on_event=None): 147 | super(FSEvent, self).__init__(loop) 148 | self.uv_fs_event = self.base_handle.uv_object 149 | self.path = path 150 | """ 151 | Directory or filename to monitor. 152 | 153 | .. warning:: 154 | This property is writable, however you need to restart the 155 | handle if you change it during the handle is active. 156 | 157 | :readonly: 158 | False 159 | :type: 160 | unicode 161 | """ 162 | self.flags = flags 163 | """ 164 | Flags to be used for monitoring. 165 | 166 | .. warning:: 167 | This property is writable, however you need to restart the 168 | handle if you change it during the handle is active. 169 | 170 | :readonly: 171 | False 172 | :type: 173 | int 174 | """ 175 | self.on_event = on_event or common.dummy_callback 176 | """ 177 | Callback which should be called on filesystem events after tha 178 | handle has been started. 179 | 180 | 181 | .. function:: on_event(fs_event, status, filename, events) 182 | 183 | :param fs_event: 184 | handle the call originates from 185 | :param status: 186 | may indicate any errors 187 | :param filename: 188 | if the handle has been started with a directory this 189 | will be a relative path to a file contained in that 190 | directory which triggered the events 191 | :param events: 192 | bitmask of the triggered events 193 | 194 | :type fs_event: 195 | uv.FSEvent 196 | :type status: 197 | uv.StatusCode 198 | :type filename: 199 | unicode 200 | :type events: 201 | int 202 | 203 | 204 | :readonly: 205 | False 206 | :type: 207 | ((uv.FSEvent, uv.StatusCode, unicode, int) -> None) | 208 | ((Any, uv.FSEvent, uv.StatusCode, unicode, int) -> None) 209 | """ 210 | 211 | def start(self, path=None, flags=None, on_event=None): 212 | """ 213 | Start watching for filesystem events. 214 | 215 | :raises uv.UVError: 216 | error while starting the handle 217 | :raises uv.ClosedHandleError: 218 | handle has already been closed or is closing 219 | 220 | :param path: 221 | directory or filename to monitor (overrides the current 222 | path if specified) 223 | :param flags: 224 | flags to be used for monitoring (overrides the current 225 | flags if specified) 226 | :param on_event: 227 | callback which should be called on filesystem events 228 | (overrides the current callback if specified) 229 | 230 | :type path: 231 | unicode 232 | :type flags: 233 | int 234 | :type on_event: 235 | ((uv.FSEvent, uv.StatusCode, unicode, int) -> None) | 236 | ((Any, uv.FSEvent, uv.StatusCode, unicode, int) -> None) 237 | """ 238 | if self.closing: 239 | raise error.ClosedHandleError() 240 | self.path = path or self.path 241 | self.flags = flags or self.flags 242 | self.on_event = on_event or self.on_event 243 | if self.path is None: 244 | raise error.ArgumentError(message='no path has been specified') 245 | c_path = self.path.encode() 246 | code = lib.uv_fs_event_start(self.uv_fs_event, uv_fs_event_cb, c_path, self.flags) 247 | if code != error.StatusCodes.SUCCESS: 248 | raise error.UVError(code) 249 | self.set_pending() 250 | 251 | def stop(self): 252 | """ 253 | Stop the handle. The callback will no longer be called. 254 | 255 | :raises uv.UVError: 256 | error while stopping the handle 257 | """ 258 | if self.closing: 259 | return 260 | code = lib.uv_fs_event_stop(self.uv_fs_event) 261 | if code != error.StatusCodes.SUCCESS: 262 | raise error.UVError(code) 263 | self.clear_pending() 264 | 265 | __call__ = start 266 | -------------------------------------------------------------------------------- /uv/handles/fs_poll.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | 16 | from __future__ import absolute_import, division, print_function, unicode_literals 17 | 18 | from .. import base, common, error, fs, handle 19 | from ..library import lib 20 | 21 | 22 | @base.handle_callback('uv_fs_poll_cb') 23 | def uv_fs_poll_cb(fs_poll_handle, status, uv_previous_stat, uv_current_stat): 24 | """ 25 | :type fs_poll_handle: 26 | uv.FSPoll 27 | :type status: 28 | int 29 | :type uv_previous_stat: 30 | ffi.CData[uv_stat_t*] 31 | :type uv_current_stat: 32 | ffi.CData[uv_stat_t*] 33 | """ 34 | previous_stat = fs.unpack_stat(uv_previous_stat) if uv_previous_stat else None 35 | current_stat = fs.unpack_stat(uv_current_stat) if uv_current_stat else None 36 | fs_poll_handle.on_change(fs_poll_handle, status, previous_stat, current_stat) 37 | 38 | 39 | @handle.HandleTypes.FS_POLL 40 | class FSPoll(handle.UVHandle): 41 | """ 42 | FS poll handles monitor a given filesystem path for changes. Unlike 43 | fs event handles, fs poll handles use `stat()` to detect when a 44 | file or directory has been changed so they can work on file systems 45 | where fs event handles can not. 46 | 47 | .. note:: 48 | For maximum portability, use multi-second intervals. Sub-second 49 | intervals will not detect all changes on many file systems. 50 | 51 | :raises uv.UVError: 52 | error while initializing the handle 53 | 54 | :param path: 55 | directory or filename to monitor 56 | :param interval: 57 | interval to be used for monitoring (in milliseconds) 58 | :param loop: 59 | event loop the handle should run on 60 | :param on_change: 61 | callback which should be called on filesystem changes after the 62 | handle has been started 63 | 64 | :type path: 65 | unicode 66 | :type interval: 67 | int 68 | :type loop: 69 | uv.Loop 70 | :type on_change: 71 | ((uv.FSPoll, uv.StatusCode, uv.Stat, uv.Stat) -> None) | 72 | ((Any, uv.FSPoll, uv.StatusCode, uv.Stat, uv.Stat) -> None) 73 | """ 74 | 75 | __slots__ = ['uv_fs_poll', 'on_change', 'path', 'interval'] 76 | 77 | uv_handle_type = 'uv_fs_poll_t*' 78 | uv_handle_init = lib.uv_fs_poll_init 79 | 80 | def __init__(self, path=None, interval=5000, loop=None, on_change=None): 81 | super(FSPoll, self).__init__(loop) 82 | self.uv_fs_poll = self.base_handle.uv_object 83 | self.path = path 84 | """ 85 | Directory or filename to monitor. 86 | 87 | .. warning:: 88 | This property is writable, however you need to restart the 89 | handle if you change it during the handle is active. 90 | 91 | :readonly: 92 | False 93 | :type: 94 | unicode 95 | """ 96 | self.interval = interval 97 | """ 98 | Interval to be used for monitoring (in milliseconds). 99 | 100 | .. warning:: 101 | This property is writable, however you need to restart the 102 | handle if you change it during the handle is active. 103 | 104 | :readonly: 105 | False 106 | :type: 107 | int 108 | """ 109 | self.on_change = on_change or common.dummy_callback 110 | """ 111 | Callback which should be called on filesystem changes after the 112 | handle has been started. 113 | 114 | 115 | .. function:: on_change(fs_poll, status, previous_stat, current_stat) 116 | 117 | :param fs_event: 118 | handle the call originates from 119 | :param status: 120 | may indicate any errors 121 | :param previous_stat: 122 | previous filesystem path's stat 123 | :param current_stat: 124 | current filesystem path's stat 125 | 126 | :type fs_event: 127 | uv.FSEvent 128 | :type status: 129 | uv.StatusCode 130 | :type previous_stat: 131 | uv.Stat 132 | :type current_stat: 133 | uv.Stat 134 | 135 | 136 | :readonly: 137 | False 138 | :type: 139 | ((uv.FSPoll, uv.StatusCode, uv.Stat, 140 | uv.Stat) -> None) 141 | ((Any, uv.FSPoll, uv.StatusCode, uv.Stat, 142 | uv.Stat) -> None) 143 | """ 144 | 145 | def start(self, path=None, interval=None, on_change=None): 146 | """ 147 | Start monitoring for filesystem changes. The change callback is 148 | invoked with status code < 0 if the given path does not exist 149 | or is inaccessible. The watcher is not stopped but your callback 150 | is not called again until something changes (e.g. when the file 151 | is created or the error reason changes). 152 | 153 | :raises uv.UVError: 154 | error while starting the handle 155 | :raises uv.ClosedHandleError: 156 | handle has already been closed or is closing 157 | 158 | :param path: 159 | directory or filename to monitor (overrides the current 160 | path if specified) 161 | :param interval: 162 | interval to be used for monitoring (in milliseconds and 163 | overrides the current interval if specified) 164 | :param on_change: 165 | callback which should be called on filesystem changes 166 | 167 | :type path: 168 | unicode 169 | :type interval: 170 | int 171 | :type on_change: 172 | ((uv.FSPoll, uv.StatusCode, uv.Stat, uv.Stat) -> None) | 173 | ((Any, uv.FSPoll, uv.StatusCode, uv.Stat, uv.Stat) -> None) 174 | """ 175 | if self.closing: 176 | raise error.ClosedHandleError() 177 | self.path = path or self.path 178 | self.interval = interval or self.interval 179 | self.on_change = on_change or self.on_change 180 | if self.path is None: 181 | raise error.ArgumentError(message='no path has been specified') 182 | c_path = self.path.encode() 183 | code = lib.uv_fs_poll_start(self.uv_fs_poll, uv_fs_poll_cb, c_path, self.interval) 184 | if code != error.StatusCodes.SUCCESS: 185 | raise error.UVError(code) 186 | self.set_pending() 187 | 188 | def stop(self): 189 | """ 190 | Stop the handle. The callback will no longer be called. 191 | 192 | :raises uv.UVError: 193 | error while stopping the handle 194 | """ 195 | if self.closing: 196 | return 197 | code = lib.uv_fs_poll_stop(self.uv_fs_poll) 198 | if code != error.StatusCodes.SUCCESS: 199 | raise error.UVError(code) 200 | self.clear_pending() 201 | 202 | __call__ = start 203 | -------------------------------------------------------------------------------- /uv/handles/idle.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | 16 | from __future__ import absolute_import, division, print_function, unicode_literals 17 | 18 | from .. import base, common, error, handle 19 | from ..library import lib 20 | 21 | 22 | @base.handle_callback('uv_idle_cb') 23 | def uv_idle_cb(idle_handle): 24 | """ 25 | :type idle_handle: 26 | uv.Idle 27 | """ 28 | idle_handle.on_idle(idle_handle) 29 | 30 | 31 | @handle.HandleTypes.IDLE 32 | class Idle(handle.UVHandle): 33 | """ 34 | Idle handles will run the given callback once per loop iteration, 35 | right before the :class:`uv.Prepare` handles. 36 | 37 | The notable difference with prepare handles is, that when there 38 | are active idle handles, the loop will perform a zero timeout poll 39 | instead of blocking for IO. 40 | 41 | .. warning: 42 | Despite the name, idle handles will get their callback called on 43 | every loop iteration, not when the loop is actually "idle". 44 | 45 | :raises uv.UVError: 46 | error while initializing the handle 47 | 48 | :param loop: 49 | event loop the handle should run on 50 | :param on_idle: 51 | callback which should run right before the prepare handles 52 | after the handle has been started 53 | 54 | :type loop: 55 | uv.Loop 56 | :type on_idle: 57 | ((uv.Idle) -> None) | ((Any, uv.Idle) -> None) 58 | """ 59 | 60 | __slots__ = ['uv_idle', 'on_idle'] 61 | 62 | uv_handle_type = 'uv_idle_t*' 63 | uv_handle_init = lib.uv_idle_init 64 | 65 | def __init__(self, loop=None, on_idle=None): 66 | super(Idle, self).__init__(loop) 67 | self.uv_idle = self.base_handle.uv_object 68 | self.on_idle = on_idle or common.dummy_callback 69 | """ 70 | Callback which should run right before the prepare handles 71 | after the handle has been started. 72 | 73 | 74 | .. function:: on_idle(idle) 75 | 76 | :param idle: 77 | handle the call originates from 78 | 79 | :type idle: 80 | uv.Idle 81 | 82 | 83 | :readonly: 84 | False 85 | :type: 86 | ((uv.Idle) -> None) | ((Any, uv.Idle) -> None) 87 | """ 88 | 89 | def start(self, on_idle=None): 90 | """ 91 | Start the handle. The callback will run once per loop iteration 92 | right before the prepare handles from now on. 93 | 94 | :raises uv.UVError: 95 | error while starting the handle 96 | :raises uv.HandleClosedError: 97 | handle has already been closed or is closing 98 | 99 | :param on_idle: 100 | callback which should run right before the prepare handles 101 | (overrides the current callback if specified) 102 | 103 | :type on_idle: 104 | ((uv.Idle) -> None) | ((Any, uv.Idle) -> None) 105 | """ 106 | if self.closing: 107 | raise error.ClosedHandleError() 108 | self.on_idle = on_idle or self.on_idle 109 | code = lib.uv_idle_start(self.uv_idle, uv_idle_cb) 110 | if code != error.StatusCodes.SUCCESS: 111 | raise error.UVError(code) 112 | self.set_pending() 113 | 114 | def stop(self): 115 | """ 116 | Stop the handle. The callback will no longer be called. 117 | 118 | :raises uv.UVError: 119 | error while stopping the handle 120 | """ 121 | if self.closing: 122 | return 123 | code = lib.uv_idle_stop(self.uv_idle) 124 | if code != error.StatusCodes.SUCCESS: 125 | raise error.UVError(code) 126 | self.clear_pending() 127 | 128 | __call__ = start 129 | -------------------------------------------------------------------------------- /uv/handles/poll.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | 16 | from __future__ import absolute_import, division, print_function, unicode_literals 17 | 18 | from .. import base, common, error, handle 19 | from ..library import lib 20 | 21 | 22 | class PollEvent(common.Enumeration): 23 | """ 24 | Events reported by :class:`uv.Poll` on IO events. 25 | """ 26 | 27 | READABLE = lib.UV_READABLE 28 | """ 29 | File descriptor is readable. 30 | 31 | :type: uv.PollEvent 32 | """ 33 | 34 | WRITABLE = lib.UV_WRITABLE 35 | """ 36 | File descriptor is writable. 37 | 38 | :type: uv.PollEvent 39 | """ 40 | 41 | 42 | @base.handle_callback('uv_poll_cb') 43 | def poll_callback(poll_handle, status, events): 44 | """ 45 | :type poll_handle: 46 | uv.Poll 47 | :type status: 48 | int 49 | :type events: 50 | int 51 | """ 52 | poll_handle.on_event(poll_handle, error.StatusCodes.get(status), events) 53 | 54 | 55 | @handle.HandleTypes.POLL 56 | class Poll(handle.UVHandle): 57 | """ 58 | Poll handles are used to watch file descriptors for readability and 59 | writability. The purpose of poll handles is to enable integrating 60 | external libraries that rely on the event loop to signal them about 61 | the socket status changes. Using them for any other purpose is not 62 | recommended. Use :class:`uv.TCP`, :class:`uv.UDP`, etc. instead, 63 | which provide faster and more scalable implementations, than what 64 | can be archived with :class:`uv.Poll`, especially on Windows. 65 | 66 | It is possible that poll handles occasionally signal that a file 67 | descriptor is readable or writable even when it is not. The user 68 | should therefore always be prepared to handle `EAGAIN` or equivalent 69 | when it attempts to read from or write to the fd. 70 | 71 | It is not okay to have multiple active poll handles for the same 72 | socket, this can cause libuv to busyloop or otherwise malfunction. 73 | 74 | Do not close a file descriptor while it is being polled by an active 75 | poll handle. This can cause the handle to report an error, but it 76 | might also start polling another socket. However the fd can be 77 | safely closed immediately after :func:`uv.Poll.stop` or 78 | :func:`uv.Handle.close` has been called. 79 | 80 | .. note:: 81 | On Windows only sockets can be polled with :class:`uv.Poll` 82 | handles. On Unix any file descriptor that would be accepted 83 | by :manpage:`poll(2)` can be used. 84 | 85 | :raises uv.UVError: 86 | error while initializing the handle 87 | 88 | :param fd: 89 | file descriptor to be polled (is set to non-blocking mode) 90 | :param loop: 91 | event loop the handle should run on 92 | :param on_event: 93 | callback which should be called on IO events after the handle 94 | has been started 95 | 96 | :type fd: 97 | int 98 | :type loop: 99 | uv.Loop 100 | :type on_event: 101 | ((uv.Poll, uv.StatusCode, int) -> None) | 102 | ((Any, uv.Poll, uv.StatusCode, int) -> None) 103 | """ 104 | 105 | __slots__ = ['uv_poll', 'fd', 'on_event'] 106 | 107 | uv_handle_type = 'uv_poll_t*' 108 | uv_handle_init = lib.cross_uv_poll_init_socket 109 | 110 | def __init__(self, fd, loop=None, on_event=None): 111 | super(Poll, self).__init__(loop, (fd, )) 112 | self.uv_poll = self.base_handle.uv_object 113 | self.fd = fd 114 | """ 115 | File descriptor the handle polls on. 116 | 117 | :readonly: 118 | True 119 | :type: 120 | int 121 | """ 122 | self.on_event = on_event or common.dummy_callback 123 | """ 124 | Callback which should be called on IO events after the handle 125 | has been started. 126 | 127 | 128 | .. function:: on_event(poll_handle, status, events) 129 | 130 | :param poll_handle: 131 | handle the call originates from 132 | :param status: 133 | may indicate any errors 134 | :param events: 135 | bitmask of the triggered IO events 136 | 137 | :type poll_handle: 138 | uv.Poll 139 | :type status: 140 | uv.StatusCode 141 | :type events: 142 | int 143 | 144 | 145 | :readonly: 146 | False 147 | :type: 148 | ((uv.Poll, uv.StatusCode, int) -> None) | 149 | ((Any, uv.Poll, uv.StatusCode, int) -> None) 150 | """ 151 | 152 | def fileno(self): 153 | """ 154 | Number of the file descriptor polled on. 155 | 156 | :rtype: 157 | int 158 | """ 159 | return self.fd 160 | 161 | def start(self, events=PollEvent.READABLE, on_event=None): 162 | """ 163 | Start polling the file descriptor for the given events. As soon 164 | as an event is detected the callback will be called with status 165 | code class:`uv.StatusCode.SUCCESS` and the triggered events. 166 | 167 | If an error happens while polling the callback gets called with 168 | status code != 0 which corresponds to a :class:`uv.StatusCode`. 169 | 170 | Calling this on a handle that is already active is fine. Doing 171 | so will update the events mask that is being polled for. 172 | 173 | :raises uv.UVError: 174 | error while starting the handle 175 | :raises uv.ClosedHandleError: 176 | handle has already been closed or is closing 177 | 178 | :param events: 179 | bitmask of events to be polled for 180 | :param on_event: 181 | callback which should be called on IO events (overrides the 182 | current callback if specified) 183 | 184 | :type events: 185 | int 186 | :type on_event: 187 | ((uv.Poll, uv.StatusCode, int) -> None) | 188 | ((Any, uv.Poll, uv.StatusCode, int) -> None) 189 | """ 190 | if self.closing: 191 | raise error.ClosedHandleError() 192 | self.on_event = on_event or self.on_event 193 | code = lib.uv_poll_start(self.uv_poll, events, poll_callback) 194 | if code != error.StatusCodes.SUCCESS: 195 | raise error.UVError(code) 196 | if events: 197 | self.set_pending() 198 | else: 199 | self.clear_pending() 200 | 201 | def stop(self): 202 | """ 203 | Stop the handle. The callback will no longer be called. 204 | 205 | :raises uv.UVError 206 | error while stopping the handle 207 | """ 208 | if self.closing: 209 | return 210 | code = lib.uv_poll_stop(self.uv_poll) 211 | if code != error.StatusCodes.SUCCESS: 212 | raise error.UVError(code) 213 | self.clear_pending() 214 | 215 | __call__ = start 216 | -------------------------------------------------------------------------------- /uv/handles/prepare.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | 16 | from __future__ import absolute_import, division, print_function, unicode_literals 17 | 18 | from .. import base, common, error, handle 19 | from ..library import lib 20 | 21 | 22 | @base.handle_callback('uv_prepare_cb') 23 | def uv_prepare_cb(prepare_handle): 24 | """ 25 | :type prepare_handle: 26 | uv.Prepare 27 | """ 28 | prepare_handle.on_prepare(prepare_handle) 29 | 30 | 31 | @handle.HandleTypes.PREPARE 32 | class Prepare(handle.UVHandle): 33 | """ 34 | Prepare handles will run the given callback once per loop 35 | iteration, right before polling for IO. 36 | 37 | :raises uv.UVError: 38 | error while initializing the handle 39 | 40 | :param loop: 41 | event loop the handle should run on 42 | :param on_prepare: 43 | callback which should run right before polling for IO after the 44 | handle has been started 45 | 46 | :type loop: 47 | uv.Loop 48 | :type on_prepare: 49 | ((uv.Prepare) -> None) | ((Any, uv.Prepare) -> None) 50 | """ 51 | 52 | __slots__ = ['uv_prepare', 'on_prepare'] 53 | 54 | uv_handle_type = 'uv_prepare_t*' 55 | uv_handle_init = lib.uv_prepare_init 56 | 57 | def __init__(self, loop=None, on_prepare=None): 58 | super(Prepare, self).__init__(loop) 59 | self.uv_prepare = self.base_handle.uv_object 60 | self.on_prepare = on_prepare or common.dummy_callback 61 | """ 62 | Callback which should run right before polling for IO after the 63 | handle has been started. 64 | 65 | 66 | .. function:: on_prepare(prepare_handle) 67 | 68 | :param prepare_handle: 69 | handle the call originates from 70 | 71 | :type prepare_handle: 72 | uv.Prepare 73 | 74 | 75 | :readonly: 76 | False 77 | :type: 78 | ((uv.Prepare) -> None) | ((Any, uv.Prepare) -> None) 79 | """ 80 | 81 | def start(self, on_prepare=None): 82 | """ 83 | Start the handle. The callback will run once per loop iteration 84 | right before polling for IO from now on. 85 | 86 | :raises uv.UVError: 87 | error while starting the handle 88 | :raises uv.ClosedHandleError: 89 | handle has already been closed or is closing 90 | 91 | :param on_prepare: 92 | callback which should run right before polling for IO 93 | (overrides the current callback if specified) 94 | :type on_prepare: 95 | ((uv.Prepare) -> None) | ((Any, uv.Prepare) -> None) 96 | """ 97 | if self.closing: 98 | raise error.ClosedHandleError() 99 | self.on_prepare = on_prepare or self.on_prepare 100 | code = lib.uv_prepare_start(self.uv_prepare, uv_prepare_cb) 101 | if code != error.StatusCodes.SUCCESS: 102 | raise error.UVError(code) 103 | self.set_pending() 104 | 105 | def stop(self): 106 | """ 107 | Stop the handle. The callback will no longer be called. 108 | 109 | :raises uv.UVError: 110 | error while stopping the handle 111 | """ 112 | if self.closing: 113 | return 114 | code = lib.uv_prepare_stop(self.uv_prepare) 115 | if code != error.StatusCodes.SUCCESS: 116 | raise error.UVError(code) 117 | self.clear_pending() 118 | 119 | __call__ = start 120 | -------------------------------------------------------------------------------- /uv/handles/signal.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | 16 | from __future__ import absolute_import, division, print_function, unicode_literals 17 | 18 | import signal as std_signal 19 | 20 | from .. import base, common, error, handle 21 | from ..library import lib 22 | 23 | 24 | class Signals(common.Enumeration): 25 | """ 26 | Standard cross platform signals enumeration. 27 | """ 28 | 29 | SIGINT = getattr(std_signal, 'SIGINT', 2) 30 | """ 31 | Is normally delivered when the user presses CTRL+C. However it is 32 | not generated when terminal is in raw mode. 33 | 34 | :type: uv.Signals 35 | """ 36 | 37 | SIGBREAK = 21 38 | """ 39 | Is delivered when the user presses CTRL+BREAK. This signal is only 40 | supported on Windows. 41 | 42 | :type: uv.Signals 43 | """ 44 | 45 | SIGHUP = getattr(std_signal, 'SIGHUP', 1) 46 | """ 47 | Is generated when the user closes the console window. After that 48 | the OS might terminate the program after a few seconds. 49 | 50 | :type: uv.Signals 51 | """ 52 | 53 | SIGWINCH = getattr(std_signal, 'SIGWINCH', 28) 54 | """ 55 | Is generated when the console window has been resized. On Windows 56 | libuv emulates `SIGWINCH` when the program uses a :class:`uv.TTY` 57 | handle to write to the console. It may not always be delivered in 58 | a timely manner, because libuv will only detect changes when the 59 | cursor is being moved. When a readable :class:`uv.TTY` handle is 60 | used in raw mode, resizing the console buffer will also trigger 61 | `SIGWINCH`. 62 | 63 | :type: uv.Signals 64 | """ 65 | 66 | 67 | @base.handle_callback('uv_signal_cb') 68 | def uv_signal_cb(signal_handle, signum): 69 | """ 70 | :type signal_handle: 71 | uv.Signal 72 | :type signum: 73 | int 74 | """ 75 | signal_handle.on_signal(signal_handle, signum) 76 | 77 | 78 | @handle.HandleTypes.SIGNAL 79 | class Signal(handle.UVHandle): 80 | """ 81 | Signal handles implement Unix style signal handling on a per-event 82 | loop basis. Reception of the generic :class:`uv.Signals` is 83 | emulated on Windows. Watchers for other signals can be successfully 84 | created, but these signals are never received. 85 | 86 | .. note:: 87 | On Linux SIGRT0 and SIGRT1 (signals 32 and 33) are used by the 88 | NPTL pthreads library to manage threads. Installing watchers 89 | for those signals will lead to unpredictable behavior and is 90 | strongly discouraged. Future versions of libuv may simply 91 | reject them. 92 | 93 | :raises uv.UVError: 94 | error while initializing the handle 95 | 96 | :param loop: 97 | event loop the handle should run on 98 | :param on_signal: 99 | callback which should be called on signal delivery after the 100 | handle has been started 101 | 102 | :type loop: 103 | uv.Loop 104 | :type on_signal: 105 | ((uv.Signal, int) -> None) | 106 | ((Any, uv.Signal, int) -> None) 107 | """ 108 | 109 | __slots__ = ['uv_signal', 'on_signal'] 110 | 111 | uv_handle_type = 'uv_signal_t*' 112 | uv_handle_init = lib.uv_signal_init 113 | 114 | def __init__(self, loop=None, on_signal=None): 115 | super(Signal, self).__init__(loop) 116 | self.uv_signal = self.base_handle.uv_object 117 | self.on_signal = on_signal or common.dummy_callback 118 | """ 119 | Callback which should be called on signal delivery after the 120 | handle has been started. 121 | 122 | 123 | .. function:: on_signal(signal_handle, signum): 124 | 125 | :param signal_handle: 126 | handle the call originates from 127 | :param signum: 128 | number of the received signal 129 | 130 | :type signal_handle: 131 | uv.Signal 132 | :type signum: 133 | int 134 | 135 | 136 | :readonly: 137 | False 138 | :type: 139 | ((uv.Signal, int) -> None) | 140 | ((Any, uv.Signal, int) -> None) 141 | """ 142 | 143 | @property 144 | def signum(self): 145 | """ 146 | Signal currently monitored by this handle. 147 | 148 | :raises uv.ClosedHandleError: 149 | handle has already been closed or is closing 150 | 151 | :readonly: 152 | True 153 | :rtype: 154 | int 155 | """ 156 | if self.closing: 157 | raise error.ClosedHandleError() 158 | return self.uv_signal.signum 159 | 160 | def start(self, signum, on_signal=None): 161 | """ 162 | Start listening for the given signal. 163 | 164 | :raises uv.UVError: 165 | error while starting the handle 166 | :raises uv.ClosedHandleError: 167 | handle has already been closed or is closing 168 | 169 | :param signum: 170 | signal number to listen for 171 | :param on_signal: 172 | callback which should be called on signal delivery 173 | (overrides the current callback if specified) 174 | 175 | :type signum: 176 | int 177 | :type on_signal: 178 | ((uv.Signal, int) -> None) | 179 | ((Any, uv.Signal, int) -> None) 180 | """ 181 | if self.closing: 182 | raise error.ClosedHandleError() 183 | self.on_signal = on_signal or self.on_signal 184 | code = lib.uv_signal_start(self.uv_signal, uv_signal_cb, signum) 185 | if code != error.StatusCodes.SUCCESS: 186 | raise error.UVError(code) 187 | self.set_pending() 188 | 189 | def stop(self): 190 | """ 191 | Stop listening. The callback will no longer be called. 192 | 193 | :raises uv.UVError: 194 | error while stopping the handle 195 | """ 196 | if self.closing: 197 | return 198 | code = lib.uv_signal_stop(self.uv_signal) 199 | if code != error.StatusCodes.SUCCESS: 200 | raise error.UVError(code) 201 | self.clear_pending() 202 | 203 | __call__ = start 204 | -------------------------------------------------------------------------------- /uv/handles/timer.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | 16 | from __future__ import absolute_import, division, print_function, unicode_literals 17 | 18 | from .. import base, common, error, handle 19 | from ..library import lib 20 | 21 | 22 | @base.handle_callback('uv_timer_cb') 23 | def uv_timer_cb(timer_handle): 24 | """ 25 | :type timer_handle: 26 | uv.Timer 27 | """ 28 | if not timer_handle.repeat: 29 | timer_handle.clear_pending() 30 | timer_handle.on_timeout(timer_handle) 31 | 32 | 33 | @handle.HandleTypes.TIMER 34 | class Timer(handle.UVHandle): 35 | """ 36 | Timer handles are used to schedule callbacks to be called in the 37 | future after a given amount of time. 38 | 39 | :raises uv.UVError: 40 | error while initializing the handle 41 | 42 | :param loop: event 43 | loop the handle should run on 44 | :param on_timeout: 45 | callback which should run on timeout 46 | 47 | :type loop: 48 | uv.Loop 49 | :type on_timeout: 50 | ((uv.Timer) -> None) | ((Any, uv.Timer) -> None) 51 | """ 52 | 53 | __slots__ = ['uv_timer', 'on_timeout'] 54 | 55 | uv_handle_type = 'uv_timer_t*' 56 | uv_handle_init = lib.uv_timer_init 57 | 58 | def __init__(self, loop=None, on_timeout=None): 59 | super(Timer, self).__init__(loop) 60 | self.uv_timer = self.base_handle.uv_object 61 | self.on_timeout = on_timeout or common.dummy_callback 62 | """ 63 | Callback which should run on timeout. 64 | 65 | 66 | .. function:: on_timeout(timer_handle) 67 | 68 | :param timer_handle: 69 | handle the call originates from 70 | 71 | :type timer_handle: 72 | uv.Timer 73 | 74 | 75 | :readonly: 76 | False 77 | :type: 78 | ((uv.Timer) -> None) | ((Any, uv.Timer) -> None) 79 | """ 80 | 81 | @property 82 | def repeat(self): 83 | """ 84 | The repeat interval value in milliseconds. The timer will be 85 | scheduled to run on the given interval, regardless of the 86 | callback execution duration, and will follow normal timer 87 | semantics in the case of time-slice overrun. 88 | 89 | For example, if a 50ms repeating timer first runs for 17ms, 90 | it will be scheduled to run again 33ms later. If other tasks 91 | consume more than the 33ms following the first timer callback, 92 | then the callback will run as soon as possible. 93 | 94 | .. note:: 95 | If the repeat value is set from a timer callback it does 96 | not immediately take effect. If the timer was non-repeating 97 | before, it will have been stopped. If it was repeating, 98 | then the old repeat value will have been used to schedule 99 | the next timeout. 100 | 101 | :raises uv.ClosedHandleError: 102 | handle has already been closed or is closing 103 | 104 | :readonly: 105 | False 106 | :rtype: 107 | int 108 | """ 109 | if self.closing: 110 | raise error.ClosedHandleError() 111 | return lib.uv_timer_get_repeat(self.uv_timer) 112 | 113 | @repeat.setter 114 | def repeat(self, repeat): 115 | """ 116 | :raises uv.ClosedHandleError: 117 | handle has already been closed or is closing 118 | 119 | :param repeat: 120 | repeat interval which should be set in milliseconds 121 | 122 | :type repeat: 123 | int 124 | """ 125 | if self.closing: 126 | raise error.ClosedHandleError() 127 | lib.uv_timer_set_repeat(self.uv_timer, repeat) 128 | 129 | def again(self): 130 | """ 131 | Stop the timer, and if it is repeating restart it using the 132 | repeat value as the timeout. If the timer has never been 133 | started before it raises :class:`uv.error.ArgumentError`. 134 | 135 | :raises uv.UVError: 136 | error while restarting the timer 137 | :raises uv.ClosedHandleError: 138 | handle has already been closed or is closing 139 | """ 140 | if self.closing: 141 | raise error.ClosedHandleError 142 | code = lib.uv_timer_again(self.uv_timer) 143 | if code != error.StatusCodes.SUCCESS: 144 | raise error.UVError(code) 145 | if self.repeat: 146 | self.set_pending() 147 | 148 | def start(self, timeout, repeat=0, on_timeout=None): 149 | """ 150 | Start the timer. If `timeout` is zero, the callback fires on 151 | the next event loop iteration. If repeat is non-zero, the 152 | callback fires first after `timeout` milliseconds and then 153 | repeatedly after `repeat` milliseconds. 154 | 155 | :raises uv.UVError: 156 | error while starting the handle 157 | :raises uv.ClosedHandleError: 158 | handle has already been closed or is closing 159 | 160 | :param timeout 161 | timeout to be used (in milliseconds) 162 | :param repeat: 163 | repeat interval to be used (in milliseconds) 164 | :param on_timeout: 165 | callback which should run on timeout (overrides the current 166 | callback if specified) 167 | 168 | :type timeout: 169 | int 170 | :type repeat: 171 | int 172 | :type on_timeout: 173 | ((uv.Timer) -> None) | ((Any, uv.Timer) -> None) 174 | """ 175 | if self.closing: 176 | raise error.ClosedHandleError() 177 | self.on_timeout = on_timeout or self.on_timeout 178 | code = lib.uv_timer_start(self.uv_timer, uv_timer_cb, timeout, repeat) 179 | if code != error.StatusCodes.SUCCESS: 180 | raise error.UVError(code) 181 | self.set_pending() 182 | 183 | def stop(self): 184 | """ 185 | Stop the handle. The callback will no longer be called. 186 | 187 | :raises uv.UVError: 188 | error while stopping the handle 189 | """ 190 | if self.closing: 191 | return 192 | code = lib.uv_timer_stop(self.uv_timer) 193 | if code != error.StatusCodes.SUCCESS: 194 | raise error.UVError(code) 195 | self.clear_pending() 196 | -------------------------------------------------------------------------------- /uv/handles/tty.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | 16 | from __future__ import absolute_import, division, print_function, unicode_literals 17 | 18 | from .. import common, error, handle 19 | from ..library import ffi, lib 20 | 21 | from . import stream 22 | 23 | 24 | class ConsoleSize(tuple): 25 | def __new__(cls, width, height): 26 | return tuple.__new__(cls, (width, height)) 27 | 28 | @property 29 | def width(self): 30 | """ 31 | Width of the console. 32 | 33 | :readonly: 34 | True 35 | :type: 36 | int 37 | """ 38 | return self[0] 39 | 40 | @property 41 | def height(self): 42 | """ 43 | Height of the console. 44 | 45 | :readonly: 46 | True 47 | :type: 48 | int 49 | """ 50 | return self[1] 51 | 52 | 53 | def reset_mode(): 54 | """ 55 | To be called when the program exits. Resets TTY settings to default 56 | values for the next process to take over. 57 | 58 | This function is async signal-safe on Unix platforms but can fail 59 | with error code UV_EBUSY if you call it when execution is inside 60 | `set_mode()`. 61 | 62 | :raises uv.UVError: 63 | error while resetting tty mode 64 | """ 65 | code = lib.uv_tty_reset_mode() 66 | if code != error.StatusCodes.SUCCESS: 67 | raise error.UVError(code) 68 | 69 | 70 | class TTYMode(common.Enumeration): 71 | """ 72 | Terminal modes enumeration. 73 | """ 74 | 75 | NORMAL = lib.UV_TTY_MODE_NORMAL 76 | """ 77 | Initial normal terminal mode. 78 | 79 | :type: uv.TTYMode 80 | """ 81 | 82 | RAW = lib.UV_TTY_MODE_RAW 83 | """ 84 | Raw input mode (on windows, `ENABLE_WINDOW_INPUT` is also enabled). 85 | 86 | :type: uv.TTYMode 87 | """ 88 | 89 | IO = lib.UV_TTY_MODE_IO 90 | """ 91 | Binary-safe IO mode for IPC (Unix only). 92 | 93 | :type: uv.TTYMode 94 | """ 95 | 96 | 97 | @handle.HandleTypes.TTY 98 | class TTY(stream.UVStream): 99 | """ 100 | Stream interface to the local user terminal console. It allows 101 | using ANSI escape codes across platforms. 102 | 103 | :raises uv.UVError: 104 | error while initializing the handle 105 | 106 | :param fd: 107 | file descriptor of the console 108 | :param readable: 109 | specifies whether the file descriptor is readable or not 110 | :param loop: 111 | event loop the handle should run on 112 | :param on_read: 113 | callback which should be called when data has been read 114 | 115 | 116 | :type fd: 117 | int 118 | :type readable: 119 | bool 120 | :type loop: 121 | uv.Loop 122 | :type on_read: 123 | ((uv.TTY, uv.StatusCodes, bytes) -> None) | 124 | ((Any, uv.TTY, uv.StatusCodes, bytes) -> None) 125 | """ 126 | 127 | __slots__ = ['uv_tty'] 128 | 129 | uv_handle_type = 'uv_tty_t*' 130 | uv_handle_init = lib.cross_uv_tty_init 131 | 132 | def __init__(self, fd, readable=False, loop=None, on_read=None): 133 | super(TTY, self).__init__(loop, False, (fd, int(readable)), on_read, None) 134 | self.uv_tty = self.base_handle.uv_object 135 | 136 | @property 137 | def console_size(self): 138 | """ 139 | Current size of the console. 140 | 141 | :raises uv.UVError: 142 | error while getting console size 143 | 144 | :rtype: 145 | ConsoleSize 146 | """ 147 | if self.closing: 148 | return ConsoleSize(0, 0) 149 | c_with, c_height = ffi.new('int*'), ffi.new('int*') 150 | code = lib.uv_tty_get_winsize(self.uv_tty, c_with, c_height) 151 | if code != error.StatusCodes.SUCCESS: 152 | raise error.UVError(code) 153 | return ConsoleSize(c_with[0], c_height[0]) 154 | 155 | def set_mode(self, mode=TTYMode.NORMAL): 156 | """ 157 | Set the the specified terminal mode. 158 | 159 | :raises uv.UVError: 160 | error while setting mode 161 | :raises uv.ClosedHandleError: 162 | handle has already been closed or is closing 163 | 164 | :param mode: 165 | mode to set 166 | 167 | :type mode: 168 | uv.TTYMode 169 | """ 170 | if self.closing: 171 | raise error.ClosedHandleError() 172 | code = lib.uv_tty_set_mode(self.uv_tty, mode) 173 | if code != error.StatusCodes.SUCCESS: 174 | raise error.UVError(code) 175 | 176 | @property 177 | def family(self): 178 | return None 179 | -------------------------------------------------------------------------------- /uv/helpers/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | -------------------------------------------------------------------------------- /uv/helpers/mock.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | 16 | from __future__ import absolute_import, division, print_function, unicode_literals 17 | 18 | 19 | MOCK_CONSTANTS = { 20 | 'UV_RUN_DEFAULT': 0, 21 | 'UV_RUN_ONCE': 1, 22 | 'UV_RUN_NOWAIT': 2, 23 | 24 | 'UV_READABLE': 1, 25 | 'UV_WRITABLE': 2, 26 | 27 | 'UV_RENAME': 1, 28 | 'UV_CHANGE': 2, 29 | 30 | 'UV_FS_EVENT_WATCH_ENTRY': 1, 31 | 'UV_FS_EVENT_STAT': 2, 32 | 'UV_FS_EVENT_RECURSIVE': 4, 33 | 34 | 'UV_TCP_IPV6ONLY': 1, 35 | 36 | 'UV_UDP_IPV6ONLY': 1, 37 | 'UV_UDP_PARTIAL': 2, 38 | 'UV_UDP_REUSEADDR': 4, 39 | 40 | 'UV_LEAVE_GROUP': 0, 41 | 'UV_JOIN_GROUP': 1, 42 | 43 | 'UV_CREATE_PIPE': 1, 44 | 'UV_READABLE_PIPE': 16, 45 | 'UV_WRITABLE_PIPE': 32, 46 | 47 | 'UV_PROCESS_DETACHED': 1 << 3, 48 | 'UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS': 1 << 2, 49 | 'UV_PROCESS_WINDOWS_HIDE': 1 << 4, 50 | 51 | 'UV_E2BIG': -7, 52 | 'UV_EACCES': -13, 53 | 'UV_EADDRINUSE': -98, 54 | 'UV_EADDRNOTAVAIL': -99, 55 | 'UV_EAFNOSUPPORT': -97, 56 | 'UV_EAGAIN': -11, 57 | 'UV_EAI_ADDRFAMILY': -3000, 58 | 'UV_EAI_AGAIN': -3001, 59 | 'UV_EAI_BADFLAGS': -3002, 60 | 'UV_EAI_BADHINTS': -3013, 61 | 'UV_EAI_CANCELED': -3003, 62 | 'UV_EAI_FAIL': -3004, 63 | 'UV_EAI_FAMILY': -3005, 64 | 'UV_EAI_MEMORY': -3006, 65 | 'UV_EAI_NODATA': -3007, 66 | 'UV_EAI_NONAME': -3008, 67 | 'UV_EAI_OVERFLOW': -3009, 68 | 'UV_EAI_PROTOCOL': -3014, 69 | 'UV_EAI_SERVICE': -3010, 70 | 'UV_EAI_SOCKTYPE': -3011, 71 | 'UV_EALREADY': -114, 72 | 'UV_EBADF': -9, 73 | 'UV_EBUSY': -16, 74 | 'UV_ECANCELED': -125, 75 | 'UV_ECHARSET': -4080, 76 | 'UV_ECONNABORTED': -103, 77 | 'UV_ECONNREFUSED': -111, 78 | 'UV_ECONNRESET': -104, 79 | 'UV_EDESTADDRREQ': -89, 80 | 'UV_EEXIST': -17, 81 | 'UV_EFAULT': -14, 82 | 'UV_EFBIG': -27, 83 | 'UV_EHOSTUNREACH': -113, 84 | 'UV_EINTR': -4, 85 | 'UV_EINVAL': -22, 86 | 'UV_EIO': -5, 87 | 'UV_EISCONN': -106, 88 | 'UV_EISDIR': -21, 89 | 'UV_ELOOP': -40, 90 | 'UV_EMFILE': -24, 91 | 'UV_EMSGSIZE': -90, 92 | 'UV_ENAMETOOLONG': -36, 93 | 'UV_ENETDOWN': -100, 94 | 'UV_ENETUNREACH': -101, 95 | 'UV_ENFILE': -23, 96 | 'UV_ENOBUFS': -105, 97 | 'UV_ENODEV': -19, 98 | 'UV_ENOENT': -2, 99 | 'UV_ENOMEM': -12, 100 | 'UV_ENONET': -64, 101 | 'UV_ENOPROTOOPT': -92, 102 | 'UV_ENOSPC': -28, 103 | 'UV_ENOSYS': -38, 104 | 'UV_ENOTCONN': -107, 105 | 'UV_ENOTDIR': -20, 106 | 'UV_ENOTEMPTY': -39, 107 | 'UV_ENOTSOCK': -88, 108 | 'UV_ENOTSUP': -95, 109 | 'UV_EPERM': -1, 110 | 'UV_EPIPE': -32, 111 | 'UV_EPROTO': -71, 112 | 'UV_EPROTONOSUPPORT': -93, 113 | 'UV_EPROTOTYPE': -91, 114 | 'UV_ERANGE': -34, 115 | 'UV_EROFS': -30, 116 | 'UV_ESHUTDOWN': -108, 117 | 'UV_ESPIPE': -29, 118 | 'UV_ESRCH': -3, 119 | 'UV_ETIMEDOUT': -110, 120 | 'UV_ETXTBSY': -26, 121 | 'UV_EXDEV': -18, 122 | 'UV_UNKNOWN': -4094, 123 | 'UV_EOF': -4095, 124 | 'UV_ENXIO': -6, 125 | 'UV_EMLINK': -31, 126 | 'UV_EHOSTDOWN': -112 127 | } 128 | 129 | 130 | class Mock(object): 131 | def __getattr__(self, name): 132 | try: 133 | return MOCK_CONSTANTS[name] 134 | except KeyError: 135 | return Mock() 136 | 137 | def __call__(self, *args): 138 | return args[0] if len(args) == 1 and type(args[0]) == type else 0 139 | 140 | def __or__(self, _): 141 | return 0 142 | 143 | @staticmethod 144 | def uv_version_string(): 145 | return '0.0.0' 146 | 147 | @staticmethod 148 | def uv_version(): 149 | return 0 150 | 151 | @staticmethod 152 | def string(string): 153 | return string.encode() 154 | 155 | @staticmethod 156 | def callback(*_): 157 | return Mock() 158 | -------------------------------------------------------------------------------- /uv/helpers/tracer.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | 16 | from __future__ import absolute_import, division, print_function, unicode_literals 17 | 18 | import functools 19 | import inspect 20 | 21 | import uvcffi 22 | 23 | 24 | wrapper_codes = set() 25 | 26 | 27 | class LIBTracer(object): 28 | def __init__(self): 29 | self.wrappers = {} 30 | 31 | def __getattr__(self, name): 32 | item = getattr(uvcffi.lib, name) 33 | if name in self.wrappers: return self.wrappers[name] 34 | if callable(item): 35 | def trace_wrapper(*arguments): 36 | stack = [info for info in inspect.stack() 37 | if info.frame.f_code not in wrapper_codes] 38 | stack.reverse() 39 | self.on_call(stack, item, arguments) 40 | result = item(*arguments) 41 | self.on_return(stack, item, arguments, result) 42 | return result 43 | self.wrappers[name] = trace_wrapper 44 | wrapper_codes.add(trace_wrapper.__code__) 45 | return trace_wrapper 46 | return item 47 | 48 | @staticmethod 49 | def on_call(stack, function, arguments): 50 | trace = ' -> '.join(map(lambda info: info.function, stack)) 51 | print('lib-trace :', trace) 52 | print('lib-call : {}{}'.format(function.__name__, arguments)) 53 | 54 | @staticmethod 55 | def on_return(stack, function, arguments, result): 56 | trace = ' -> '.join(map(lambda info: info.function, stack)) 57 | print('lib-trace :', trace) 58 | print('lib-return : {}{}: {}'.format(function.__name__, arguments, result)) 59 | 60 | 61 | class FFITracer(object): 62 | def __getattr__(self, name): 63 | return getattr(uvcffi.ffi, name) 64 | 65 | def callback(self, callback_type, function=None): 66 | if function is None: return functools.partial(self.callback, callback_type) 67 | 68 | def wrapper(*arguments): 69 | stack = [info for info in inspect.stack() 70 | if info.frame.f_code not in wrapper_codes] 71 | stack.reverse() 72 | self.on_callback_call(stack, function, arguments) 73 | result = function(*arguments) 74 | self.on_callback_return(stack, function, arguments, result) 75 | return result 76 | 77 | wrapper_codes.add(wrapper.__code__) 78 | return uvcffi.ffi.callback(callback_type, wrapper) 79 | 80 | @staticmethod 81 | def on_callback_call(stack, function, arguments): 82 | trace = ' -> '.join(map(lambda info: info.function, stack)) 83 | print('callback-trace :', trace) 84 | print('callback-call : {}{}'.format(function.__name__, arguments)) 85 | 86 | @staticmethod 87 | def on_callback_return(stack, function, arguments, result): 88 | trace = ' -> '.join(map(lambda info: info.function, stack)) 89 | print('callback-trace :', trace) 90 | print('callback-return: {}{}: {}'.format(function.__name__, arguments, result)) 91 | -------------------------------------------------------------------------------- /uv/library.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | 16 | from __future__ import print_function, unicode_literals, division, absolute_import 17 | 18 | import collections 19 | import os 20 | import sys 21 | import weakref 22 | 23 | from . import __version__ 24 | 25 | 26 | if os.environ.get('PYTHON_MOCK_LIBUV', None) == 'True': # pragma: no cover 27 | from types import ModuleType 28 | 29 | from .helpers.mock import Mock 30 | 31 | uvcffi = ModuleType('uvcffi') 32 | uvcffi.__version__ = __version__ 33 | uvcffi.ffi = Mock() 34 | uvcffi.lib = Mock() 35 | 36 | sys.modules['uvcffi'] = uvcffi 37 | 38 | c_library_version = __version__ 39 | else: 40 | import uvcffi 41 | 42 | c_library_version = uvcffi.ffi.string(uvcffi.lib.PYTHON_UV_CFFI_VERSION).decode() 43 | 44 | 45 | if uvcffi.__version__ != __version__: # pragma: no cover 46 | raise RuntimeError('incompatible cffi base library (%s)' % uvcffi.__version__) 47 | 48 | if c_library_version != __version__: # pragma: no cover 49 | raise RuntimeError('incompatible cffi c library (%s)' % c_library_version) 50 | 51 | 52 | trace_uvcffi = os.environ.get('PYTHON_TRACE_LIBUV', None) == 'True' 53 | 54 | if trace_uvcffi: # pragma: no cover 55 | from .helpers.tracer import LIBTracer, FFITracer 56 | 57 | lib = LIBTracer() 58 | ffi = FFITracer() 59 | else: 60 | ffi = uvcffi.ffi 61 | lib = uvcffi.lib 62 | 63 | 64 | Version = collections.namedtuple('Version', ['string', 'major', 'minor', 'patch']) 65 | version_string = ffi.string(lib.uv_version_string()).decode() 66 | version_hex = lib.uv_version() 67 | version_major = (version_hex >> 16) & 0xff 68 | version_minor = (version_hex >> 8) & 0xff 69 | version_patch = version_hex & 0xff 70 | version = Version(version_string, version_major, version_minor, version_patch) 71 | 72 | 73 | def uv_buffer_set(uv_buffer, c_base, length): 74 | """ 75 | Set libuv buffer information. 76 | 77 | :param uv_buffer: 78 | libuv buffer 79 | :param c_base: 80 | buffer base which should be set 81 | :param length: 82 | buffer length which should be set 83 | 84 | :type uv_buffer: 85 | ffi.CData[uv_buf_t] 86 | :type c_base: 87 | ffi.CData[char*] 88 | :type length: 89 | int 90 | """ 91 | lib.py_uv_buf_set(uv_buffer, c_base, length) 92 | 93 | 94 | UVBuffer = collections.namedtuple('UVBuffer', ['base', 'length']) 95 | 96 | 97 | def uv_buffer_get(uv_buffer): 98 | """ 99 | Get libuv buffer information. 100 | 101 | :param uv_buffer: 102 | libuv buffer 103 | 104 | :type uv_buffer: 105 | ffi.CData[uv_buf_t] 106 | 107 | :return: 108 | buffer information `(base, len)` 109 | :rtype: 110 | UVBuffer[ffi.CData[char*], int] 111 | """ 112 | length_pointer = ffi.new('unsigned long*') 113 | return UVBuffer(lib.py_uv_buf_get(uv_buffer, length_pointer), length_pointer[0]) 114 | 115 | 116 | _c_dependencies = weakref.WeakKeyDictionary() 117 | 118 | 119 | def c_require(structure, *requirements): 120 | try: 121 | _c_dependencies[structure].append(requirements) 122 | except KeyError: 123 | _c_dependencies[structure] = [requirements] 124 | 125 | 126 | def make_uv_buffers(iterable_or_bytes): 127 | if isinstance(iterable_or_bytes, bytes): 128 | buffers = (iterable_or_bytes, ) 129 | elif isinstance(iterable_or_bytes, (list, tuple)): 130 | buffers = iterable_or_bytes 131 | elif isinstance(iterable_or_bytes, collections.Iterable): 132 | buffers = [bytes(item) for item in iterable_or_bytes] 133 | else: 134 | raise Exception('fix me') 135 | c_buffers = [ffi.new('char[]', item) for item in buffers] 136 | uv_buffers = ffi.new('uv_buf_t[]', len(buffers)) 137 | c_require(uv_buffers, c_buffers) 138 | for index, c_base in enumerate(c_buffers): 139 | lib.py_uv_buf_set(uv_buffers + index, c_base, len(c_base) - 1) 140 | return uv_buffers 141 | -------------------------------------------------------------------------------- /uv/metadata.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | 16 | __version__ = '0.1.0.dev0' 17 | __project__ = 'Python libuv CFFI Bindings' 18 | __author__ = 'Maximilian Köhl' 19 | __email__ = 'mail@koehlma.de' 20 | -------------------------------------------------------------------------------- /uv/misc.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | 16 | from __future__ import print_function, unicode_literals, division, absolute_import 17 | 18 | from collections import namedtuple 19 | 20 | 21 | from . import dns, error, handle 22 | from .library import ffi, lib 23 | 24 | Timeval = namedtuple('Timeval', ['sec', 'usec']) 25 | 26 | ResourceUsage = namedtuple('ResourceUsage', ['utime', 'stime', 'maxrss', 'ixrss', 27 | 'idrss', 'isrss', 'minflt', 'majflt', 28 | 'nswap', 'inblock', 'oublock', 'msgsnd', 29 | 'msgrcv', 'nsignals', 'nvcsw', 'nivcsw']) 30 | 31 | CpuTimes = namedtuple('CpuTimes', ['user', 'nice', 'sys', 'idle', 'irq']) 32 | CpuInfo = namedtuple('CpuInfo', ['model', 'speed', 'times']) 33 | 34 | InterfaceAddress = namedtuple('InterfaceAddress', ['name', 'physical', 'internal', 35 | 'address', 'netmask']) 36 | 37 | 38 | def unpack_timeval(uv_timeval): 39 | return Timeval(uv_timeval.tv_sec, uv_timeval.tv_usec) 40 | 41 | 42 | def unpack_resource_usage(uv_resource_usage): 43 | pass 44 | 45 | 46 | def unpack_cpu_times(uv_cpu_times): 47 | return CpuTimes(uv_cpu_times.user, uv_cpu_times.nice, uv_cpu_times.sys, 48 | uv_cpu_times.idle, uv_cpu_times.irq) 49 | 50 | 51 | def unpack_cpu_info(uv_cpu_info): 52 | model = ffi.string(uv_cpu_info.model).decode() 53 | return CpuInfo(model, uv_cpu_info.speed, unpack_cpu_times(uv_cpu_info.cpu_times)) 54 | 55 | 56 | def unpack_interface_address(uv_interface_address): 57 | name = ffi.string(uv_interface_address.name).decode() 58 | physical = bytes(ffi.buffer(uv_interface_address.phys_addr, 6)) 59 | internal = bool(uv_interface_address.is_internal) 60 | address = dns.unpack_sockaddr(lib.interface_address_get_address(uv_interface_address)) 61 | netmask = dns.unpack_sockaddr(lib.interface_address_get_netmask(uv_interface_address)) 62 | return InterfaceAddress(name, physical, internal, address, netmask) 63 | 64 | 65 | def guess_handle(fd): 66 | uv_handle = lib.cross_uv_guess_handle(fd) 67 | return handle.HandleTypes(uv_handle).cls 68 | 69 | 70 | def kill(pid, signum): 71 | code = lib.uv_kill(pid, signum) 72 | if code != error.StatusCodes.SUCCESS: 73 | raise error.UVError(code) 74 | 75 | def cpu_info(): 76 | uv_cpu_info_array = ffi.new('uv_cpu_info_t**') 77 | uv_cpu_info_count = ffi.new('int*') 78 | code = lib.uv_cpu_info(uv_cpu_info_array, uv_cpu_info_count) 79 | if code != error.StatusCodes.SUCCESS: 80 | raise error.UVError(code) 81 | result = [] 82 | try: 83 | for index in range(uv_cpu_info_count[0]): 84 | result.append(unpack_cpu_info(uv_cpu_info_array[0][index])) 85 | finally: 86 | lib.uv_free_cpu_info(uv_cpu_info_array[0], uv_cpu_info_count[0]) 87 | return result 88 | 89 | 90 | def hrtime(): 91 | return lib.uv_hrtime() 92 | 93 | 94 | def interface_addresses(): 95 | uv_interface_addresses = ffi.new('uv_interface_address_t**') 96 | c_count = ffi.new('int*') 97 | code = lib.uv_interface_addresses(uv_interface_addresses, c_count) 98 | if code != error.StatusCodes.SUCCESS: 99 | raise error.UVError(code) 100 | addresses = [] 101 | for index in range(c_count[0]): 102 | addresses.append(unpack_interface_address(uv_interface_addresses[0] + index)) 103 | lib.uv_free_interface_addresses(uv_interface_addresses[0], c_count[0]) 104 | return addresses 105 | -------------------------------------------------------------------------------- /uv/request.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | 16 | from __future__ import print_function, unicode_literals, division, absolute_import 17 | 18 | from . import abstract, base, common, error 19 | from .library import lib 20 | from .loop import Loop 21 | 22 | __all__ = ['UVRequest'] 23 | 24 | 25 | class RequestType(common.Enumeration): 26 | UNKNOWN = lib.UV_UNKNOWN_REQ 27 | CONNECT = lib.UV_CONNECT 28 | WRITE = lib.UV_WRITE 29 | SHUTDOWN = lib.UV_SHUTDOWN 30 | FS = lib.UV_FS 31 | WORK = lib.UV_WORK 32 | GETADDRINFO = lib.UV_GETADDRINFO 33 | GETNAMEINFO = lib.UV_GETNAMEINFO 34 | UDP_SEND = lib.UV_UDP_SEND 35 | 36 | def __call__(self, cls): 37 | self.cls = cls 38 | return cls 39 | 40 | 41 | @RequestType.UNKNOWN 42 | class UVRequest(object): 43 | """ 44 | The base class of all libuv based requests. 45 | 46 | :raises uv.LoopClosedError: 47 | loop has already been closed 48 | 49 | :param loop: 50 | loop where the request should run on 51 | :param arguments: 52 | arguments passed to the libuv request initializer 53 | :param uv_handle: 54 | libuv handle the requests belongs to 55 | :param request_init: 56 | libuv function for request initialization 57 | 58 | :type loop: 59 | Loop 60 | :type arguments: 61 | tuple 62 | :type uv_handle: 63 | ffi.CData 64 | :type request_init: 65 | callable 66 | """ 67 | 68 | __slots__ = ['__weakref__', 'loop', 'finished', 'base_request'] 69 | 70 | uv_request_type = None 71 | uv_request_init = None 72 | 73 | def __init__(self, loop, arguments, uv_handle=None, request_init=None): 74 | self.loop = loop or Loop.get_current() 75 | """ 76 | Loop where the request is running on. 77 | 78 | :readonly: True 79 | :type: Loop 80 | """ 81 | self.finished = False 82 | """ 83 | Request has been finished. 84 | 85 | :readonly: True 86 | :type: bool 87 | """ 88 | if self.loop.closed: 89 | self.finished = True 90 | raise error.ClosedLoopError() 91 | self.base_request = base.BaseRequest(self, self.loop.base_loop, 92 | self.__class__.uv_request_type, 93 | request_init or 94 | self.__class__.uv_request_init, 95 | arguments, 96 | uv_handle=uv_handle) 97 | self.set_pending() 98 | 99 | @property 100 | def type(self): 101 | """ 102 | Type of the request. Returns a subclass of :class:`uv.UVRequest`. 103 | 104 | :type: type 105 | """ 106 | return RequestType(self.base_request.uv_request.type).cls 107 | 108 | def cancel(self): 109 | """ 110 | :raises uv.UVError: error while canceling request 111 | """ 112 | code = self.base_request.cancel() 113 | if code != error.StatusCodes.SUCCESS: 114 | raise error.UVError(code) 115 | 116 | def set_pending(self): 117 | """ 118 | .. warning:: 119 | This method is only for internal purposes and is not part 120 | of the official API. 121 | """ 122 | self.loop.structure_set_pending(self) 123 | 124 | def clear_pending(self): 125 | """ 126 | .. warning:: 127 | This method is only for internal purposes and is not part 128 | of the official API. 129 | """ 130 | self.loop.structure_clear_pending(self) 131 | 132 | 133 | RequestType.cls = UVRequest 134 | 135 | abstract.Request.register(UVRequest) 136 | -------------------------------------------------------------------------------- /uvcffi/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koehlma/uv/f2d33af895054511cee1f3383b69800d35d98d8e/uvcffi/.keep --------------------------------------------------------------------------------