├── .editorconfig ├── .gitignore ├── LICENSE ├── MANIFEST.in ├── README.rst ├── asynciomagic.py ├── setup.cfg └── setup.py /.editorconfig: -------------------------------------------------------------------------------- 1 | ; This file is for unifying the coding style for different editors and IDEs. 2 | ; More information at http://EditorConfig.org 3 | 4 | ; Editors and IDEs plugins: 5 | ; — SublimeText — https://github.com/sindresorhus/editorconfig-sublime 6 | ; — Vim — https://github.com/editorconfig/editorconfig-vim 7 | ; — JetBrains IDEs — https://github.com/editorconfig/editorconfig-jetbrains 8 | 9 | root = true 10 | 11 | [*] 12 | charset = utf-8 13 | end_of_line = lf 14 | insert_final_newline = true 15 | trim_trailing_whitespace = true 16 | 17 | [*.{py,md,ini}] 18 | indent_style = space 19 | indent_size = 4 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### Python ### 2 | # Byte-compiled / optimized / DLL files 3 | __pycache__/ 4 | *.py[cod] 5 | *$py.class 6 | 7 | # C extensions 8 | *.so 9 | 10 | # Distribution / packaging 11 | .Python 12 | env/ 13 | build/ 14 | develop-eggs/ 15 | dist/ 16 | downloads/ 17 | eggs/ 18 | .eggs/ 19 | lib/ 20 | lib64/ 21 | parts/ 22 | sdist/ 23 | var/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *,cover 47 | .hypothesis/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Sphinx documentation 54 | docs/_build/ 55 | 56 | # PyBuilder 57 | target/ 58 | 59 | 60 | ### Django ### 61 | *.log 62 | *.pot 63 | *.pyc 64 | __pycache__/ 65 | local_settings.py 66 | db.sqlite3 67 | media 68 | 69 | 70 | ### SublimeText ### 71 | # cache files for sublime text 72 | *.tmlanguage.cache 73 | *.tmPreferences.cache 74 | *.stTheme.cache 75 | 76 | # workspace files are user-specific 77 | *.sublime-workspace 78 | 79 | # project files should be checked into the repository, unless a significant 80 | # proportion of contributors will probably not be using SublimeText 81 | *.sublime-project 82 | 83 | # sftp configuration file 84 | sftp-config.json 85 | 86 | 87 | ### Other ### 88 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Nikita Grishko 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.rst 2 | 3 | global-exclude __pycache__ 4 | global-exclude *.py[co] 5 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | asyncio-ipython-magic |Requirements Status| |PyPI| |Supported Python versions| 2 | ============================================================================== 3 | 4 | An extension for `IPython `__ that help to run 5 | AsyncIO code in your interactive session. 6 | 7 | Based on 8 | `Gist `__. 9 | 10 | Installation 11 | ------------ 12 | 13 | Install ``asyncio-ipython-magic`` using 14 | `pip `__: 15 | 16 | :: 17 | 18 | $ pip install asyncio-ipython-magic 19 | 20 | ...or directly from the repository using the ``%install_ext`` magic 21 | command: 22 | 23 | :: 24 | 25 | $ In[1]: %install_ext https://raw.githubusercontent.com/Gr1N/asyncio-ipython-magic/master/asynciomagic.py 26 | 27 | Enjoy! 28 | 29 | Usage 30 | ----- 31 | 32 | :: 33 | 34 | In [1]: %load_ext asynciomagic 35 | 36 | In [2]: import asyncio 37 | 38 | In [3]: import time 39 | 40 | In [4]: async def foo(): 41 | ...: i = 0 42 | ...: while i < 3: 43 | ...: print('time =', time.time()) 44 | ...: i += 1 45 | ...: await asyncio.sleep(2) 46 | ...: 47 | 48 | In [5]: %%async_ 49 | ...: await foo() 50 | ...: 51 | time = 1478985421.307329 52 | time = 1478985423.309606 53 | time = 1478985425.31514 54 | 55 | In [6]: %await_ foo() 56 | time = 1487097377.700184 57 | time = 1487097379.705614 58 | time = 1487097381.707186 59 | 60 | In [7]: 61 | 62 | Testing 63 | ------- 64 | 65 | It just works, I hope. 66 | 67 | License 68 | ------- 69 | 70 | *asyncio-ipython-magic* is licensed under the MIT license. See the 71 | license file for details. 72 | 73 | .. |Requirements Status| image:: https://requires.io/github/Gr1N/asyncio-ipython-magic/requirements.svg?branch=master 74 | :target: https://requires.io/github/Gr1N/asyncio-ipython-magic/requirements/?branch=master 75 | .. |PyPI| image:: https://img.shields.io/pypi/v/asyncio-ipython-magic.svg 76 | :target: https://pypi.python.org/pypi/asyncio-ipython-magic 77 | .. |Supported Python versions| image:: https://img.shields.io/pypi/pyversions/asyncio-ipython-magic.svg 78 | :target: https://pypi.python.org/pypi/asyncio-ipython-magic 79 | -------------------------------------------------------------------------------- /asynciomagic.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import ast 4 | from ast import ( 5 | Call, 6 | Attribute, 7 | Name, 8 | Load, 9 | ) 10 | import asyncio as _asyncio 11 | 12 | from IPython.core.magic import ( 13 | Magics, 14 | magics_class, 15 | cell_magic, 16 | line_magic, 17 | ) 18 | from IPython.utils.text import indent 19 | 20 | 21 | class RewriteAwait(ast.NodeTransformer): 22 | def visit_Await(self, node): 23 | self.generic_visit(node) 24 | 25 | new = Call( 26 | func=Attribute( 27 | value=Call( 28 | func=Attribute( 29 | value=Name( 30 | id='_asyncio', 31 | ctx=Load() 32 | ), 33 | attr='get_event_loop', 34 | ctx=Load()), 35 | args=[], 36 | keywords=[] 37 | ), 38 | attr='run_until_complete', 39 | ctx=Load() 40 | ), 41 | args=[node.value], 42 | keywords=[] 43 | ) 44 | 45 | return ast.copy_location(new, node) 46 | 47 | def visit_AsyncFunctionDef(self, node): 48 | # Don't transform awaits inside an 'async def' function 49 | return node 50 | 51 | def visit_Return(self, node): 52 | raise SyntaxError('Return outside function definition') 53 | 54 | 55 | @magics_class 56 | class AsyncIOMagics(Magics): 57 | @line_magic 58 | def await_(self, line): 59 | expr = 'async def __f(): await {line}'.format(line=indent(line)) 60 | 61 | self._exec(expr) 62 | 63 | @cell_magic 64 | def async_(self, line, cell): 65 | expr = 'async def __f():\n{cell}'.format(cell=indent(cell)) 66 | 67 | self._exec(expr) 68 | 69 | def _exec(self, expr): 70 | expr = ast.parse(expr) 71 | expr = expr.body[0].body 72 | 73 | nodes = [RewriteAwait().visit(node) for node in expr] 74 | module = ast.Module(nodes) 75 | ast.fix_missing_locations(module) 76 | 77 | coro = compile(module, filename='', mode='exec') 78 | 79 | self.shell.ex(coro) 80 | 81 | 82 | def load_ipython_extension(ipython): 83 | ipython.user_global_ns['_asyncio'] = _asyncio 84 | ipython.register_magics(AsyncIOMagics) 85 | 86 | 87 | def unload_ipython_extension(ipython): 88 | ipython.user_global_ns.pop('_asyncio', None) 89 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [bdist_wheel] 2 | universal = 1 3 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from setuptools import setup 4 | 5 | readme = open('README.rst').read() 6 | 7 | setup( 8 | name='asyncio-ipython-magic', 9 | version='0.0.3', 10 | description='An extension for IPython that help to run AsyncIO code in ' 11 | 'your interactive session.', 12 | long_description=readme, 13 | author='Nikita Grishko', 14 | author_email='gr1n@protonmain.com', 15 | url='https://github.com/Gr1N/asyncio-ipython-magic', 16 | py_modules=( 17 | 'asynciomagic', 18 | ), 19 | install_requires=( 20 | 'ipython', 21 | ), 22 | classifiers=[ 23 | 'Development Status :: 3 - Alpha', 24 | 'Framework :: IPython', 25 | 'Intended Audience :: Developers', 26 | 'Operating System :: OS Independent', 27 | 'Programming Language :: Python', 28 | 'Programming Language :: Python :: 3', 29 | 'Programming Language :: Python :: 3.5', 30 | 'Programming Language :: Python :: 3.6', 31 | 'Topic :: Software Development :: Libraries :: Python Modules', 32 | ] 33 | ) 34 | --------------------------------------------------------------------------------