pax_global_header00006660000000000000000000000064137570632460014527gustar00rootroot0000000000000052 comment=7623ac98121725fc31da8a1d18a7eb8ced705773 pyserial-asyncio-0.5/000077500000000000000000000000001375706324600146665ustar00rootroot00000000000000pyserial-asyncio-0.5/.gitignore000066400000000000000000000001271375706324600166560ustar00rootroot00000000000000**/__pycache__ *.pyc *.pyo documentation/_build build dist *.egg-info /MANIFEST .ideapyserial-asyncio-0.5/.travis.yml000066400000000000000000000004201375706324600167730ustar00rootroot00000000000000# (C) 2016 Chris Liechti # SPDX-License-Identifier: BSD-3-Clause language: python cache: pip python: - 3.5 - 3.6 - 3.7 - 3.8 install: - pip install -r requirements-travis.txt - pip install -e . script: - python test/test_asyncio.py pyserial-asyncio-0.5/CONTRIBUTING.rst000066400000000000000000000030251375706324600173270ustar00rootroot00000000000000============== Contributing ============== How Can I Contribute? ===================== Reporting Bugs and Suggesting Enhancements The easiest is creating an issue_. You can do that for ideas or problems. For bugs, if possible, try to construct a small code example that exhibits the problem. Include version numbers (Python, pySerial, pySerial-asyncio). Pull Requests Code / test / documentation contributions are of course welcome_! Consider that this is a project that aims to run on many platforms and architectures and therefore needs implementations covering all of them, of course there are exceptions and starting on one platform is better than starting on none. When providing new features, also think of documentation and tests if possible. Ownership ========= - By contributing to this project, you agree that the material is labeled as "copyright by pySerial-team". Names or handles of individual contributors are listed in a separate file: CREDITS.rst_. - Your contribution may be used in its original or modified form. - The project maintainer(s) can make decisions in the name of the pySerial-team. Including but not limited to: - making releases, adding/removing features, changing the design, etc. - adding or removing project maintainers - license changes, as long as it remains a permissive open source license. .. _CREDITS.rst: CREDITS.rst .. _issue: https://github.com/pyserial/pyserial-asyncio/issues .. _welcome: https://github.com/pyserial/pyserial-asyncio/pulls pyserial-asyncio-0.5/CREDITS.rst000066400000000000000000000006261375706324600165210ustar00rootroot00000000000000========= Credits ========= Main authors of pySerial-asyncio ================================ - Chris Liechti (zsquareplusc) - Robert Smallshire (rob-smallshire) Contributors ============ - David Ko - Nicolas Di Pietro - jabdoa2 - Chris Seymour - ... not all names may be listed here, see also ``git log`` or online history_ .. _history: https://github.com/pyserial/pyserial-asyncio/commits/master pyserial-asyncio-0.5/LICENSE.txt000066400000000000000000000035341375706324600165160ustar00rootroot00000000000000Copyright (c) 2015-2020 pySerial-team (see CREDITS.rst) All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------- Note: Individual files contain the following tag instead of the full license text. SPDX-License-Identifier: BSD-3-Clause This enables machine processing of license information based on the SPDX License Identifiers that are here available: http://spdx.org/licenses/ pyserial-asyncio-0.5/MANIFEST.in000066400000000000000000000004671375706324600164330ustar00rootroot00000000000000include CREDITS.rst include LICENSE.txt include README.rst include MANIFEST.in include requirements.txt include setup.cfg include documentation/*.rst include documentation/conf.py include documentation/Makefile include documentation/pyserial-asyncio.png include test/__init__.py include test/test_asyncio.py pyserial-asyncio-0.5/README.rst000066400000000000000000000015131375706324600163550ustar00rootroot00000000000000======================================== pyserial-asyncio |build-status| |docs| ======================================== Async I/O extension package for the Python Serial Port Extension for OSX, Linux, BSD It depends on pySerial and is compatible with Python 3.5 and later. Documentation ============= - Documentation: http://pyserial-asyncio.readthedocs.io/en/latest/ - Download Page: https://pypi.python.org/pypi/pyserial-asyncio - Project Homepage: https://github.com/pyserial/pyserial-asyncio .. |build-status| image:: https://travis-ci.org/pyserial/pyserial-asyncio.svg?branch=master :target: https://travis-ci.org/pyserial/pyserial-asyncio :alt: Build status .. |docs| image:: https://readthedocs.org/projects/pyserial-asyncio/badge/?version=latest :target: http://pyserial-asyncio.readthedocs.io/ :alt: Documentation pyserial-asyncio-0.5/documentation/000077500000000000000000000000001375706324600175375ustar00rootroot00000000000000pyserial-asyncio-0.5/documentation/Makefile000066400000000000000000000056651375706324600212130ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d _build/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf _build/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) _build/html @echo @echo "Build finished. The HTML pages are in _build/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) _build/dirhtml @echo @echo "Build finished. The HTML pages are in _build/dirhtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) _build/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) _build/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) _build/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in _build/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) _build/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in _build/qthelp, like this:" @echo "# qcollectiongenerator _build/qthelp/pySerial.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile _build/qthelp/pySerial.qhc" latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) _build/latex @echo @echo "Build finished; the LaTeX files are in _build/latex." @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ "run these through (pdf)latex." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) _build/changes @echo @echo "The overview file is in _build/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) _build/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in _build/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) _build/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in _build/doctest/output.txt." pyserial-asyncio-0.5/documentation/api.rst000066400000000000000000000075631375706324600210550ustar00rootroot00000000000000====================== pySerial-asyncio API ====================== .. module:: serial_asyncio The following high-level functions are provided for initiating a serial connection: .. function:: create_serial_connection(loop, protocol_factory, *args, **kwargs) :async: Open a streaming connection to the specified serial port. :param loop: The *asyncio* event loop :param protocol_factory: A callable which when invoked without arguments and which should return a :class:`asyncio.Protocol` subclass. Most commonly the protocol *class* (*e.g.* ``MyProtocol``) can be passed as this argument. If it is required to use an existing protocol *instance*, pass a zero-argument lambda which evaluates to the instance, such as ``lambda: my_protocol`` :param args: Forwarded to the :class:`serial.Serial` constructor :param kwargs: Forwarded to the :class:`serial.Serial` constructor :returns: A coroutine for managing a serial port connection, which when awaited returns transport and protocol instances. :platform: Posix Use this function to associate an asynchronous call-back based protocol with an new :class:`serial.Serial` instance that will be created on your behalf. The chronological order of the operation is: 1. ``protocol_factory`` is called without arguments and must return a :class:`asyncio.Protocol` subclass instance. 2. The protocol instance is tied to a :class:`~serial_asyncio.SerialTransport`. 3. This coroutine returns successfully with a ``(transport, protocol)`` pair. 4. The :meth:`~serial_asyncio.SerialTransport.connection_made()` method of the protocol will be called at some point by the event loop. .. function:: connection_for_serial(loop, protocol_factory, serial_instance) :async: Open a streaming connection to an existing serial port instance. :param loop: The *asyncio* event loop :param protocol_factory: A callable which when invoked without arguments and which should return a :class:`asyncio.Protocol` subclass. Most commonly the protocol *class* (*e.g.* ``MyProtocol``) can be passed as this argument. If it is required to use an existing protocol *instance*, pass a zero-argument lambda which evaluates to the instance, such as ``lambda: my_protocol`` :param serial_instance: A :class:`serial.Serial` instance. :returns: A coroutine for managing a serial port connection, which when awaited returns transport and protocol instances. :platform: Posix Use this function to associate an asynchronous call-back based protocol with an existing :class:`serial.Serial` instance. The chronological order of the operation is: 1. ``protocol_factory`` is called without arguments and must return a :class:`asyncio.Protocol` subclass instance. 2. The protocol instance is tied to a :class:`~serial_asyncio.SerialTransport`. 3. This coroutine returns successfully with a ``(transport, protocol)`` pair. 4. The :meth:`~serial_asyncio.SerialTransport.connection_made()` method of the protocol will be called at some point by the event loop. .. function:: open_serial_connection(*, loop=None, limit=None, **kwargs) :async: Open a streaming connection to an existing serial port instance. :param loop: The *asyncio* event loop :param limit: A optional buffer limit in bytes for the :class:`asyncio.StreamReader` :param kwargs: Forwarded to the :class:`serial.Serial` constructor :returns: A coroutine for managing a serial port connection, which when awaited returns an :class:`asyncio.StreamReader` and a :class:`asyncio.StreamWriter`. :platform: Posix Use this function to open connections where serial traffic is handled by an asynchronous coroutine interacting with :class:`asyncio.StreamReader` and a :class:`asyncio.StreamWriter` objects. pyserial-asyncio-0.5/documentation/appendix.rst000066400000000000000000000030571375706324600221060ustar00rootroot00000000000000========== Appendix ========== License ======= Copyright (c) 2015-2020 pySerial-team (see CREDITS.rst) All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. pyserial-asyncio-0.5/documentation/conf.py000066400000000000000000000147501375706324600210450ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # pySerial-asyncio documentation build configuration file, created by # sphinx-quickstart on Tue Jul 21 00:27:45 2009. # # This file is execfile()d with the current directory set to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys, os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.append(os.path.abspath('.')) # -- General configuration ----------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [ 'sphinx.ext.intersphinx', 'sphinx_rtd_theme', ] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8' # The master toctree document. master_doc = 'index' # General information about the project. project = u'pySerial-asyncio' copyright = u'2015-2020, pySerial-team' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = '0.5' # The full version, including alpha/beta/rc tags. release = '0.5' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of documents that shouldn't be included in the build. #unused_docs = [] # List of directories, relative to source directory, that shouldn't be searched # for source files. exclude_trees = ['_build'] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. #pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. Major themes that come with # Sphinx are currently 'default' and 'sphinxdoc'. html_theme = "sphinx_rtd_theme" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. html_logo = 'pyserial-asyncio.png' # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_use_modindex = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = '' # Output file base name for HTML help builder. #htmlhelp_basename = 'pySerial-asyncio-doc' # -- Options for LaTeX output -------------------------------------------------- # The paper size ('letter' or 'a4'). #latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). #latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'pySerial-asyncio.tex', u'pySerial-asyncio Documentation', u'pySerial-team', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. latex_logo = 'pyserial-asyncio.png' # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # Additional stuff for the LaTeX preamble. #latex_preamble = '' # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_use_modindex = True # for external links to standard library intersphinx_mapping = { 'python': ('https://docs.python.org/3', None), 'pyserial': ('https://pyserial.readthedocs.io/en/latest', None), } pyserial-asyncio-0.5/documentation/index.rst000066400000000000000000000017311375706324600214020ustar00rootroot00000000000000.. pySerial-asyncio documentation master file Welcome to pySerial-asyncio's documentation =========================================== `Async I/O`_ extension for the `Python Serial Port`_ package for OSX, Linux, BSD It depends on pySerial and is compatible with Python 3.5 and later. .. _`Async I/O`: https://docs.python.org/3/library/asyncio.html .. _`Python Serial Port`: https://pypi.python.org/pypi/pyserial Other pages (online) - `project page on GitHub`_ - `Download Page`_ with releases - This page, when viewed online is at https://pyserial-asyncio.readthedocs.io/en/latest/ or http://pythonhosted.org/pyserial-asyncio/ . .. _Python: http://python.org/ .. _`project page on GitHub`: https://github.com/pyserial/pyserial-asyncio/ .. _`Download Page`: http://pypi.python.org/pypi/pyserial-asyncio Contents: .. toctree:: :maxdepth: 2 shortintro api appendix Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` pyserial-asyncio-0.5/documentation/pyserial-asyncio.png000066400000000000000000000320501375706324600235400ustar00rootroot00000000000000PNG  IHDRpVObKGD pHYs  tIME!(OB IDATxw|\ՙNIdKfl"Wb0N$PB $B !}I@3IdwS^H6!$J@056Wܭbɖեsǹ3f$0g>#>yyRcP ;9A3>@{"0 N9S\; .jl'&dD. ffpXdC300@ PxLv4{ єCt.6;6ش/^90 nPL@)@@w†\CJ$rír!N9rr!akNE-e}o!7GrȌ0mN)% K89%aN}.Mؾ0->lTp6(^{ }aw[O}t)1&sB3h' SH>$4瘾 M+7P]k8g-FKM2)6Aoܘìo%?]TdScec˖-Ks/JSWpTÒ1N1VV"f1Fi*c[ ' 1Cb&*=2UDw_ P!쫁TGL '7*̘1c9|>?;1,0uׯn@/ʶC4ww7h * 49,q3 od1ʞJy:ayC ۛuV精 ܕu^5|ʐdizgHŎSؾ>WzK)ɬƮho5l г%.f8[>y&3 lņ p6N:Pmf{iD:<~Z?D嵟VXPd)̉v6-}4=HН$Ojτlj1ƩqW8b!9ҩZdz1C7 5, iJQÆ1s9G:k4o7F~aga,mK/fG'&ę1&΋U1IvWsWcFJ BǮoyL.dCcBwɛV@G$"PD1c׻kӦMe$kS_APX_|ˈ;rG3F^`*tey0C\c6pVa&/xmsR:fK7dKxA[__ϯ[Q]W;eOX\4Bh2emOb k/É 8Ev 4|QSlh &Yv 谾)0:P2Vsd7ţjM6INmš#L&K8*+B]P䶐g1{я~9^BK$=@yO+ri\6+i{TUUՕ !Z\px<xmy!._֭[ҥK)tj| ;mLPԸxCvF#dUf8[fI2}] x}]s}¼u(ȮQ)jKƺ5\:;/JUWWo*$caQiLo%Mǎ;.9 w+W-t^nJ (ͷzky^6y$:S\0Eۨd:@E}X}FŸ9n#wygqLڼ}(S[1AUlj}P?)O O’#hzvmS^ӨXk*7s9&Zɳ^ v4{6sJ \ >7`ב0O[o|\|IkO$J*,oS{̩ٝ}|!`٭yN>06,"3H 56>^Yn&%0ͨ=YU <6q|%igHgcSTYb{Ki Z[Ĕ''zBє(/{45&y]Ђ*Mw.N[C4)&&ԕ9,ګ)@{N?J_RaNi.S$ 2;evvaTGU 8GZt: 6|^yzҕJTըm<'ΉF93M~w_oIECaG8oDح1έo81۸dvc*ey+3F`Ǒ0*|Sz9vZB̫T%kկ>Sѥ/ʚ:; -sw|}?-]t,pyM jRp';{׌C{,C:~;fA \Dm NC!BQAUOU@R/b{C7]IgCiRDo/,Q0j'|oua9>_[0}0oJ8FI߽O'Hi|niH I(2TѓHs2p0d^g{K()v \)Zx~##̚h7H4Oj[}c:X4>sحe6D^Rr7y$Q .It>`o[xZhRU;pBJ; *L,q7`8HQtT:Czr8r X-J-qjƃF~$߮!z ]3{?oIWV=l\*]7 J66f|z !x`Д8'A@[c^T3+m^t#q ]7T0! u8AIi5nXǟ[a4_C%^hZr%N 6:Màk `Jv3-[f2q}(\RJ"ѢG,%Kh蓻P@e^u202L0I>я~pwjX-* fݠ:~4*o] %6G4Aaaa3 /\|FjAR`uk5vM74=6[9"h~n?qllj2\'eBFIƱ~YL*7{e`E `aJc=fjp8'AՎ1Yz:ZBl q?JTW/~#'vET<^?b6]vFp\>'G,K"QRJbNiҫr7wͭ-! %x;'A'ʩ@13QNcPӯ< =~$on1ƥZNd> 2)N`HCgڰXR]m-!:\]_`GJ|a\U&kIZ7Q͑ N tIS*m5fiѺ >t`Xʾԁ]UVٰ&N"zTb1s#xG=ժU˧Z657ƉR]+ɷPLSUCA>1?[cweHP21mr(#| ړPϝO*9MÑ6-^g}C0kPI)ebF &8q4]]ݪr"lH9"y{c׮]MrbZ1K49iʓZ5*p+-ko"C>jѮPQ?CE7ě@l@ _}!q%[ˮE.sq#"S9uϹ5NJ,حI`մX:A2#׫!ô6v JAT9:+QuUUf$uev ?@inxepP5;I$KbJii[o>]:b75ݧTg>;Lb=&4!gYn×zY>!X bIDATbncǁN+%qR.},M;zo !Ƥs{^{^wI3k#!Ss|߰OGkMCl}nj,1/[ou*Is1héG`dǩo!>P+ֽUW]b}}sϭp³r$aCѱaH'˯R Bm-B#a[KDQ)e:5rR THK6S|%RbW=퉓,(!UBRU%1ub^[j*:#~ Ảdzuxao@'Kƺ- xUüJ3&KOa98_HH)VxQVKcVbk2VeVw˦eYisaGJYw ts,8ګc{NӂNq DŽy߿e:KO-Aaiti F%ᨤ;BcWEe DwZ`F٘a4q#{,[+VEcW-!К&/3%yCɡ~@yq #˳qc>2;t:pM!Z{#*@J!;Z,bou'BP>TJN>8-$v :NpnO=GChZOy MyW_u׮]a͚5x<VLw 89T b{ Hb4N 3N˒ҸO&#2cwQF I h9ȁ vbҾEJg}2FB!e_W/|\0kײbK-|MCG=ʚ6 K% dg?[zg~Дű I.qlIm /5i;OZTIu%ACgFW^"VQeӸd"N_Mˊ8 #F[n#͋jt"Prwj}AgxHhK7vü{$LKZ7^x';qM2(*ݣw,zX,^Sj~C[!(+Cj1+ܹsR&&q$UupND^ V,Hꅿ [&C(sZH85ơI3Gtr<>2*E.[t64$F|7EM5=!~]T ={?k@UkXFz hH1cnve޼yx$>>KK\w> dlM$˦5v n1WؙWQ]g}tu S[fruP~Us`R z:6i"CU_81. И9 :]wݢ}_kT:hNC_\Iه q <t6UTƮ_gx0j8'1kA#NMjlqZIf9c#@ŤՖٙ5k%bƲgOq- ]uɸ< EV$jnz]oJjB%l%ym;v ò)N\6.6>B$ V QEN}*IZ4ӨHdtWKg_1P8n%OF8s<Ԕ8obU-̦M]v#\_|ɽs]wI.;32{jsH4-u< Z~B v0y<.XSS l`=m^`%@K*5I'w$m/N3'NC0ii6VHNma7R+O9fx+W|⮻vvJdR?&Ktyn{ BRD1/v!ݞ '@0zȼ*щT˨O\'DF6?IT׮7JH4dGK>576ƢEz{AӌuQ3?9[SEVW;dKeߤ"O7.BO`p"2}Uܪ0:.IOI\:P5s(0-=Qެ`{KRH)=<㚦]UTTHEu|!e#48ؑ^//p,7&ظq㯽^g3yU tN6vk /f8XJBP֙M/C{~sGVI^Qw},Kd R a& ^ :¼ǔq6a8aUg[{"W83ASp'*^@ `τ0F$o "ST/ 9qjݚSӞ3_Pw#]~l@; ̘`^2&Z(N!Oŀg\=meD҄2+s'c>ϕU6icSG)CpZ"N'΍C\xx8bEt3Ě)%6Ξ0c#ko;yy1u-1Q^hpwvıkeai f+VMiF}ma66T%N3]BCG}d**z:-) \暳P+HJdg"oP%&\DO"0WSaIĢ+=|;ɭ yY!'9& zl6OV!=4' 0ƙ=KmqmIsu@I6gPn9iTu,g8iQ&nƹS\TԤhCgMSif )JIQiX-m̮rnXvs98>gΜW\TԨ.Еz@Mcvչj'aU%*O1U xppag :ĹqKL1ľ{Q1ϖ!.=X]+riuk,"+ne(ͳP=֒6B>M6h=[X`IM h)Fl!y6f 1uK?1`ve$GU ]Gœ=2OCgvy ,=ϗ+Bu:8ڠ`$$2 4;;;AE$L7XT*$=ßO*)A| U/P1Msa8q!VC>: S{'yg7H#R:;eQbЩln!4wM 䣵lI?|wwwelsW켯Y8q6fL 1OFC8f!-VbLi8ę}Z'3':2U׎E~,-~4k`d@dM!>Z'<'#%='[~oF .gFL%q\QTxͷҜ#TX9Jq=FTռ5^)<{TM8-@SRw*H>y" XL`cQT?ۼyJ"Et_7Fth^q?o6-B7|T/~ux!foc.Fp^! :phD%Fy&vd!*{uvCը#*VoYU:t]ԓN2 lh d)Y2*G܃f$3'YX`qWn2V//Q7 8CNk캞愞Mq|”&~a_c7 ;FMzɩ3* C7'-3O~.b2]1XaxC)k\4M/ʁ y~';RDxt]E;Z)QfgH4O5:dF8V!Tϲ=^4Tdtv0ZO*S[[٥_:u9bGE@Xϫ11 > ؟=dR̻ oGd%Y3P=nڇ~QC񂆊s1?ܛilG# Ea? EBdؚA7Q_jϙ Yz u$`G$^*㭷ykkkigggIwwwI___V}*Fe0:2TbC'v:px 4 58_5Q}r 3pV4cac92PcnfIhBI_΋L33qrrC#Gr'r!qr!Gr'r'NqrD! 10'?>9i ,`1Su9b םL<ޛ9p:-VB݌nti9nª{DYTiϵa N32QUϻ?ў)CUyra0tTj{+).T!qv!8Q+0SǞk>@z sePv8q3%IENDB`pyserial-asyncio-0.5/documentation/shortintro.rst000066400000000000000000000102321375706324600225020ustar00rootroot00000000000000======== Overview ======== Serial transports, protocols and streams ---------------------------------------- This module layers `asyncio `_ support onto `pySerial `_. It provides support for working with serial ports through *asyncio* `Transports `_, `Protocols `_, and `Streams `_. Transports are a low-level abstraction, provided by this package in the form of an :class:`asyncio.Transport` implementation called :class:`SerialTransport`, which manages the asynchronous transmission of data through an underlying *pySerial* :class:`~serial.Serial` instance. Transports are concerned with *how* bytes are transmitted through the serial port. Protocols are a callback-based abstraction which determine *which* bytes are transmitted through an underlying transport. You can implement a subclass of :class:`asyncio.Protocol` which reads from, and/or writes to, a :class:`~serial_asyncio.SerialTransport`. When a serial connection is established your protocol will be handed a transport, to which your protocol implementation can write data as necessary. Incoming data and other serial connection lifecycle events cause callbacks on your protocol to be invoked, so it can take action as necessary. Usually, you will not create a :class:`~serial_asyncio.SerialTransport` directly. Rather, you will define a ``Protocol`` class and pass that protocol to a function such as :func:`~serial_asyncio.create_serial_connection()` which will instantiate your ``Protocol`` and connect it to a :class:`~serial_asyncio.SerialTransport`. Streams are a coroutine-based alternative to callback-based protocols. This package provides a function :func:`~serial_asyncio.open_serial_connection` which returns :class:`asyncio.StreamReader` and :class:`asyncio.StreamWriter` objects for interacting with underlying protocol and transport objects, which this library will create for you. Protocol Example ---------------- This example defines a very simple Protocol which sends a greeting message through the serial port and displays to the console any data received through the serial port, until a newline byte is received. A call is made to :func:`create_serial_connection()`, to which the protocol *class* (not an instance) is passed, together with arguments destined for the :class:`~serial.Serial` constructor. This call returns a coroutine object. When passed to :func:`~asyncio.run_until_complete` the coroutine is scheduled to run as an :class:`asyncio.Task` by the *asyncio* library, and the result of the coroutine, which is a tuple containing the transport and protocol instances, return to the caller. While the event loop is running (:meth:`~asyncio.AbstractEventLoop.loop.run_forever`), or until the protocol closes the transport itself, the protocol will process data received through the serial port asynchronously:: import asyncio import serial_asyncio class OutputProtocol(asyncio.Protocol): def connection_made(self, transport): self.transport = transport print('port opened', transport) transport.serial.rts = False # You can manipulate Serial object via transport transport.write(b'Hello, World!\n') # Write serial data via transport def data_received(self, data): print('data received', repr(data)) if b'\n' in data: self.transport.close() def connection_lost(self, exc): print('port closed') self.transport.loop.stop() def pause_writing(self): print('pause writing') print(self.transport.get_write_buffer_size()) def resume_writing(self): print(self.transport.get_write_buffer_size()) print('resume writing') loop = asyncio.get_event_loop() coro = serial_asyncio.create_serial_connection(loop, OutputProtocol, '/dev/ttyUSB0', baudrate=115200) transport, protocol = loop.run_until_complete(coro) loop.run_forever() loop.close() pyserial-asyncio-0.5/requirements-travis.txt000066400000000000000000000003561375706324600214640ustar00rootroot00000000000000# Depend on the head of pyserial git+https://github.com/pyserial/pyserial.git#egg=pyserial # OR # Depend on a specific pyserial commit # git+https://github.com/pyserial/pyserial.git@000b14389171a9f0d7d713466b32bc649b0bed8e#egg=pyserial pyserial-asyncio-0.5/requirements.txt000066400000000000000000000000201375706324600201420ustar00rootroot00000000000000pyserial>=3.1.1 pyserial-asyncio-0.5/serial_asyncio/000077500000000000000000000000001375706324600176725ustar00rootroot00000000000000pyserial-asyncio-0.5/serial_asyncio/__init__.py000066400000000000000000000447531375706324600220200ustar00rootroot00000000000000#!/usr/bin/env python3 # -*- coding: utf-8 -*- # # Implementation of asyncio support. # # This file is part of pySerial. https://github.com/pyserial/pyserial-asyncio # (C) 2015-2020 pySerial-team # # SPDX-License-Identifier: BSD-3-Clause """\ Support asyncio with serial ports. Posix platforms only, Python 3.5+ only. Windows event loops can not wait for serial ports with the current implementation. It should be possible to get that working though. """ import asyncio import os import serial try: import termios except ImportError: termios = None __version__ = '0.5' class SerialTransport(asyncio.Transport): """An asyncio transport model of a serial communication channel. A transport class is an abstraction of a communication channel. This allows protocol implementations to be developed against the transport abstraction without needing to know the details of the underlying channel, such as whether it is a pipe, a socket, or indeed a serial port. You generally won’t instantiate a transport yourself; instead, you will call `create_serial_connection` which will create the transport and try to initiate the underlying communication channel, calling you back when it succeeds. """ def __init__(self, loop, protocol, serial_instance): super().__init__() self._loop = loop self._protocol = protocol self._serial = serial_instance self._closing = False self._protocol_paused = False self._max_read_size = 1024 self._write_buffer = [] self._set_write_buffer_limits() self._has_reader = False self._has_writer = False self._poll_wait_time = 0.0005 # XXX how to support url handlers too # Asynchronous I/O requires non-blocking devices self._serial.timeout = 0 self._serial.write_timeout = 0 # These two callbacks will be enqueued in a FIFO queue by asyncio loop.call_soon(protocol.connection_made, self) loop.call_soon(self._ensure_reader) @property def loop(self): """The asyncio event loop used by this SerialTransport.""" return self._loop @property def serial(self): """The underlying Serial instance. Equivalent to get_extra_info("serial") """ return self._serial def get_extra_info(self, name, default=None): """Get optional transport information. Currently only "serial" is available. """ if name == "serial": return self._serial return default def __repr__(self): return '{self.__class__.__name__}({self.loop}, {self._protocol}, {self.serial})'.format(self=self) def is_closing(self): """Return True if the transport is closing or closed.""" return self._closing def close(self): """Close the transport gracefully. Any buffered data will be written asynchronously. No more data will be received and further writes will be silently ignored. After all buffered data is flushed, the protocol's connection_lost() method will be called with None as its argument. """ if not self._closing: self._close(None) def _read_ready(self): try: data = self._serial.read(self._max_read_size) except serial.SerialException as e: self._close(exc=e) else: if data: self._protocol.data_received(data) def write(self, data): """Write some data to the transport. This method does not block; it buffers the data and arranges for it to be sent out asynchronously. Writes made after the transport has been closed will be ignored.""" if self._closing: return if self.get_write_buffer_size() == 0: self._write_buffer.append(data) self._ensure_writer() else: self._write_buffer.append(data) self._maybe_pause_protocol() def can_write_eof(self): """Serial ports do not support the concept of end-of-file. Always returns False. """ return False def pause_reading(self): """Pause the receiving end of the transport. No data will be passed to the protocol’s data_received() method until resume_reading() is called. """ self._remove_reader() def resume_reading(self): """Resume the receiving end of the transport. Incoming data will be passed to the protocol's data_received() method until pause_reading() is called. """ self._ensure_reader() def set_write_buffer_limits(self, high=None, low=None): """Set the high- and low-water limits for write flow control. These two values control when the protocol’s pause_writing()and resume_writing() methods are called. If specified, the low-water limit must be less than or equal to the high-water limit. Neither high nor low can be negative. """ self._set_write_buffer_limits(high=high, low=low) self._maybe_pause_protocol() def get_write_buffer_size(self): """The number of bytes in the write buffer. This buffer is unbounded, so the result may be larger than the the high water mark. """ return sum(map(len, self._write_buffer)) def write_eof(self): raise NotImplementedError("Serial connections do not support end-of-file") def abort(self): """Close the transport immediately. Pending operations will not be given opportunity to complete, and buffered data will be lost. No more data will be received and further writes will be ignored. The protocol's connection_lost() method will eventually be called. """ self._abort(None) def flush(self): """ clears output buffer and stops any more data being written """ self._remove_writer() self._write_buffer.clear() self._maybe_resume_protocol() def _maybe_pause_protocol(self): """To be called whenever the write-buffer size increases. Tests the current write-buffer size against the high water mark configured for this transport. If the high water mark is exceeded, the protocol is instructed to pause_writing(). """ if self.get_write_buffer_size() <= self._high_water: return if not self._protocol_paused: self._protocol_paused = True try: self._protocol.pause_writing() except Exception as exc: self._loop.call_exception_handler({ 'message': 'protocol.pause_writing() failed', 'exception': exc, 'transport': self, 'protocol': self._protocol, }) def _maybe_resume_protocol(self): """To be called whenever the write-buffer size decreases. Tests the current write-buffer size against the low water mark configured for this transport. If the write-buffer size is below the low water mark, the protocol is instructed that is can resume_writing(). """ if (self._protocol_paused and self.get_write_buffer_size() <= self._low_water): self._protocol_paused = False try: self._protocol.resume_writing() except Exception as exc: self._loop.call_exception_handler({ 'message': 'protocol.resume_writing() failed', 'exception': exc, 'transport': self, 'protocol': self._protocol, }) def _write_ready(self): """Asynchronously write buffered data. This method is called back asynchronously as a writer registered with the asyncio event-loop against the underlying file descriptor for the serial port. Should the write-buffer become empty if this method is invoked while the transport is closing, the protocol's connection_lost() method will be called with None as its argument. """ data = b''.join(self._write_buffer) assert data, 'Write buffer should not be empty' self._write_buffer.clear() try: n = self._serial.write(data) except (BlockingIOError, InterruptedError): self._write_buffer.append(data) except serial.SerialException as exc: self._fatal_error(exc, 'Fatal write error on serial transport') else: if n == len(data): assert self._flushed() self._remove_writer() self._maybe_resume_protocol() # May cause further writes # _write_ready may have been invoked by the event loop # after the transport was closed, as part of the ongoing # process of flushing buffered data. If the buffer # is now empty, we can close the connection if self._closing and self._flushed(): self._close() return assert 0 <= n < len(data) data = data[n:] self._write_buffer.append(data) # Try again later self._maybe_resume_protocol() assert self._has_writer if os.name == "nt": def _poll_read(self): if self._has_reader: try: if self.serial.in_waiting: self._loop.call_soon(self._read_ready) self._loop.call_later(self._poll_wait_time, self._poll_read) except serial.SerialException as exc: self._fatal_error(exc, 'Fatal write error on serial transport') def _ensure_reader(self): if (not self._has_reader) and (not self._closing): self._loop.call_later(self._poll_wait_time, self._poll_read) self._has_reader = True def _remove_reader(self): self._has_reader = False def _poll_write(self): if self._has_writer: if self.serial.out_waiting: self._loop.call_soon(self._write_ready) self._loop.call_later(self._poll_wait_time, self._poll_write) def _ensure_writer(self): if (not self._has_writer) and (not self._closing): self._loop.call_later(self._poll_wait_time, self._poll_write) self._has_writer = True def _remove_writer(self): self._has_writer = False else: def _ensure_reader(self): if (not self._has_reader) and (not self._closing): self._loop.add_reader(self._serial.fileno(), self._read_ready) self._has_reader = True def _remove_reader(self): if self._has_reader: self._loop.remove_reader(self._serial.fileno()) self._has_reader = False def _ensure_writer(self): if (not self._has_writer) and (not self._closing): self._loop.add_writer(self._serial.fileno(), self._write_ready) self._has_writer = True def _remove_writer(self): if self._has_writer: self._loop.remove_writer(self._serial.fileno()) self._has_writer = False def _set_write_buffer_limits(self, high=None, low=None): """Ensure consistent write-buffer limits.""" if high is None: high = 64 * 1024 if low is None else 4 * low if low is None: low = high // 4 if not high >= low >= 0: raise ValueError('high (%r) must be >= low (%r) must be >= 0' % (high, low)) self._high_water = high self._low_water = low def _fatal_error(self, exc, message='Fatal error on serial transport'): """Report a fatal error to the event-loop and abort the transport.""" self._loop.call_exception_handler({ 'message': message, 'exception': exc, 'transport': self, 'protocol': self._protocol, }) self._abort(exc) def _flushed(self): """True if the write buffer is empty, otherwise False.""" return self.get_write_buffer_size() == 0 def _close(self, exc=None): """Close the transport gracefully. If the write buffer is already empty, writing will be stopped immediately and a call to the protocol's connection_lost() method scheduled. If the write buffer is not already empty, the asynchronous writing will continue, and the _write_ready method will call this _close method again when the buffer has been flushed completely. """ self._closing = True self._remove_reader() if self._flushed(): self._remove_writer() self._loop.call_soon(self._call_connection_lost, exc) def _abort(self, exc): """Close the transport immediately. Pending operations will not be given opportunity to complete, and buffered data will be lost. No more data will be received and further writes will be ignored. The protocol's connection_lost() method will eventually be called with the passed exception. """ self._closing = True self._remove_reader() self._remove_writer() # Pending buffered data will not be written self._loop.call_soon(self._call_connection_lost, exc) def _call_connection_lost(self, exc): """Close the connection. Informs the protocol through connection_lost() and clears pending buffers and closes the serial connection. """ assert self._closing assert not self._has_writer assert not self._has_reader try: self._serial.flush() except (serial.SerialException if os.name == "nt" else termios.error): # ignore serial errors which may happen if the serial device was # hot-unplugged. pass try: self._protocol.connection_lost(exc) finally: self._write_buffer.clear() self._serial.close() self._serial = None self._protocol = None self._loop = None async def create_serial_connection(loop, protocol_factory, *args, **kwargs): """Create a connection to a new serial port instance. This function is a coroutine which will try to establish the connection. The chronological order of the operation is: 1. protocol_factory is called without arguments and must return a protocol instance. 2. The protocol instance is tied to the transport 3. This coroutine returns successfully with a (transport, protocol) pair. 4. The connection_made() method of the protocol will be called at some point by the event loop. Note: protocol_factory can be any kind of callable, not necessarily a class. For example, if you want to use a pre-created protocol instance, you can pass lambda: my_protocol. Any additional arguments will be forwarded to the Serial constructor. """ serial_instance = serial.serial_for_url(*args, **kwargs) transport, protocol = await connection_for_serial(loop, protocol_factory, serial_instance) return transport, protocol async def connection_for_serial(loop, protocol_factory, serial_instance): """Create a connection to the given serial port instance. This function is a coroutine which will try to establish the connection. The chronological order of the operation is: 1. protocol_factory is called without arguments and must return a protocol instance. 2. The protocol instance is tied to the transport 3. This coroutine returns successfully with a (transport, protocol) pair. 4. The connection_made() method of the protocol will be called at some point by the event loop. Note: protocol_factory can be any kind of callable, not necessarily a class. For example, if you want to use a pre-created protocol instance, you can pass lambda: my_protocol. """ protocol = protocol_factory() transport = SerialTransport(loop, protocol, serial_instance) return transport, protocol async def open_serial_connection(*, loop=None, limit=None, **kwargs): """A wrapper for create_serial_connection() returning a (reader, writer) pair. The reader returned is a StreamReader instance; the writer is a StreamWriter instance. The arguments are all the usual arguments to Serial(). Additional optional keyword arguments are loop (to set the event loop instance to use) and limit (to set the buffer limit passed to the StreamReader. This function is a coroutine. """ if loop is None: loop = asyncio.get_event_loop() if limit is None: limit = asyncio.streams._DEFAULT_LIMIT reader = asyncio.StreamReader(limit=limit, loop=loop) protocol = asyncio.StreamReaderProtocol(reader, loop=loop) transport, _ = await create_serial_connection( loop=loop, protocol_factory=lambda: protocol, **kwargs) writer = asyncio.StreamWriter(transport, protocol, reader, loop) return reader, writer # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # test if __name__ == '__main__': class Output(asyncio.Protocol): def __init__(self): super().__init__() self._transport = None def connection_made(self, transport): self._transport = transport print('port opened', self._transport) self._transport.serial.rts = False self._transport.write(b'Hello, World!\n') def data_received(self, data): print('data received', repr(data)) if b'\n' in data: self._transport.close() def connection_lost(self, exc): print('port closed') self._transport.loop.stop() def pause_writing(self): print('pause writing') print(self._transport.get_write_buffer_size()) def resume_writing(self): print(self._transport.get_write_buffer_size()) print('resume writing') loop = asyncio.get_event_loop() coro = create_serial_connection(loop, Output, '/dev/ttyUSB0', baudrate=115200) transport, protocol = loop.run_until_complete(coro) loop.run_forever() loop.close() pyserial-asyncio-0.5/setup.cfg000066400000000000000000000000721375706324600165060ustar00rootroot00000000000000[flake8] max-line-length = 120 ignore = E265, E126, E241 pyserial-asyncio-0.5/setup.py000066400000000000000000000052141375706324600164020ustar00rootroot00000000000000# setup.py for pySerial-asyncio # # For Python 3.x use the corresponding Python executable, # e.g. "python3 setup.py ..." # # (C) 2015-2017 Chris Liechti # # SPDX-License-Identifier: BSD-3-Clause import io import os import re import sys if sys.version_info < (3, 5): raise RuntimeError("pyserial-asyncio requires at least Python 3.5") from setuptools import setup def read(*names, **kwargs): """Python 2 and Python 3 compatible text file reading. Required for single-sourcing the version string. """ with io.open( os.path.join(os.path.dirname(__file__), *names), encoding=kwargs.get("encoding", "utf8") ) as fp: return fp.read() def find_version(*file_paths): """ Search the file for a version string. file_path contain string path components. Reads the supplied Python module as text without importing it. """ version_file = read(*file_paths) version_match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]", version_file, re.M) if version_match: return version_match.group(1) raise RuntimeError("Unable to find version string.") version = find_version('serial_asyncio', '__init__.py') setup( name="pyserial-asyncio", description="Python Serial Port Extension - Asynchronous I/O support", version=version, author="pySerial-team", url="https://github.com/pyserial/pyserial-asyncio", packages=['serial_asyncio'], install_requires=[ 'pyserial', ], license="BSD", long_description="""\ Async I/O extension package for the Python Serial Port Extension for OSX, Linux, BSD - Documentation: http://pyserial-asyncio.readthedocs.io - Project Homepage: https://github.com/pyserial/pyserial-asyncio """, classifiers=[ #~ 'Development Status :: 5 - Production/Stable', 'Development Status :: 4 - Beta', 'Intended Audience :: Developers', 'Intended Audience :: End Users/Desktop', 'License :: OSI Approved :: BSD License', 'Natural Language :: English', 'Operating System :: POSIX', 'Operating System :: MacOS :: MacOS X', 'Programming Language :: Python', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Topic :: Communications', 'Topic :: Software Development :: Libraries', 'Topic :: Software Development :: Libraries :: Python Modules', 'Topic :: Terminals :: Serial', ], platforms='any', ) pyserial-asyncio-0.5/test/000077500000000000000000000000001375706324600156455ustar00rootroot00000000000000pyserial-asyncio-0.5/test/__init__.py000066400000000000000000000000011375706324600177450ustar00rootroot00000000000000 pyserial-asyncio-0.5/test/test_asyncio.py000066400000000000000000000055741375706324600207360ustar00rootroot00000000000000#!/usr/bin/env python # # This file is part of pySerial-asyncio - Cross platform serial port support for Python # (C) 2016 pySerial-team # # SPDX-License-Identifier: BSD-3-Clause """\ Test asyncio related functionality. To run from the command line with a specific port with a loop-back, device connected, use: $ cd pyserial-asyncio $ python -m test.test_asyncio SERIALDEVICE """ import os import unittest import asyncio import serial_asyncio HOST = '127.0.0.1' _PORT = 8888 # on which port should the tests be performed: PORT = 'socket://%s:%s' % (HOST, _PORT) @unittest.skipIf(os.name != 'posix', "asyncio not supported on platform") class Test_asyncio(unittest.TestCase): """Test asyncio related functionality""" def setUp(self): self.loop = asyncio.get_event_loop() # create a closed serial port def tearDown(self): self.loop.close() def test_asyncio(self): TEXT = b'Hello, World!\n' received = [] actions = [] class Input(asyncio.Protocol): def __init__(self): super().__init__() self._transport = None def connection_made(self, transport): self._transport = transport def data_received(self, data): self._transport.write(data) class Output(asyncio.Protocol): def __init__(self): super().__init__() self._transport = None def connection_made(self, transport): self._transport = transport actions.append('open') transport.write(TEXT) def data_received(self, data): received.append(data) if b'\n' in data: self._transport.close() def connection_lost(self, exc): actions.append('close') self._transport.loop.stop() def pause_writing(self): actions.append('pause') print(self._transport.get_write_buffer_size()) def resume_writing(self): actions.append('resume') print(self._transport.get_write_buffer_size()) if PORT.startswith('socket://'): coro = self.loop.create_server(Input, HOST, _PORT) self.loop.run_until_complete(coro) client = serial_asyncio.create_serial_connection(self.loop, Output, PORT) self.loop.run_until_complete(client) self.loop.run_forever() self.assertEqual(b''.join(received), TEXT) self.assertEqual(actions, ['open', 'close']) if __name__ == '__main__': import sys sys.stdout.write(__doc__) if len(sys.argv) > 1: PORT = sys.argv[1] sys.stdout.write("Testing port: %r\n" % PORT) sys.argv[1:] = ['-v'] # When this module is executed from the command-line, it runs all its tests unittest.main()