├── .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
--------------------------------------------------------------------------------