pax_global_header00006660000000000000000000000064130010116200014472gustar00rootroot0000000000000052 comment=ae22aaad504896462816d2f6e38ff7dfdd796fb2 terminaltables-3.1.0/000077500000000000000000000000001300101162000145015ustar00rootroot00000000000000terminaltables-3.1.0/.gitignore000066400000000000000000000021241300101162000164700ustar00rootroot00000000000000# Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class # C extensions *.so # Distribution / packaging .Python env/ build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ *.egg-info/ .installed.cfg *.egg # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *,cover .hypothesis/ # Translations *.mo *.pot # Django stuff: *.log local_settings.py # Flask stuff: instance/ .webassets-cache # Scrapy stuff: .scrapy # Sphinx documentation docs/_build/ # PyBuilder target/ # IPython Notebook .ipynb_checkpoints # pyenv .python-version # celery beat schedule file celerybeat-schedule # dotenv .env # virtualenv venv/ ENV/ # Spyder project settings .spyderproject # Rope project settings .ropeproject # Robpol86 test*.png *.rpm .idea/ requirements*.txt .DS_Store terminaltables-3.1.0/.travis.yml000066400000000000000000000022731300101162000166160ustar00rootroot00000000000000# Configure. env: TOX_ENV=py language: python matrix: include: - python: 3.5 env: TOX_ENV=lint after_success: - echo - python: 3.5 env: TOX_ENV=docs after_success: - eval "$(ssh-agent -s)"; touch docs/key; chmod 0600 docs/key - openssl aes-256-cbc -d -K "$encrypted_c89fed6a587d_key" -iv "$encrypted_c89fed6a587d_iv" < docs/key.enc > docs/key && ssh-add docs/key - git config --global user.email "builds@travis-ci.com" - git config --global user.name "Travis CI" - git remote set-url --push origin "git@github.com:$TRAVIS_REPO_SLUG" - export ${!TRAVIS*} - tox -e docsV python: - 3.5 - 3.4 - 3.3 - pypy3 - pypy - 2.7 - 2.6 sudo: false # Run. install: pip install tox script: tox -e $TOX_ENV after_success: - bash <(curl -s https://codecov.io/bash) # Deploy. deploy: provider: pypi user: Robpol86 password: secure: "aj+Hl25+NbtmKpHcqxxNJhaMmawgzEPdLX+NwxwAZuTrvUCdiMtYhF9qxN0USHIlXSGDNc\ 7ua6nNpYPhjRv7j5YM4uLlK+4Fv/iU+iQcVfy89BS4vlXzUoje6nLIhogsxytb+FjdGZ0PK\ JzzxfYr0relUjui/gPYmTQoZ1IiT8A=" on: condition: $TRAVIS_PYTHON_VERSION = 3.4 tags: true terminaltables-3.1.0/CONTRIBUTING.md000066400000000000000000000034621300101162000167370ustar00rootroot00000000000000# Contributing Everyone that wants to contribute to the project should read this document. ## Getting Started You may follow these steps if you wish to create a pull request. Fork the repo and clone it on your local machine. Then in the project's directory: ```bash virtualenv env # Create a virtualenv for the project's dependencies. source env/bin/activate # Activate the virtualenv. pip install tox # Install tox, which runs linting and tests. tox # This runs all tests on your local machine. Make sure they pass. ``` If you don't have Python 2.6, 2.7, or 3.4 installed you can manually run tests on one specific version by running `tox -e lint,py35` (for Python 3.5) instead. ## Updating Docs You don't need to but if you wish to update the [Sphinx](http://sphinx-doc.org/) documentation for this project you can get started by running these commands: ```bash source env/bin/activate pip install tox tox -e docs open docs/_build/html/index.html # Opens this file in your browser. ``` ## Consistency and Style Keep code style consistent with the rest of the project. Some suggestions: 1. **Write tests for your new features.** `if new_feature else` **Write tests for bug-causing scenarios.** 2. Write docstrings for all classes, functions, methods, modules, etc. 3. Document all function/method arguments and return values. 4. Document all class variables instance variables. 5. Documentation guidelines also apply to tests, though not as strict. 6. Keep code style consistent, such as the kind of quotes to use and spacing. 7. Don't use `except:` or `except Exception:` unless you have a `raise` in the block. Be specific about error handling. 8. Don't use `isinstance()` (it breaks [duck typing](https://en.wikipedia.org/wiki/Duck_typing#In_Python)). ## Thanks Thanks for fixing bugs or adding features to the project! terminaltables-3.1.0/LICENSE000066400000000000000000000020631300101162000155070ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2016 Robpol86 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. terminaltables-3.1.0/README.rst000066400000000000000000000106101300101162000161660ustar00rootroot00000000000000============== terminaltables ============== Easily draw tables in terminal/console applications from a list of lists of strings. Supports multi-line rows. * Python 2.6, 2.7, PyPy, PyPy3, 3.3, 3.4, and 3.5 supported on Linux and OS X. * Python 2.7, 3.3, 3.4, and 3.5 supported on Windows (both 32 and 64 bit versions of Python). 📖 Full documentation: https://robpol86.github.io/terminaltables .. image:: https://img.shields.io/appveyor/ci/Robpol86/terminaltables/master.svg?style=flat-square&label=AppVeyor%20CI :target: https://ci.appveyor.com/project/Robpol86/terminaltables :alt: Build Status Windows .. image:: https://img.shields.io/travis/Robpol86/terminaltables/master.svg?style=flat-square&label=Travis%20CI :target: https://travis-ci.org/Robpol86/terminaltables :alt: Build Status .. image:: https://img.shields.io/codecov/c/github/Robpol86/terminaltables/master.svg?style=flat-square&label=Codecov :target: https://codecov.io/gh/Robpol86/terminaltables :alt: Coverage Status .. image:: https://img.shields.io/pypi/v/terminaltables.svg?style=flat-square&label=Latest :target: https://pypi.python.org/pypi/terminaltables :alt: Latest Version Quickstart ========== Install: .. code:: bash pip install terminaltables Usage: .. code:: from terminaltables import AsciiTable table_data = [ ['Heading1', 'Heading2'], ['row1 column1', 'row1 column2'], ['row2 column1', 'row2 column2'], ['row3 column1', 'row3 column2'] ] table = AsciiTable(table_data) print table.table +--------------+--------------+ | Heading1 | Heading2 | +--------------+--------------+ | row1 column1 | row1 column2 | | row2 column1 | row2 column2 | | row3 column1 | row3 column2 | +--------------+--------------+ Example Implementations ======================= .. image:: docs/examples.png?raw=true :alt: Example Scripts Screenshot Source code for examples: `example1.py `_, `example2.py `_, and `example3.py `_ .. changelog-section-start Changelog ========= This project adheres to `Semantic Versioning `_. 3.1.0 - 2016-10-16 ------------------ Added * ``git --porcelain``-like table by liiight: https://github.com/Robpol86/terminaltables/pull/31 3.0.0 - 2016-05-30 ------------------ Added * Support for https://pypi.python.org/pypi/colorama * Support for https://pypi.python.org/pypi/termcolor * Support for RTL characters (Arabic and Hebrew). * Support for non-string items in ``table_data`` like integers. Changed * Refactored again, but this time entire project including tests. Removed * ``padded_table_data`` property and ``join_row()``. Moving away from repeated string joining/splitting. Fixed * ``set_terminal_title()`` Unicode handling on Windows. * https://github.com/Robpol86/terminaltables/issues/18 * https://github.com/Robpol86/terminaltables/issues/20 * https://github.com/Robpol86/terminaltables/issues/23 * https://github.com/Robpol86/terminaltables/issues/26 2.1.0 - 2015-11-02 ------------------ Added * GitHub Flavored Markdown table by bcho: https://github.com/Robpol86/terminaltables/pull/12 * Python 3.5 support (Linux/OS X and Windows). 2.0.0 - 2015-10-11 ------------------ Changed * Refactored code. No new features. * Breaking changes: ``UnixTable``/``WindowsTable``/``WindowsTableDouble`` moved. Use ``SingleTable``/``DoubleTable`` instead. 1.2.1 - 2015-09-03 ------------------ Fixed * CJK character width fixed by zqqf16 and bcho: https://github.com/Robpol86/terminaltables/pull/9 1.2.0 - 2015-05-31 ------------------ Added * Bottom row separator. 1.1.1 - 2014-11-03 ------------------ Fixed * Python 2.7 64-bit terminal width bug on Windows. 1.1.0 - 2014-11-02 ------------------ Added * Windows support. * Double-lined table. 1.0.2 - 2014-09-18 ------------------ Added * ``table_width`` and ``ok`` properties. 1.0.1 - 2014-09-12 ------------------ Added * Terminal width/height defaults for testing. * ``terminaltables.DEFAULT_TERMINAL_WIDTH`` * ``terminaltables.DEFAULT_TERMINAL_HEIGHT`` 1.0.0 - 2014-09-11 ------------------ * Initial release. .. changelog-section-end terminaltables-3.1.0/appveyor.yml000066400000000000000000000021661300101162000170760ustar00rootroot00000000000000# Configure. environment: PYTHON: Python35 matrix: - TOX_ENV: lint - TOX_ENV: py35 - TOX_ENV: py34 - TOX_ENV: py33 - TOX_ENV: py27 - TOX_ENV: py PYTHON: Python35-x64 - TOX_ENV: py PYTHON: Python34-x64 - TOX_ENV: py PYTHON: Python33-x64 - TOX_ENV: py PYTHON: Python27-x64 # Run. init: set PATH=C:\%PYTHON%;C:\%PYTHON%\Scripts;%PATH% install: - appveyor DownloadFile https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-desktop.ps1 - ps: .\enable-desktop build_script: pip install tox test_script: tox -e %TOX_ENV% on_success: IF %TOX_ENV% NEQ lint pip install codecov & codecov # Post. # on_finish: https://github.com/Robpol86/terminaltables/issues/30 #- appveyor PushArtifact test_ascii_table.png https://github.com/Robpol86/terminaltables/issues/30 #- appveyor PushArtifact test_double_table.png https://github.com/Robpol86/terminaltables/issues/30 #- appveyor PushArtifact test_single_table.png https://github.com/Robpol86/terminaltables/issues/30 #- appveyor PushArtifact test_terminal_io.png https://github.com/Robpol86/terminaltables/issues/30 terminaltables-3.1.0/docs/000077500000000000000000000000001300101162000154315ustar00rootroot00000000000000terminaltables-3.1.0/docs/_templates/000077500000000000000000000000001300101162000175665ustar00rootroot00000000000000terminaltables-3.1.0/docs/_templates/layout.html000066400000000000000000000002731300101162000217730ustar00rootroot00000000000000{# From https://github.com/snide/sphinx_rtd_theme/issues/166 #} {# Import the theme's layout. #} {% extends "!layout.html" %} {% set css_files = css_files + ['_static/pygments.css'] %} terminaltables-3.1.0/docs/asciitable.png000066400000000000000000001274701300101162000202520ustar00rootroot00000000000000PNG  IHDRb5 pHYs   iCCPPhotoshop ICC profilexڭgTSBKЛ JFH(cHP "RPD2ĂmPl'Ƞ*Gxz9s @ @H* e'$&r@&DFL9pˊ+ U~j~/9%RL ˤb)V LIBbv̔I LIL6 @qt{`ҥ4&؈feg/Yʿ3E+x.@䈅ll eH4f- U(en S! b^_S,ˊbdz@ʉbɒ(H87LQpjN@ 9S?ŹS:OKdQi@sgI3b3$(S(V^,UxA ='7ZW*QܐiH06Ă8-A84uoxD!eT6GijŶuHHLbOwE,WnZKhh5L;@e`&!؁7@D@ $"AdA>b(-aA##',\pCQ:@4=D @($IF"CuH)RT#&r!dy|A12Q>h(.DѥhZnB+Z چEwP9b,L\0?,K0 +*Zz[{}p gsbqF-Wji_ Ty-=;{VgJy~L~AAAcCaaaᨑQQQcq&&&MMM5L9yͦf^fKjn]̳w߰@--2,j,[N]}\gf귢YXZ5[ Xì ۭ_64{mm6mڪنvھݶگ`#1qc7'g'SӈsN~KFKxW_5']?9Iݎn~}x9 zxp=y=ٞɞ{=^^\Zކ|z>>>}^J|~s[T< 4Ll r Z 8MU!CiѡաO,$ahxHGsG@'b[Hȥ#̋W3YmT~TO4#zq11cƚb5}/'NXp5Q+QؑDLKO0 ]hpˋ Zh2>9>@Wn;Iٙ2{#e<҆=ҷdxeTd o23d~̊jȚ [I'Dj,%K/[nK/Js9RT,&3 ̭,nE˯XaWVVvXjjdu5k 4Z[MaYu:t XXRܿ}q? ~`j~ɕRҊүydSO6nvڼ{ ahݭ^[Tok+g߾x =;(;d;aUFU[VgTߩiݩsΏnݲGgO/{{ VkR[QG˭{?n.?7k՗k57975>m5\p!C-V-ZY_{$HQ-nj<8^҆hmhw$v9yW_N꟬9~jigΌu^M?;ؽṄs;{!¥qe'\it9vש7:u-[osn_3Nػ  ÂGG%UW<~Rr'kO> #珯CE*=o>98rC//_f׵фѡ77o7|}Xؓ?|sϗ/Ǘ}%~f{Gb iioɎ d '{48uuT({Dzxjox59iv^v|Ļx9o@ ޕvAGG:Da 8:ciTXtXML:com.adobe.xmp 2016-06-11T22:28:31-07:00 2016-06-11T22:51:37-07:00 2016-06-11T22:51:37-07:00 Adobe Photoshop CC 2015 (Macintosh) image/png 3 Display xmp.iid:508b3963-aecc-436a-99b5-095949dbf026 adobe:docid:photoshop:f6c7a306-70ec-1179-ac3d-f90aca98f8a3 xmp.did:28962b21-b313-4455-a6f0-ef661ef1bba5 saved xmp.iid:28962b21-b313-4455-a6f0-ef661ef1bba5 2016-06-11T22:29:29-07:00 Adobe Photoshop CC 2015 (Macintosh) / saved xmp.iid:508b3963-aecc-436a-99b5-095949dbf026 2016-06-11T22:51:37-07:00 Adobe Photoshop CC 2015 (Macintosh) / 1 720000/10000 720000/10000 2 65535 501 172  cHRMn'sxp1\'%iIDATxy\TU30þ&*kff` -f iYk)oJVJR\,MqDvdg`dEL|nə{><{yd@ kd?QFRpuuNll,}6mBts d2oRYYIeee@ Z2 LLLXx1 .D$]P0a| FXJ BK/ƍu=2@ fCU>}jeFy63iPH rT^@@pӧ(ظw虙h hϟ@y>T=1hOe2ׇY5I^Ze~,qaWH(1sT2h%P`V9Wlc)+Caf&.@ ptt S6bee\W^ZJQQ|0 TY}QiKK)**$ϴ&i#$Kzyї56=o&*T.JIB\H.IT$䕀\̤F\.LI%[h9+s?0GGby}i|0 O=FdOHi}u~n-?jm>NԴVqE]܍s9u)U;8*Hp(39BxF.ApsszDvS0ui HU_ˬr # {RIҙUƣppu-jIyg{3)(ՠtmOdB*$F & iD0JKk&Ur""Ogi1b4!z (Q臧 nj5@.Ziu{lSoUh5Vrb66˗_r׻bWCՔ[4:7|~_6aq7wLd~.ndzx}XFLLޥ?ͅuI2 S[;\8K!{qҿǾhu@Z =GKC>WеRmyrvʌ}Lv W151A.\ rTz_ieHzMJzLtG>h#?Cc|ިJTõcEh@vF_yIn$IhZry $sR\M뼚z׌6ՉpQJdĤѷ<2kf?kLfPB.$!d{ ƛo =yZ489z++^kI9eVVV=417m/XYYW_m 2uʚܬ_+Ȩ_sƪ*Kɐ@.Ҋ_s`R7 -&Z&F^CL !C+P%;uLáMKY{>Kж?ne΃R'x~̠,VItEౙsϣ%6}s|ǦNIcӌu̝A_l6-QY:bN-kvSP=0]*!IhƎ-$nmY&<@ 9Mjҏ9M_amփ O2 @Cl wAL{ 8zY&}O؍Ǧ>ǃ_g,H:{e8gLdÙ56 ^VٷJ,L%QLm!<[!ÈZΪw6$m[˦+R*mK5h1uVIl(L'9vzoZ }3úc{?GjڛjєKHiUsB+*51<;{ }*DcA+4M~tF-b k ^q(y=b:e>kAoA&  Vdv d nM@~xڢ VU84;ߢog'*ٴa^:s/Δ/Y3@~*/;9 N~ET(ai"U M ~&Ե={RwX[YLzל&CxpE9csBuF. ϩrzrȠAEO1] 2-Mu#r),[kP%~7:te}> 3JV RIXI"l _7wҹ%%jԿskHɃ Ո]7 Qs_zˤ;6-:92t{^% Ay[zlzdx(Wo>q36^@X䵆# l>j߁xW\'}}4(ɟlY4W.ršydzPߠCJ_^ͯ57\OJ_OfUjuL@)zrLЩ1EBV=KBV ݇8GԆ.*Owq*-G.孷"ɬl}\/H~TB*(+XI2oE}uḻg`> ȹtF#lv~Hgf$KKI;Yo_饔kƾLL,0WZ"IPQ0$b>_LؿH}dߧR͜ׯa{mVR|5+k|yVz),']Ϩp1Sqn3﹡t5 ˔KS:z;|bb$1ho֬88}.KI.>MC 9848Qe[4A\ғ.>Ew*!)6(…m'E+;]{3Td UfJȵw?Z՗WHM%wkM3.a/<04`.c|x~/!hO⯒ZnL$J$ B~\4Znh^UQ+w# MϯxJKuB~&7V+~&" @ A] YP߲e@ 92@ٳ@ Bvڅ;:t@.S^^Naa!rJ%fff(JJ%&&&TVVRTT.cnnBR9666P\\YզP(TVVVF+@p z@ R}Y] *++U 6l0ԧNL&cuĪ:uB i;E:DIGV2?$gfXĿ:dռ-mn6#)l"F.7G~jL{\/6j*^O$k*sqWQUq@2֭W^^H5sfvONZR7퉌%~be\pbE[*# HĚ5kZD^W\wQЂ5I¼0^fSvvs92999*k*jigp0Mbp^ !@g0aao+GgR،m 9Q?J2 7UZ5S˗'prtUMM%-#sA7[3w~2s Hہ(f#1/'p|P{x]0W'֚|eHZFii_;jYkFRR LL%RH˸X5R`HN=Sh? 7A/8x~={"+^w} %ѿsW@<]Iiy{b>ot hDnǣft|}/'_}Xջdtoז=#]x$ і%}aXWQvv!g׏Mt[d2 gWgGu]92}J9^B.Mn`3{:dpVYRǞÙ% EtriDqЪ/̯[Dgi{*#I)zNc[?n7U!q<C;n/tpwy={|{f(h輳OhVpj{nطoݻ={45ݭ;)&|l掛`HxHE4ml]ч~l^:zi0%ƿp*&dʚNmHIg &2 l 9:#.nx{{?RUtVZ ~,A Xt63NrIɸ[@`W ,k]4і]7"ur2GۛmZ:YaWxuʚ7"0 >25q/߈wYO]!z<\`0~@P#G2|px|Aj5=Pz(nSgЇ\쵬];K[FJOV84Y#>lWA!I1wy pL>f>}c6vk49H$*^FXQVNk9գgwfoˌ%*6d˙uG'gBg\2&h0ʊBߥSs[amn\"7|"WkHOߓdjHh1pر'pv+At7jF;f"~^5 f vB*(,(ILZr& Ñ٘>My#_":ӋBA<~w9f@L S:M.fۘYRyObg'O:´$jK~>O)߿pt6ΘAN 8S\BqQ|bJ5e@f_̜)1} dc*j?%)̐.RpSk(>(EGo Q?SAe>2*s.i@V{wPAEnnU_hjilFa;)#l=*}G=ŀg0$r*`j<@R]-FB{M r Xu(1"Q7Ϲدw&j&|~3ҿx/q. Ծ 3gc{2F)#Ej_ʪȌm0O.){T 7A#dLhޝ1@Y/S|u wkɫ[C +"3C&?ڙ[ #ݷzO Bs_|I "laؙ@8e3L#jU|\rR;rk\їYwИFڜ]oJ@8[SG݇7.G?W3(ߘ,@Ed_][ц\Num\Spf# )Sq3f&R~1܅NG8WArJ^1}"2hBՂVP~&,ЗOu%b˜ZX}0&\Vĭ%$'7]=t>vPwJAq^T^ֈ>oe|u;iD .`ݺuHdpE&X~q oȱ5XqW^˝C__8>fOڋL`'!| Nlxr~ٙy)` ߌRa24deΝS$'Sp<{rd,9dg@1O~:mJy K8Rz{ڦfhBa8{ʐ?\^>CQ򿍦L=>S٧өd UX]%F5o ; 뺘c3B<+1+b-\z=SGqOl>tNrtŴ1SF%jL-\cEDFeFqD uW{#z]Po}7f%|!qFSkh4d2ZIjUkU?\K>>> M:Uz$}*ZɷfYS7Di$)Zc7I* P P]W+H:2$f=ɡ[^*iōا[ɡ&wtlZ nHip1Gcg鮗j+% 'K6VkN~G/=pAm0-sj)@Uw@P2/_)@8[/R|}}v6~S߆λ$oi"^Z /HSN{׮]ґ#G\)??_̔.\ Iҹs礔)##C˓ <)55UFei|33Fͳ}Y4|<{?c܌~* )d\`<x>`D'dn| 2./D~DeV/ωb5VM剰XQ3Woҧ4xds$7ˉD@IIIURBv-Kee%%%%_^UP%*6U708WYJ|&cooL&3ܵ (@ wJPW@ ChA=pfA꛷͖#AUҎq6 gncƏe`gMf1dBJFi2_2j]p[.|t3ukw@ !6eli]QlhAby_͖cn'y]U"Rވ<  ^k:yӷ _SW/;)U~++7@qY%WǷަÑӓ5uf]3MΠCWXPZM·5bP4bMe:8Ec`e{,_J;ۉ]@ ڙ >ttTI`"LBn/k{<:o>/gžGoo"5yZCY o$Z} 1?9e\X)b olˡ<,rD Gaڈ^{ÄΔlu.^xأr;y`Fvv_ vc* zZaasrYws{k#d̪x3K-;*@!K^gaccKF.{[z"ԣ|/ue]ɮ&?o=T~~/gZ$}p-;Abߣ{.>wNx_F#vj]%i9:4RU-b Dr='gՐ^ C$|Ho+9bRt!OUs/E(ۙ[>8Ow0qJ$i?чZq ISB%_Qof(YcՊ-u@69X)_zLh#`C853o L- \~ 8"~sq3V_cvsU"S*egKC1u Ķi\۹:Xm=ҏkɬ^*]C n)6#MZQgl[ˉ_W&휩_Z5_:zVOT(Oi5׌ޙ"yVHG0u5)#nn_IyRэ\7@#7WW|ay)fFuWHY%5upmbv&==]*--ʤz7F#i4Ze 5NU (S+]6o{r)0./s'o]~;=i8gr]5&K\HXby<" ou0v^xSS %N\̉0^XM*&L)k6o5f!ޑu'8Ǧ\?N POd?o3uoIPG[j)(8Xln)NYlbvۍ?t@ܱvG<$L ѵM yO0<כ-*"VE oIvA@di_UjMoK@P @P|w܉Do?e4ϲx_pg-WHyq3WH2Ҥ?H;%m fH_yJ-]NSڽgo7IΝ*hui7&J%%Ml uKY>۵5Kv)^N;%%JO-E;'$%Ii9RNF"]ۡ6*iRx/ݴvg7NF=pxd_6Д,d)Y£m>Sf:9ץt( |30=d0ù|Q {Ň<8coߛef-OSW͜WO̭Ӏ~U/3KpV7Tr65 \[@5cY%2*6*'0#ei3^'g3'-u}J$^E"?#:ȁw 9dl,mNĠ?&gOZ6A!퍛894d˖}QfC@>fϸ:iBQ!8{wmzvĖ/ fYlקvaZF]gV_V7h_o)xhyZ.8F÷gQxP2l(wq!9=1OVVgei3m`NP6򓖻MI`գgw۫b(8X_L% ߂|׈XOJ,|zx#}q]&ߐ>A4nmSWgIfn@6ff$&d!,n8賐4C?,7(NJ 33{fzfP;C`0!?i` Ӥaو}GsS"S1oV%(ՒF#o}qD uP_X0;Ȃ|*`6\)/2,߻32ZFX2-[I>dȔ/Gx׌w\ QS8yzi -_=%1[qȪڅ7'-}[ u _PsWTȞOŽ!{/@D4z{;i@cv3Ou%b- ;7Ǵ珯?3Oױ 5l$ 5+MhLH%?|:aߒ6>WkҪ9=/"8pgSyuWVe1/ !TIHMhb1>נkpm٩|b6Bi${[oܠ?&?imM3M~,s1#j'2Ώ˟2ҭu?(*ĥﳤ$;skfzT3#6[z}&ߘ>j(v` L1):E𐧉E%rns@(3>}qf;.aLu/)9Gpڳ.v4Ϣ\[]}=ҏZb#A& NsUJgDpGg@p'3"@ M"SAu@ DP@ @ [UjԪ?Qԍӗ|orOU T-pޛ]&BR P߶jP"h,,xՆ6ZlxBا;ooq;c;+csKoM-qUY4?dqN޽[:4_} J{yIvuU>ESZ*mMj+n點n{K[vz^UI)ҕT)55U8}[37zkS^AIc ;Cf9K~QBQQQIbc@hS~ȃ36o&_"6c 1J l-;%j,u^ίr|oafE,|g^,܉ .شaSgK,ܶgCvjdéKDpo׶ݽ{3BJe|HPd˞ڐ?lj*i[fY{B^a߆Q 6ͬ1)Gb>iݱ5bPIb ۈ!Lo9;~fi@c}~ޘR}ݯ=z{2@_Չ}۳_t]w^su`]rO AyP''6tuj r$ hU.XQ5G:*I-$ k ->:-nSKxpr w{vdO,0mܧViiO7Q`o?VZx\yտ ٻ+ľGoo"5wHbА/[#: O,mz>2#8wE& {VNtc'oOO0%cf[NVFXs l<^x{{$&e9b oAI.nLB3|. ۧbдA<7ޒ3/x qsLޠ bmÔYubs?JإH߿r@9>iU+e R{4߮rb4lqNgZ#5MDeaGS#G.2t`-wQhcNCL5\̲ {9TǞSɸE ꆃ|YDHeeuHH~/8_L% ^cX'pv+Ate ]g.{$QF7UυOŘ zbmt2>נ}nNZHx&vBы.4%}Ro ARJ_Ս{;:toGPa*oklK_zH᷆S~5C>nŘw]-I;ax7B>fEOyRy=;%iI|]#78Lٸ)VTQhya|pᣑlxU=KK(.~S\XL ln[N:4oM8WbfIi<H1j"@D4z{;i@0RQ#{rn< ;nԹ5PY0n>@xgr'7{mQR'(4ë/{#) znO!#~bhie#4z)yen Ͻ;c(s@e-cٲxW_sʝX yG;k rϗvk׮|q<_M8y"wɉ+DFYj Կ@?Wʋ ׷A?{] ؓMdhM9+M)lHJ6G]b 4]n}I~KSFLNw)*l JsQ]"~tEZ|7-nwD~2AwԟZvk1 g|ʌs8pɼ:˸I9Bb˚,GRJ}%6[sYzc0g>~9-nHOJs ( נ.}Z_{6p˞-s0 (L-onx r\}E) ˚$ƞu2G\:lbW1aU|N`NUiuR:JjMŸ뉤 Z@כ7}kW-H7O QK3W|pF8X\4JsNSb+=bH9 iy점ڛv6[_Z &}[?ߌvFSW@ h-oEU@  @ @ -71ی:0?Rf1]k:n,gwjz_mJipс!TAaKC=o=.C^|$f;pl1^TCqW'he \-gv_N3yw9{ŧ/fg‰2^o)pso$~{P2?YϞg8}ɳk29|{+fL\g:-oΰ [OG_ ͺuz lAf2s|)#(f=i)\9nj%9%ĉKm-v2NmLzwg¢5&əKeCoV2(W9ߵ3Cѐk}g>5l8[쐐ˈߙ΂͍S 4ZlRFyaqߙ9V0+G9 ެbfjVDSҢ=x>3< dVNLϞׇR0IK~|<{/ITCUb$daThX1тt<'-4>R[ʱ>%[mO.[i>QEi$lfnY d1ߐĂ}3-,R!G/^L Kp6ƕ;`g.OW b7\dpxG;,'~U,nZ~(nxv#_P#ro37=y}Q{V}؞zr+~@}+ء6\.Y*$K4(s9;XR^T##R*xWY̺"StmW%2Mm1-]MpkQ/幞,}fLF)gkX} w sbzOK M3e5ob uRD~.ڑ<~>RQ-}y+*N^f1Bog*z>]?]1h5P;s؜æ|_'\fHWg 4C#vQU?6ē#6 Q\ְg]3lfϮf\pkїʳyS+~ޜ+ϝD?K(wPP].6}{5rS՗ 1 *ؑ4Oyc6$8%\d\>sBTmWfWhMu߸va%2`gV\=7 U*1a%hqSbr2\Dc|%P̰+$s߬"/_TNрh'Z4K:h43(N֝)(x%ޮfdSY*i<-C%էԳyF[c@y젔6& @ & ]@ A] EYJMPPվ-:0zoUD@+͂x٠ ZO;ct1kjpƞn0]:ut1#C:#ɷEgI)ycǤW7Kl& 5oM|p )znHR>j闌R̢ѭSĿߢH<+e%H{!sR?ۧet bs1K;KRFF!eTϸog}2bhignt{ei̒6 `"' ^ezK bD^Ϥ{T]ݕO?ڮ xCe&YU ̊XHoK9U{j +3XZ/zo}n2%j,si +q ;:4TfeUvLSU-RFS@a:~c, @umbOfniq;\^PVKd< C;n/tp* ?lj*i aј.X^h( Fn !.~ 0UlЮZ4RX?P}a_I)m?Ŝ`pr$&˩=62>q('=U.'oݡ&}jq$)$2ݛT;Bp/lgcUη "_CH,`c]uގk9ͺf=\ߠOIeX_ff9<.'a;n."Ʋ )lbFBOj/~(L[.5 d3'>L yu|_J[G+6G]`sT]<;, o@ %\Juf|.ˉLl} GF`4ۡ՝ejjg4lagvn? ֫Yoﬠnw31x>S$q}?̺frYw%dFɘUF[P-LZ6V>*geٸXM5`7?ǁwq`[[y3Y[WNVrd֎@ gO,|o@ZҜg`a&˹ٝOLn=٢rS?лœw: zrQ|2tCe;M\=[;+! ;έ(ȋ°ekR O)߿&ˁbVb6jNP iΰM?Dw) ^7l?ZT  G!Qw <|Cwb Qyac_e#|Gu)lFzS7GV>&CʧE YQhȩK.0$0FsazA zkR Ez /%WtayCfhs@D4"¦3)3ƀ?^WŇ VrKy,C>Uw^ n ɉ+Ǿaܤ '٫ g;r;PV@Cv6 'q|߿PVea f+GIJ_y%z ¾%mI)|ְ=6U@H _VL8ucגi`de98d^!ޑu'8ǦهC%#4ѻws817VgHL)kΌ4N%idz/?1D|Yn M?nBjj*!,ܚSsY=Y&}d{lSS?l>tQ7I5?{8߉eog}Ậqjz5!+--j'-W YѮ(FJ9ZW-DbUO^9S7ψ4ik!9`ifpMOCe;]2vg[ggfi*VsYLT]36\ ۝;6[6C@ ,m@ kz(qx-E;܃i6alv8ߦ߸?Wi} h>M6&nv>:0yjd Җ^ߵm msKP g\pc9-3 7N019YH)cMكQ),LԱS\Z!43c>cD/ dzE4^"ڒ\ 0R Jjj*F)߼3i /YI1Ɨ/wD;rYڰ ;>K"q[y+ZŹ( },Xwh*K%$p79{֟L$^fM+tD~ȓC,Xދp|#7*R"/>:sE e.,dM`Ya1g oy~[M\<x1Krc5 fq1eXz9IFumpikPv0S?ር5rB=Y$׿? ބS7[-=+8:dOp$ԟW{s>ꝪcwƜf_y5|:BR/yp͏- =*.F֟BmlOM3q\tH; h [gi4adBS(M0fùdusNjPfrrcU hnoM>+la )L^1(eO|ȟ!3Gq7mL ܱ{ƫ[9v}^U;VRgaz^8xKn+k('yuRR-9\ޘZcX Jsh[a3&hDOz9RyM~}V@Cfu6.e!٥w,3ЫWUc##ڢk`{'dyW?Lˌ}~֜66hgGSYhlN- /[dLN}ozoFSaI^!7v.pW^׎j1Z+L]MeRH2vľ)i"%ʂu5))8UdϦW@iTg׿O(і;|H/!lf MF5?M \%N|+g2o0oLl4vVZmLˊI%MeKIT⮢‐e>ێZQ66|gp:Vre.{yW1/l^ ]!/g=*bgֳtêk*jigp0Mbp^ !VD Կ\<+_.?lj*iaݲk[@ZG1'4SU}pF}9ˎ02Y:ܰ䓥/G2HHciUZ4R`b/DZŪCrB!^ȭ z_KM]1t7ոMxN-J:L[L̫tKy[ݷD'r;5{븇|I=ê%ç}K4k&oH/>ú\ >~זhz"Ӕqn@=:? ȑ釢\x/ w77o`[}gK{g6 oo#JfQݻۈC NA=nܹNy0 f0w\7Eu|eF*qnc>Lcs?JKG-6E]4'SDpjX~xE]O*h=V0 7Ihec1 uqۃO!퍛89ٰ(ڴy=cOp<2nc8\w'O sp2PZ o9$oJqB?2V}' \Ѡ)q+b9ӻqOQ }+ ;f_MFa~ bAx 4r +ס< G܏7zmrQT\g?[zD2.h2xCnOᩔ};J8ĂK kx)t2V" %F*1u֡0tkgbiK7h@մ}<@ɪ֒|?Ksx' <($ 76rRCQNg`,T§b}bmt2> DK=)nPY>]/3r .gʟ MsH(+ 1|N!ajneas\7!MKme JJR Hkc*|+1ݴ0MvD6m=@4Frfhb;l-pb䖆 !'exF~Cѐ|%oʎ*3YB>l$!+dJ?-% iJJe8t3%%.'9Wr eniTg<_`$5GbK%g8p$'~"´$jK_036NGWV[3xbȓ\pKAPZ\BqQ|bJ5e@UE Ȉlr9s 3$4 +HX@E)}0?zY] *AQs+f@V{wPAEnnUaYoL6?֞fdOARGJr*`QgH\! *KuL$-FڜPmTyo9]4/p+1"Q7Ϲد/,-Z@ʃ:Gƙu̭eVwuj?^!ohGv-_qRp >d򣭝 E9u_<]{_ "lѴg` u _&Z7,ؓ(;uN8Chܩܾ빼ith  9uΕ¹-vAqrFP԰Wo/PPƍ\E'6`j{_)Gz ?ͱ>:cf!g ]T= 4Wu_5j PbqH$U`/-9F|q+3+xshTb GI{ $ Ć1Q/(Gkڸ8,ؿ"͈8.%&CMN] 9Mr2 θWQ,_NٙcLyE'?y6n„%] Q=mSQu0=eHJZ.XX!H@є'~*R"t:՝ ҽOCr0(=´W7MZ ؁ty +4ì<o2G.sv4\JF.8Th)ТT2y eWx=d,K"-JM//^@~ԅ _AԐ ݦ 6lb-NNh|$@ E47؄Z=>V_&aEC؋G~I佟1nZ? `y gpq 4'r81'evsmqy%#*"~~Nk$9j*O5G.@ ^rܵ.ɸ{AA] HPOHH A޾*ȧ.-nSPPp]6SSSڷorF@ &,--ڵ+mڴA.#iӦ ]vKܹs`PXXu]|@ >̛^s5vUS/,,ʊ6mڈ@ ;v ( a%@ Z1ܹ[rI]/K @ -e16krIENDB`terminaltables-3.1.0/docs/asciitable.rst000066400000000000000000000005161300101162000202650ustar00rootroot00000000000000.. _asciitable: ========== AsciiTable ========== AsciiTable is the simplest table. It uses ``+``, ``|``, and ``-`` characters to build the borders. .. image:: asciitable.png :target: _images/asciitable.png API === .. autoclass:: terminaltables.AsciiTable :members: column_max_width, column_widths, ok, table_width, table terminaltables-3.1.0/docs/changelog.rst000066400000000000000000000001741300101162000201140ustar00rootroot00000000000000.. _changelog: .. include:: ../README.rst :start-after: changelog-section-start :end-before: changelog-section-end terminaltables-3.1.0/docs/conf.py000066400000000000000000000025451300101162000167360ustar00rootroot00000000000000"""Sphinx configuration file.""" import os import sys import time # General configuration. sys.path.append(os.path.realpath(os.path.join(os.path.dirname(__file__), '..'))) author = '@Robpol86' copyright = '{}, {}'.format(time.strftime('%Y'), author) master_doc = 'index' project = __import__('setup').NAME pygments_style = 'friendly' release = version = __import__('setup').VERSION templates_path = ['_templates'] extensions = list() # Options for HTML output. html_context = dict( conf_py_path='/docs/', display_github=True, github_repo=os.environ.get('TRAVIS_REPO_SLUG', '/' + project).split('/', 1)[1], github_user=os.environ.get('TRAVIS_REPO_SLUG', 'robpol86/').split('/', 1)[0], github_version=os.environ.get('TRAVIS_BRANCH', 'master'), source_suffix='.rst', ) html_copy_source = False html_favicon = 'favicon.ico' html_theme = 'sphinx_rtd_theme' html_title = project # autodoc extensions.append('sphinx.ext.autodoc') # extlinks. extensions.append('sphinx.ext.extlinks') extlinks = {'github': ('https://github.com/robpol86/{0}/blob/v{1}/%s'.format(project, version), '')} # google analytics extensions.append('sphinxcontrib.googleanalytics') googleanalytics_id = 'UA-82627369-1' # SCVersioning. scv_banner_greatest_tag = True scv_grm_exclude = ('.gitignore', '.nojekyll', 'README.rst') scv_show_banner = True scv_sort = ('semver', 'time') terminaltables-3.1.0/docs/doubletable.png000066400000000000000000001313641300101162000204310ustar00rootroot00000000000000PNG  IHDRb5 pHYs   iCCPPhotoshop ICC profilexڭgTSBKЛ JFH(cHP "RPD2ĂmPl'Ƞ*Gxz9s @ @H* e'$&r@&DFL9pˊ+ U~j~/9%RL ˤb)V LIBbv̔I LIL6 @qt{`ҥ4&؈feg/Yʿ3E+x.@䈅ll eH4f- U(en S! b^_S,ˊbdz@ʉbɒ(H87LQpjN@ 9S?ŹS:OKdQi@sgI3b3$(S(V^,UxA ='7ZW*QܐiH06Ă8-A84uoxD!eT6GijŶuHHLbOwE,WnZKhh5L;@e`&!؁7@D@ $"AdA>b(-aA##',\pCQ:@4=D @($IF"CuH)RT#&r!dy|A12Q>h(.DѥhZnB+Z چEwP9b,L\0?,K0 +*Zz[{}p gsbqF-Wji_ Ty-=;{VgJy~L~AAAcCaaaᨑQQQcq&&&MMM5L9yͦf^fKjn]̳w߰@--2,j,[N]}\gf귢YXZ5[ Xì ۭ_64{mm6mڪنvھݶگ`#1qc7'g'SӈsN~KFKxW_5']?9Iݎn~}x9 zxp=y=ٞɞ{=^^\Zކ|z>>>}^J|~s[T< 4Ll r Z 8MU!CiѡաO,$ahxHGsG@'b[Hȥ#̋W3YmT~TO4#zq11cƚb5}/'NXp5Q+QؑDLKO0 ]hpˋ Zh2>9>@Wn;Iٙ2{#e<҆=ҷdxeTd o23d~̊jȚ [I'Dj,%K/[nK/Js9RT,&3 ̭,nE˯XaWVVvXjjdu5k 4Z[MaYu:t XXRܿ}q? ~`j~ɕRҊүydSO6nvڼ{ ahݭ^[Tok+g߾x =;(;d;aUFU[VgTߩiݩsΏnݲGgO/{{ VkR[QG˭{?n.?7k՗k57975>m5\p!C-V-ZY_{$HQ-nj<8^҆hmhw$v9yW_N꟬9~jigΌu^M?;ؽṄs;{!¥qe'\it9vש7:u-[osn_3Nػ  ÂGG%UW<~Rr'kO> #珯CE*=o>98rC//_f׵фѡ77o7|}Xؓ?|sϗ/Ǘ}%~f{Gb iioɎ d '{48uuT({Dzxjox59iv^v|Ļx9o@ ޕvAGG:Da 8:ciTXtXML:com.adobe.xmp 2016-06-11T22:32:40-07:00 2016-06-11T22:51:11-07:00 2016-06-11T22:51:11-07:00 Adobe Photoshop CC 2015 (Macintosh) image/png 3 Display xmp.iid:30e4e5ae-55a4-44b6-bf92-69fd7568c4b0 adobe:docid:photoshop:8e773652-70ed-1179-ac3d-f90aca98f8a3 xmp.did:8bbc5531-ebea-4859-a77a-c600213da1e5 saved xmp.iid:8bbc5531-ebea-4859-a77a-c600213da1e5 2016-06-11T22:33:21-07:00 Adobe Photoshop CC 2015 (Macintosh) / saved xmp.iid:30e4e5ae-55a4-44b6-bf92-69fd7568c4b0 2016-06-11T22:51:11-07:00 Adobe Photoshop CC 2015 (Macintosh) / 1 720000/10000 720000/10000 2 65535 501 172 @ cHRMn'sxp1\'%mBIDATxy\T30þȦ"*kYhj bbPZ5-JM\2E,! -As_ U1#3"&~>̹}s{{ν<2@B A?d2FbذaT*Zh!#A#$--~g6l؀$2@dwޡ @ 2 ,,,?>1$ѣY|9ZVXJ &BW_eG}F#,#AC裏V=zefv6"+鐐PHD_ *a @ M0(ZgW7CН? ={?I ()ңsꂅGd-"񬓼ќ(]a<[3YҾMJ t(q #VYnf۠-)Aae%N@ puu /ʥOQfbggZ_^\LAAmCYs銋)('DzC9yXkb IG^y]ٯ',R.JIB]H.I$e\̢ V\.LE2m_ug~ïaD,֜L|,V RY5if~ j].%PbiYԊ=Ihs9o/'/場k=}^WŴ\:uEyUD&6=f>y.8E_#zSW DXz4evvc}9ᛐ{Ttf-,P mpZig!GŗY'\A=vvvIA۩b^O([TD:PA@K t`aa%8~"OԨŬ*ΌGo}7tZ NNW6T߷7*tZVNh8h hsw{N8(ӤDjJvnbMszײͺ\?6f[.骪'C [[;ؖcę:,t2,p$2ABN @%3tL X7׷B׼m^O3q}*]Z98OLYK=/@^Gֲ3<1T6L^tdÂ=וHv,(?r^gzSm0o>b_`c=/ 򠞕_n@ߒ^O=J\o7.~|ՠn d?a/?E(6#a/_n0=yb8ܬzdppz"~۰1⑀[>ecşvDnE'P6|q+}(!7O^EGykii 7h&C$laT9s>v?8y,2F蕂0H2dj'GެE=A^ [[K;y' h^uڽ~rkkkNʼn7;SakJ~z]fK-,2,@i_~/X?2-c5z͜ٱ;z]krfeK{A,tZ$AO]c?#y3eó71Yb -Wk\KoVbhټ8067Y'wb<4q+XU3> t6d^þ)lїW>,aW1-_tBW.[GQ^_ϼȻH7s1/褸H$4쮕|`+> _r$<1a23\4y)K/a%rzL''X#+VkA_A&O[T [&Vdv d n3-@*qzڦ-\,uǫhScG*=ǼKnZ20d{Jq?ZJZ'3yi{WRVfE-qYYhfU$JNe2pI.Q y=&.S?Bn.'"::_+I@{~a xԫvUslk3^VOl .[y^?O;AJV4o=yS3y>I=(A[A62uXm dnZ2;;(0Be7m[[U?~+.*]b^wM-%&4L[dxn)Rɸ:5NHāy\3r$!ƑeͱTvOt:t:t:"IBxKa傔QA+#M߮`jU=Jq]ZZ쫐UI:@^r\NHAJ{onbKY lӖ7G՟~_|]*,!XwnWq~loJzU[`c-|yv"C`Zr^?¶MU3y|wF{Q:{7>"g˺X'@{1>zj^She}K~R%?ѡCʗWiSPG E#u]׮3v 3ޕD_PPj~~ su9\FAA>5ܛ23j/!:#t9(,dt ϴ_KHqe2H]!׿].IjKYZb<,;6KJP( #TbjecT<ؿ R1GszrGޗg/9U;F6PR}mWXUm'H2?wԯg{ ^C))%Nn/SP%}NR5ÈJp%pIZI"lv[Ӿ-$l[Կ}sHk֫%D|t/hs/\S?g~&1lN͍p3RѨZo.[72Q@P[U#MlѺ r.KF6zW2AdʺSp6C^!Sօ2A5{AsZnEڵ\Ӄ5Vӿ`JI?:#wdNW$doɗʌ,@qsWsH蠒j;'Ss(.P}7euѵOS9 T$>%< Ⱥ%;$*uK6H#Q~*Iu/-Ue; @:),.&N.S7|]qWZi$Di)W}JfRsFA{7Cb )̹9ch_Wt5yzRV,[)kv'P\깽\߰)H?GB'`Ct6& &e4fҒD:]XSH$wzt>ƥ?~)3W})}1$_Cw0g2Li`$e5Wxb|%.>S_|һxh+<{=$:u, -OK%wꖒ;v-k>/?f[sx(p&#t|3Fw^x{n(IВ*Sje:XU|/i{} W~* lt'>ON-<>- W\z7Z`Tz'xTaoI|nhoQ\0 +>fAo_]'Ct@~r~O3⾕~ђT,dhnZmnUuզ` v7զX//.~pt\Z/dΰBQz#U~,GtA}P֥ eErsы/c<{/XeZ-ZXY)ncu̅V( N }AV\aD7u!2mY r@aEWBQ2 +m'E+z\\F]#Ec,$t2$ B~ \:~j^vF[D@@@-Q-h굯B~&V+~&" @ A] XPߴi@ M qYa @ n#۷oUVi\F!??Br9J+++J%J (((UV@ ۏ5  J%888Gaa!VVVB@.SVVV]t  @Hy9&JK)++C.o]tP0a2+VTb\^HS1ۆ?}LD!Lx<{>S^n OPQ㑟귿M@\:$oK8UkO\.n/(/G}+i4ZɲJ? 05a ۜ+D/ %F/$I\AU~7[zE ؒ `ue6T<\NLVVyKr~$) aSo={q}yLxa3]/]o2d8w4uٕ|02wo:a ]BJ|gb d2:CeРA 0Z2dѺ X=~l' ݔpp5K`AkI`t鋼0a2ӟg v [OoJBQrEv7TS7 Eop&G4Z/8BP7nΡln ҂as2o2i ̴2涚\9.lcTĤoe@ʼnz+6*E,B*VjH:͉U!_q6%tf\vnUGjVl9+G-4b_DÌLaeT''\2 eoTRSٽSW"==d.?K$'&~|#i/k>{#=Ln5~>'iS$=#rWp>ZJ7#>?ο)Λ"#-Z/G HMt+ݷN\ISrtŖx\+Fmdr;6Eҭ&h7zo7ޞhA̐tzEtwdzne'pI_l8p~̣ܳ҈=3닐v13A*;<\ܹ̉ oP]_)s!a=Ƃ\'&93Ѧ_Xh#i|;eq$l*f(l9$XKĀ3ξݙ7RVcMwJD%zq;v ""]vΝ;_YƁm;:g(kӣ9}_!4HKG_c"fUŐ6F\߸^2[H^wXCތ_GGRk5TXO<=Ƣ8!?I#2__/Z?50Rm/I@Ѭ%VȬaViQ}oc/Ri$m:r I%(m7ks,6:~ANrb@hQXW` Ί="n܇Fe##%늄ɦ^-Aq0/S>fΖ ;!/{##rdbr͞u\e%VHͰ,*: :$ kCJPjV+Jק"8%~_c(qe~zyl[V'~/ʮz A>gykNW؞љśf֏hyj-oZ"G>/t4w=!]aÌ+òM;Rkn~ Af4}`Wv8gِ9c'\A% yyGPI3cHsAFrG/wl"e٩|J6ăc?b8lNayՇ7gyn @POL?S8M6UNw N8-Т?T4ᬎ| /Dhv1=,N⯮0'?;x5QXPsa~! q|5KLGqLnՇPM1,UVH9ɋ?=Rrr05 s#7觨/, eY_ǴVLw)XPJivvyoDsrt9Uj{#ô\{~-DaOr1yM GC,@A{HPVlx\bTCJ\$!][b+?[;J,%(M9=ΏDLR/`ʏ.IZ\_tnC_ ̫sU=-9WA)#E _ȨAvˊy1$eqrM1K]G|j8x-orPfߩMmw8貪׻e½9NpkeO7E5o^a'?eR9;p5X36Eb?#MTO y c;R̓v;VZ5ЃvGr8WFJ_1 <-hBƵV<ק,#aGTggŁhZdI*B;pҏgo&ωjOgx@_5y4 5}ܸe 90i oj'D.TbHdrE:>k?#+ðY~^çM &X8W6f6lS_ fytݝ+x5`? R!ҵ8\ c@5m&MJ"OႯ&)a<3c8Z6]"`B9MnRڭMőqvrR"dhA~=iJJ:Oim7nl1J@O'#JS!t"RxOqcSXe鲪/͝=R?it'O!]LViE$TB=v9_eS49wTGq ֧pў/\! 6ƵF(q[mȍPeWʴ}e&H7kP5_DA{8/Ӂ̪b&O|`E$>Ӷx"/>K'd ʙ?x"7`y ie,'Ǖݟ"lA{ɽ!N W6p8o5o9M"Lt6Z|ojdt:$IBӕ^RRqs-I4a_ qB/H6JcTUWKAA岺n1RU-|Y'Y )(PuC:0H T_ eJ;ÒK6U}T%O6KuFMi#Z6S![۹m]9U*WJ.C%';sZqj#tZ_}A{/fR Ie~/)P];[%%X!ڭ^5Sү׊8_~Y0aB۷o>,eggKիW .HR\\t9)99YJOOrrr<)''GJIIVY/a~BGE5}ߌ"i<3yi9 2<)^x?lxǧg6Nt.ϿDaFej^FVٞ&ܦנE#4Fp\CFXq 7 GBQQQRB(v=KYYEEE[^SP7&"&Q7(d-c2@32Js+/@@ )A]^)3VߺZ@ ?gK;ٔ+=*5>Ucn\II!%%̪b85{ү5j]Er![Z4:M滕o@ ,뼇R4Xq -}6Z1<۫-嘲 k2=԰n+Ek &Ou|I\@2<_$a % C>drsig&s81+)'dMd]3Sh5L1U@=e2֙Nj 啎|+nJ-Hkn;'>=Efo@ h#uRBg1.Րٞo?CnjV^9vF{xSg}|qǽU -uIwO*+癸uS\!|b>&mdS%Ò&`~7}>K|y nnfRVer1ÁC[|G'=PZ8 '*'snzҨ݂<|rFiJe)&"(noMA(\Y ^0CYYG (a䶝Y{kg{aIJ8+[2@Gzg,b'U9Wb}}vVVd'"e7xoZ>ڍelr qdS0hދxs׳cp?W|ؽ)nǾxXa'Gf TZ[E뭀5CiB-샗 S-'OM$݈U!<FLDFBiI{7򦮿(墼)+َ Ùޘ ~?у[2$i?Ճ*qI[D%_ai*YmU uD6960Z_@SX1 nPqao6V6X+Jϙ^ϘX _00"!34^svkDTl+4].0VȐ()܍/E(o[OWߡ%~88b > h21"e?3M&Y׌7}&?srr@5{ pښ=9'_م,ED=[P]zrz]#k}_A# %rJ߱Z["klRMXQ.Eygtfk1u}&Vtgfiy$zG}_]5D5M7p35z;$d $=oL[{s2r~M.c0zYvVJ([hR#llu@8LdFa6 T eehJ4uAgn/Iދnـ*0',2¨p*aqMᯂؔpkfStf"nkn@Z^i]nn M]@p37LΘ0"FݖVgͬx G/ؾ;^h Dq[ln17mg@ ~j J(59ׅ PWr8jcBuVN&ݘWg?LVzM\1]̔:g(kӣ9}_!4H/룯1D{3*zbHYhGvk@,&.شN-}#חI:c859ZiDL!XO<=Ƣ8!fAo]?_͎fSe^xz<÷&6 ٔ<w|QDEE1;E3m__/Z?5s_sUW,YMc<6u }OV˲XWקvexMOI=/5O[*WpxMe!3͖ie[Q?&+ :Afot$l-i ;#Zof1~e7O&4w;s#寣ipUѤ%&Z˖xv1ð,M?Gtt4gSѪY"~8WV'Pld*E|JGFs);Mٹru@p;1ibi}ҒZo/HA*e_-JuG(k CH!Z*ߔpA 9` nImo]?_ Եѳq{"@uV~`pj@}VGѧ6v; z r>㈿'R -gnyUժnD6@ h&A#Adi@Դp o/ g@p3z@ ^?QN?OSg0KΧg@pg3♺13KP(pDc$M(˯q\i\7~uKD)z G Mq(m?P \?4uE(Me E$QZZ!CZcD<=!B+"K E(X?75ґV N6آ2)~bmLRw13xuS󇦮(墼3:P-+6/Fv2PCS_ 4~6o[/]΢iCS_ 4~Qu@ l3Ÿψ,m@ 4D6@ @MjԨ MA65 7ԭ:g0%$UVN4?4`MV=7!&llx9(n6> uމ~{A= I/2tkj:ks9}GO®US+Q#^U 3tm T{3i%!!a?k< }Tn۵olZݛ~}BF/Ǽ7g?4$8Y?3(4V{dO5 //4c{3jSfԷ4%Y͇:T ^ u3;۹3Y_>n/fr ˝Mwv=fS~Kr~>WRRHII!=t4\6;{r{e/&43bxWZTDAAVď:~By 6;Pqa"&}M1߼6dRljXU>)+/uLfjtS>*̒7_;)M&Л-bX鹥S[*Y{aEž-m)Hā25I= 8~V"/36Bbb" fڴIMO%5=ݫ0oF)̢owG4ѩ_ÏT7p5៪8Bjz:a3}C, كXu S*͜}u7W:&}L* C3h%mgQZgQWQ#*´:YCc,ځ3Y`> D=І- ꦃ|F:ND)GX1{<ǗOk.wt'~-7\KŸ ]\RF)LˌowtPO|JBZ4?L?ZD v|?|aF~]Lq5ڧifG$-yCV 3{(^kUaH'~&VǶ{ #t蹫Huj<vYMv{ގ7;92{W%ĉwѡ7pcN@Eh~8G8MB@Q g+fx;@^No[<$[3ϙfr=R.b }9p<9 ktpk;.Y)%~N6LȷUÈL_]L'S#kɺ.Xӥk;@Qv;|q,.ôb9;oM]&08pm h|܈lXq"Z%59c\/_v);~[1HNV71SH59|d=O̫w7:an,:Y\XDaAB%`XwvyqR"ޣ;og-W}NNpAGoE_\KDf$GƏm $66OW3?H3l6ѡ _|Fge:sxmNq@UmWi!3l5$reml~k:5ąڐgx[d.iJ Aj2h;u_W5iU"@7F5} 8JE) J(ⷾWciZ0l>@xwerf(5y9n 6JX-AwѶzqR 424H ^g;[t@s[%m^spR*=3|t6l_];ұcG=vߏƿ3|-wjlƕO| /s5yh LF?~^L59IY b(-˷ɹMF&J{ƴB"gO>HGaHU<%*H&ﻭdMG-qـǚ)xVoR:hWs@3mbYcݜGHl~k:io+s>.!ɎιU1yZ{^1=_Pb[z*'s45q܀/-~_㪦?!գef?ih DoqN8ΡX6dΕ9̔>/hvN䁍Uou9f}n:RRR}ͳ_=\J8ġ؋whFZ w?élɠw֛*vkϘY|v爎lJ1/+f~u3b*,ǀRJØ%fu]sY6Nܟ9dQkOnc&y1ӞZ'unIÁ /x7vdkOoF90^ bUI3yO[{6u뻜!/o3uLuls:~HɃt~|'_umA|oO.$[iyI<- i;\)Ҟ]=[3QGd~)23z65y[çG\jb1zY\F'UH#Kw쓖bMn )(P}KdwSJ*Zժ[yI$ޤ?ҭO(߬N $G"M _m8?^bso66`7;f@r9J϶QGYZQHX !g[ۯ6Wiڿ'ҤbDh4F7V+i*e _=p _ا11ŸA#gn"NNgD6@  "K@ Kxcw(Z- ƛKU]WDwkΜKDL|3:}S~ekk*OR>l[&?kw~?c>2%Sea}3Y$i[K[-<Ǒ|E 1Xj:qMw/'|',>ZOMugB0_pKQ"a$ +B66 eg>G5^fX!of9c_fSt!Wxg=y8z25^_S d#u6#/w.m~]xٽ+ Lf3Sg]ш̢CYΉC1MG:w,j]Xu젙&>3E/]/wef{AS dcّ9~ۢc|J>@L6u%K*IOtG$}6C(,!/8OB ;'9̞]S\<"Uŋ<5w'F~ژ YPҠbGň^JA^Rrԧ+GrY`ˇݸK!;I-I?WE7=X:^^'92yu\&t`i7+k [_3VHYZʹG'wkx0=/~OvKw miX =-^R}5Kg1qi4.>@tG7u YLd1ͬ1/ߎIry Ɯg<;/I8;}DKb%D~T\h&X2Ɔ%t}LK֬ķmTRq8UGmIq;JO@$9L{Y ꙷDV1xLe3/d/1'r%?|~R>G.5>/VsD 傓}^mK+Y{Ylۯ!n52lV]xYe9@#AQqVRͼOtdVxMR}<Ӓ-іC1ײG'S5@ 4؋rqFdq@qE/q* (b.j:WG2e?i3RxM@D_MSg{guqR ̨'d ʙ?x"dW{ޙ*V4PEo='JӵYZQ(LE6Wr5vL)-5)n9 E<ΜgahEeU }źBoM8TUo EZnFxDL]eSXo[j'lT~i݁{|-O,3JDJt}X\A7LeC'"t poq3[Lxu.ڂZp83KQ}8K6S;h>,|D hL,m:! AgD6@  "K@ di&W{7E."ߐq/t jxuxoB ngs%/h$'7n˥Q˥őc!I'.Kv,1O6\)9?G:}t6tFIݐ{ʷc5p%J8.E!NLb#?VG-I&E8o0;L'Usଔ%~ "wfi5DJȒG}|ܔUl)==]JOOf畷ϼoFXؿ&4X*))4MJZJYffH +[S1?^ p=9(11WhDܒĪlnCY0,g}Lw[y-7,m1po5W_<&n,׎EFFuOʼnz+66M9DV@v6 i]x|{}.+_644'EΩ8[dA k3snf5e eyIӤA||ErUWMI!5=谙i;'>=Ef/`ЌnHH^+>&-צEzz*\{!Ob2994{aBhWZ^g9U_ÏTàcE௪uԌdIMOeA3ξݙ7Rvع oLbl?kӮJov4`Pע&8(/iJf:;U zh5s C>F'>riV y~8`>=t9!GNW 2Mog.gyF_6^>*,e/`")?wcGX7q4wcc{\삐Zg9slh"E(`,R;;Ց\NdL}c:2c,;WP5+i ;s i]x|gu1!H34Y `2)4m;*4I)q5F,3#߆ԟxpg@ gRq3YgPh?mVM֑N& =玢 ?dS܎}f'Gf 4VǶ{ #t蹫Huj<vYQώďIZ,χד-XA}:x7D1cQ9rp*Sב ;4X&biߪYM_ټ^7feldvn4GAN&?&0gDLX զ픔kHGՠjJ>PQ2d+w N8hN %2vX k.믑km xI}Lluv);~[!٤"iH7\KDf,G-)=Vɝ@Ҟaa8RV}l>,W !Vw) 2pm wbMQ\Zؿ S80|'e_F.e&}fl&(_3du~F%(9ϔP؉Fw6Bˁd/{RUeo'\<~u=?}o-1>A-ʄ\I?Z-ᣗr x,Rs[4r7lƕ?L'ƷWM_G7f;4 䎱3 h`lyjagCh^_#2q~/_/UzgDIKL-1(jV225kB~I!ͺѓ\?'fcfaU#::)ILz?[+s(6 3!w4'Oc"q?զD2PhF-=b9zޚ8v'ů_ݣτnzLJJ W/y ?f3?$Gfj\1yΕ9̔>/_vRMgBV?p*!=f6};7dS?͵Yo fS_"\,-ޱOZrSK[-͋|/o|WKAAFSJ*w ucקLƨuՁARPN~R䨂B)!A ~n뺩عa/l7Ow zь͚7jU.ԫ@o3u6&t?i~Ȥ:5AxptnX{U\{rN.{~>Nތ?jT!D%dq>곆_5N=ٔ+=50h8y$9cq:XE͌5 Qh*}aj԰hv|*c"Wj.\II!%%Rc3(˗ ;l,m8y_'GEϭj6;^YYgO2|{ȋpl\bɀ~uAOѮ1<߆[u#eJ'xH /~VsU~!gz8&؏'l^ݘfd[ۗSdC)KTtqujs-@6'gg+~J: ~ +x}z''{6ZbfwpdɞT=+`_/O7I9~ۢc2}7!` 5tgkѮ$xʗ}-rc éAiL'1Ɠ\] i,2xdvC.#rSJV8w,#q}1clI_v YK%(g3K9% 2;ߌ/quY HiggکY^J[;S9#qlrpF-̞d4+lVJ}fiQYCvNH0RG"?Gԧ]1b+я2kwr0?_lcN3V޸ [4G2ZZ?Lk[P֛\.(@eq~dCR6϶Ƀ.@;uBω&Z"OQ)+=qUd#K?iJ\>4LvrJĢ$!#ƌ%v:?Λ'OfZ+`M^Cv1f)(C 2Zuu$tJsBeYZ*/)"[ffl#hȋ/ Srn ,eP3P.tٓLe4@ŵ-k`03{[p˃R&g|d7+~MWZE&Y t Lgw, 6^6xf`;oJWYab㊋DI]MI,Rdqy}6a1(q כatK}-Љ?i$ͭƬ̊l|d lqV^nxvl;'ߗUc /"̶Ok7d9W ?<,oȌ+6wG1Ry\dK>tYI_W9Bmm9^_D]vC?ωlauII*#pbǧ{2ܖe(ZX%'D{6AS Ycr(ܜ[(M+"l)VzE5_Z8KJxck7rNXV@4G~Ƣ#{2ؿ8S|G=5?6ٮza IGlx^杕[@=_$l2>wQ4"ۘB!Ci+gϒ<%F-H);y#+5.HJkQ$i5>v% "}9*!뤦JYӀQ.5Bgγ0'089,MwStf"⅟ M ]ꎆ #VU2dEIu=$s{jY_Cᓂ[v[xc"+ _9g83M o?# @HY@ $KEdiFϘLz FY(:^j9RW1e`.u 8m>&O&<R @^n OPQ㑟귿M@\:$oK8UvX} 3Ug&83/E^n!撜'IJBtoNn{k{|mz_;艺o@W7-FvL]| N*C̊[cNLxa3]/]o2d8w4wA#H]3gqϖ|"xsdoO,9؂ׇR%䃹Tҥ/8ޣ"ɜN1|m8;雒Ps]}-E*M~C)=ɑw2V)+> 8= s+D,[>`w骹̛Laǽ'^D 4̎Կ?K_!BlJ D4ݹ4vs5;حQ0EUsWZ^iľyuʨN*O쵹dX;驤{uEzz*\4Q HNL$5bL)9FGӺr;__pf?|&G}{?pj|OӦHzF>x}6o̧G}~cS07E#| ^G'[=~_.8Λ"Wo>-=#훹Wȸ匯wm[eMn#2m nܳ=#:вa*tzEtwdzne'pI_l8p~̣ܳ҈=3닐v1ie3ÅΝD!RPgVB蜡 O~} /]}!Hܛ9WуCq~^/{Avo"1z?E`Mz3~e'o¬IaPc=<vLևt&hlO|}h4K͎fSe^xz<÷&cEX; nWkZy=F~d9ÏHYȁ'pAެ-H^/YKo8ʉɺEQSDYáGUYލ6(L~osdd]P8Kr%(#98e 2a`Z-rg[;A+pdIK\Nv"J@֬JL~:i+ A ƑN;k+[! N vJSiLrd:kK@ncj)!$]厕? ]!v?+ ^1|@BƵѓFV {n𑕊ga,M?gӊ&g,Uk+f3bn6)}< :VbN0r)Zwn1Ŷm&%pWǔ0Pԋ1~- ww.?0!(r7IiG4G~5I_hr)!z+0>]^߰?S)H@O'#JS!t"RxO?6)9%X{.b܉k#5Ɍ&@W:|ҕ˔Y:aUz\DO%tc9]_U6EÚ~\JuP:`} Wh2 `Jk\[ZHjn>Ն܈ /\LKWftY uYKT' mBH~~~={h/0LLu1UF,A=8NvSa풹g'^^./(Nڋ - ՁAs!008"*˔s{eNVz>­ m1 .XQSr`ۑվM&o S;; GFtNW攸 ,՞;s !&kN-dd?j TrR!CG1+Z,+BZZd2d21172~V$T*eyyy/a~Bf4fI;KgևGPq` 戳c۠= #+]?j H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3-8piTXtXML:com.adobe.xmp Adobe Photoshop CC 2015 (Macintosh) 2016-06-10T20:48:38-07:00 2016-06-10T20:48:38-07:00 2016-06-10T20:48:38-07:00 image/png xmp.iid:32113ac1-598c-40b5-b19c-e82b3ee43213 xmp.did:32113ac1-598c-40b5-b19c-e82b3ee43213 xmp.did:32113ac1-598c-40b5-b19c-e82b3ee43213 created xmp.iid:32113ac1-598c-40b5-b19c-e82b3ee43213 2016-06-10T20:48:38-07:00 Adobe Photoshop CC 2015 (Macintosh) 3 sRGB IEC61966-2.1 1 1440000/10000 1440000/10000 2 1 784 515 q cHRMz%u0`:o_FSIDATx{|U myȳVZ-P >~uS"]kVvMVJ%R4]VTЈ*t;nWBTP-Sϴ3Lf$4I|?~0̜{﹯sC%&& ݔ'OA:N8q ݏaÆ߻  4@LT ,]tl*Q  !U  0!HO y(` (J%{AtɣW)BrqZ*\&-?~>hCRUGR Hd.=}㉓Ġ  J)w)49lr*8ڥ_1 LϥVL{jpLĠ'zvJ|]A0r_9ӪWą+A–k}3"703.%F.GA%4W4S*}cw{+5 UrgϞnAto7n\svm^4fzTtcf H8B4qzx?|_.z„Se mpM1u#?{tĩ{[9wgbUzdG;C$tp ֯#XknLmu)SL2e֭BSIh? i8R }F~o~c^Z~x ^ؠk#XK߭%=F *c;ւ HS7<`,Y//X X⛲h~> Vj8 .~S?꘲Ȫ ptsknzA3+W[{N^K_`$fVƊ; $"]f $?υ'S`=Lo,<$g>2н HW.c0I)-[s"^IvIvΝWM5u9vTDO]g)}=$L{XCkS=!QȎ(X +ajGgZ?|h-^M&a5̑{)3+LI_k=lsZvÔ5̖)[|vp 8m lv*g2w'mw w5uES=z" Tu& s l'ә%+~߸8'Ä HcDZbTn3$' r-n_rpVlݼvtW%m`r7Χ Rn=CNIWMX;q ں_D7}gMEM]U`9容/a:«-G?{ܧt^R Gzxĉ3v1(֫$.?~2?ܦKr'yE\;n]{3=c">\ɺD'iLXy)S<ܽk[p>WMxU޵+>l?ܳ>]^Uύu ۣsS Ɍ+/XA|,8z"E휻Y3eʢU=Hxn?+B|(kOYe.@EĮı6S"L([r 7QS.q1Q8q8^Hs|0L_$ Hwç't:e &6"~%<5Jo]Ȃ0O hs+kȤOFN]8agI5vԅ0wK'lUGN V:o±[)s.bm7r!:28|OXL.  [hʂtUǯk'\]|I'lӧ3T`wZ~LF&8322K=&qAh$#Oa"CDIa\5q |-qOgSy{&,t7容"a? w.I-c\5̖y|na Kwʨx|Ώ̣u~Ιgc[E75wԑ¹[5SY5q$cY>F~:U퇵kR3In|ťBrDc1n8؃+XQu:AGvvvPS"11ĉX?*A#x _3 P H8= =~̀DhF% xƴj"L6 HwƧC  =xh4.VhWWuD nq,Ɖnry=D9*JzQNQEn'D.^t듞L\|rVOUAYkaB$fS}O.LQ|FG|uyHZx7D׵ 7O|S:QtCc)׿Tu;fҫZK>LZ#mvL1Z٠ QU*-Ckidi4M=?;HrBWl LLɏGn#㗇6(I-'\9ht0QBCAD;Cۆ柸X4r=+ya>xnWD6鹾<oSR}|*9 sw9*iEM3*Yn D ANgOpw#I~61%2 sG0ݵd<+:HpߞSa0/W‘8I9y6_m-6٤VsF`Nk6SU0sltj,~ gF[59K觽`Ye0 FO)IK "9Z#Md&喺gKUhi3{lP|P`CznY`H535o:t%ֆ< Ne0s ׅiXKsVֿ0E<nYţ;yx0d`-aD#-uDEX($DyV̢L`-yת8]݇! {\ D79ĶA֪ЪJ^:)+vNs-AV}-.ӏ^L?aBzIKI,`ͷ̤4V]Rf%80BdsRQVyz?yʍ&L:FxJQZKYBeťl9uxJs|$O9DB)dZCQ,~¢J͖TQ4o), ym(?,\GCA`:))% 2y*+tLl,)LVn|I,Zkjn6e캓^ZJTs4&Т,Hi)@+R9~hT<(h,W#UFxzJrb)HZU̥j^H6` 29^umf,feV(0Iy+֡?VG~… WZ h0Y** L5f.`fKuK(шG Rbfayd9,X٘sԕM ,}JVpc,(+L6Z3ME8WNuedG$ҹ~t6i)Z2wcI$~j!!ɱȷ2Na}I\s: k̴9U3A09}׏L*TyL r.NxKY-{.w0=D0!`AG(g6j5Z2KS6jyR >V(܅a.%aJVc'Յ Hhj}k3} 0!濐D&?WԹl?;a(%X=L D|0  Hw&q5H:s3FAĝz\.B`)17˽  Hl&fgg%ޒSծ 5Hu&5Ir$Wlƒ . 33OLx 'Ge0ffr@:8 |jbk`-#kpe(e#A3j,Z9uo?$WTsAe! ey4fOe&56|iRhHU[B+Z'M@m.b]9T*9d#Ge0eL6=AJmNRHy]h@<*+]%kZM  oQ1SKP8w0Ϩ`YQhB kνWHyՒKI!/La*&ɴ+Ig=!fo-W(o&xH_;T|uU>?}$'٫Wʭ[jWSlZR%sEhlZ`i y -5(?`A=#an0Y Cͤ8ѡ 2&v | =LgFܷ OB7\wᷜz>PH8Lt}N `l+Pψo]~s(E)Uj7h(+Ld-"mC9r_@WGw d,v#Hr-{"2h$_іݏ֒T,t`QFI{zvt$?^gAId=c)XTsωյ%] %\iit^-!~$!~0hؚݦlB#ɯį玔 & k\pVUe"o=XpN&N\ eest%μܢI"<= dݗ:ך+jZBDiւ ek"H &E{6b9u8qŐ ey} f)"RXe6 32 Hc1eoB/֍eLc-W=-Yhʋz+qh0!A 2!SxO"?dujq&/+DgbLˉJLLC%GN\C;đsəY>]GgB&ŒC70 &i.nQS. ղf> %~1ZB:N0 >LR|]j|MR㌘Oq9꣇ &A:;_lQC\ng'D0!x' nתAl9H"&;=JsʅtZĸ'6BHY0r=8)?rqtIk4TXa;vFiZ4 fnS:燺h4k%G\>(w]ΖHfS4^3ڏ+?u&0k~D|3 IZ#]Cj:|N]N73'g t^]*\~HC (^c)XTs{յ%%Ge0A1hNݠ\iB G_9_n gA`A:TYVBM} [L Kk @bk4}Yr|ڪװ ZIm),j[$X%)E2,F_VWYhx+'%u8$Hk2 i\fgבHtinRfGi?r+Yr0N)*`0 /IbҧF(PHIv$玒Jv& A //Kr吪f"('inzu!f*4 -j8F F47 SU+1Jd]7~Jf@5)XXCe(J8SekKX'srKO@qw-倴䴗\,9{.9W]\j4:>=lX 5J΄3kyI\[m#If%; A ҽ=Ld[9e`O--\b)|z(|~htV*XmںV |l`UBTu=Q] i) 2|E6-ZTP6\=C!tIScY 5`2<۹L ǵ(7 =͖’,dZzƂ2%݂´k4]f6PY`),ɢ٧<6j[Js6ɥrՕm1EyVkBt|[5dI G@]ބC Hx{6Sbb" ar!a\ ÜDbm2'E4"9QȃE+p AĒCw+S9k5iL:0m})WYY)yd-gyA !KÄtZx,9AB@b  ݙ@Ä\2 HߒCAƒ#{eADzў#ⳅ&|~gv+`AAEhar#5`\pYGk|07g5rSPWQr$ry&ljTWt\W@UZ 5`d/: Dk)e =ƒrYP Rˡ%:TH|WWJnV%Q^FAĒB="B:=LNKWJs*K4&Q_'rFl+E$Ħ|"#'ZQ4{0ܬե;[hJ 馥ZKqo6G$HvQM 7?uX]! HB|Ԉ ݆ÄKrcbdvɗK|VtE-9AID4n`|)&冎tck> :&A+,W-鷾lA™aaK-Jsʅ`-#ǒCAr?GuT2twȱ4VZ_9+^k9/\%`"q~K`Q"2]}zB>\H8rOA=Z ]ePq#-PIw= NT<Ag0twO&NBߒbwutRr!ݲ.zQ~$:d?  zK]R+I|)M*\xΧã/*cvu/¹Ih?N~4 E*k"WDSF*H𣏕$]K +W^zԎ }m~gb0oWx7 c: )rϖ_  N:Xbm&_7{T6hƒĈ1 !_zG~&l) Hx9.rkh3=74G %6Ƭ60v$;x*4 {pIA5X{˼yhK[x%9[AI^=t0Q'O a?G0.\鑌/>͟FގX-ΛySd(6M=a7H7_oSn)٬k!G0M2B/³._m&ɟÄ HLvS'dB9҇Hu[й 9L$ H=G$`?  =~^ca? !x  aB AA zAA&AA/It| `=v{r7LңB3ŢN4mq  p  ä5 */WBI禎  HTHs Rbn`6j  HO#oY 5HuIPWיlZ#] t.w)MrzDSh!5PX. 33OLS`.$uҤqD%%AA.Aߒbڨ E)YFk4T:SƢ5S7MrfiL"B5,Ji:-Z#5ԕ:mZ -ɹj^ShcE뤩@VEP+ښ*B~Ǚk*9d#Ge0eK 2"A@g 70yR2si@E࿩+L: F.Pg-TeR++Y**Rٕʍ&N^K`dR[KrՕMr%cT6u8v.m0wR ; ZorKPXa|&U;E+$gMU5u%'̝&튊h4MI,MIefQCMتk/Yղƚ0u[umrj6@yyMJv*V櫭z 皒)) J৫ ['j+14POuRs4{v X7Pqwq,4]#"ԕm⃂w[J^ VXE~ #OR&".7KE-& J"4M`orM8%Bzȫ 쭖e3 rGeK.kؤFsdI2m)I>!.$ʣzwN}=*GnLՑU$q L(+7ͤ+q杍`NN.?QxTSr&Aڪ?Qڢ \( 鈇ɗs͒8R1ޜ(·{bH΄RuJҔFfc),2TRqwҲ\7!2uAe)kւ Hl&D`M$Y "I NHͫ۠c,҂ZqR fN]HנTl5kkڪ -ʂA jm꼜!ʼnXWQԝErk]J U5un/s˔B"C f_̴)Ķ*iAS Ib9wst|8i9eq6#)'TWǃ/7 ּ *(iQZA:o Ä\2 H];;  HXrh0! ҝA  ar{K=Jsʅ,A  "FM˅k9 0\AMkDo=\C4ZK쁒U/MWt:+!;,@1Q9˫0X/,~KAē$#F(:\gބ~MD#ۡhX>=LjOUX/QÄ H: u$D7wyh̝N׃Il Uw=L >"CKW #T(,Sj`{0T3 qF4yXPrt5$*_ q%4 D֋WÄ H{b.ɭ>]%6{O19{Q:gD9'}/ z*͉pEnƏ*˅z mDt~%g-^/aBL%%Dq w2IJ{Nקu`ZKYdZKڏ,UANHezl`,98!:ufZxr$}{+WOߪi\][/KdEb,9ksM ޣ`XoDFzպ7ą*{eӹN{FA\iiԮ2^D+C~F5ElIHȧ @^+."hg^6|r.W.K{؋½e~% { 11Qѯ[9jܴ#L}Bۤ3OCPev\<(ɏ7 f7!fBk0yV"0}$11q] ߻k]K,K3zÊB/vT/qTYVBM-n+ӳ&d K\vUheYZ6Ej^^*0j Wl&gTBm-pAk,VƔ"fKFїUh4|kF`3u54]YQv@ڏ l6mVfrRT`^zŤOiFQ 2rK=p.u%,qaA@x{|yKRj(Ζy5zޒrK\v Kf*@M9յu6LT*U|MyPRr5Tq&lhYkB`.Vl`(J٨X3;~TřM!K]OFER(c+BQdil`34&z)2,&@r,Ae1Y 5#24f*XFs-V] 5RYsZ"Y\ׄOAfB, &Uj2@0'&@k>r U]{?F:ufMYI 6_m`qsH\JlKi, t16~NHK|@4]qף ЖT?+5pcruQgm4qƔCxJ1e$ҠR+l),N\FAS=LmtUrg]^SEXfHKI]d3`*U+[u-ԕ5P2km+ZS/?N$}A[s϶23mNUff9ו2;Xi?d%!ϹXRC;PٴhSaCEJV~ Td$s\ E& %=L?R#MufGu]^g Ks(7 '$v-Y@zƂ2%/Z6Sq TT3C,dS\Ug-&ۏ ezRg6Șɢ<+y5! h@vV y7YҠ*quez] a"LJߒ*V$ÄtZƷ(gG+ oah *`=voaB Az( =%G"1 GW%G"q GΎ%GaX@ ,ǟXr H''}#=K#K:"yItEp@x ң@Qؔ\Fs )r}4Fē.ȃ H脓+L֎u ,MI%6w$窹 *в14D9=3Wv0  "{\h\pEl0 *Ş7LJ78S1wz% `#B)FimBT8d1iC\NrK e^CU]&CZj&g تk% al/sTdȻJ5^b$[E9rSQ*d.!#zDzAAo! x=L6h.AaB$co(7 #G_&lPyJ:7usmG&BQ,9Mh=b"]%5uezd;'(i Nq*9ZWhv ~ϵ+ zE"=[e0&qTh 5!jVN&]J\ VhX%1 233&K'Ge0ffr@:Qi5gjJSzA\GAA# '}Cbb"gHDk[ZaINe0- fk4wHW@e0s7DfVe0%|U.'BIx(gLGaHbbٿw,Y"ttm=A%Q$ 'nZJRf.Mp?+ 7ue2I1H|ur@UM]*5ˑ2/Y*7l: (֔lUZJ21?rkr)[2I\~lյ ۡ Яͩ,נAL@{A#{zYnRsg!HmA\=LၥP2-t6& C&IWBk8\&zB-cD 34Jxaۆ9BAB0vv,9E'NUfT0=A$ H,9KF/Vזh4& f:NA~HGA< Qk!:Hm%yWe0$gfBYYmnn8LgvnQacG5u_ 7_kԯj - [ Hgo񯜩 "b*XFs޾j,2ҔӷXrڪXAŐ ey} f)"RXe6 32U"WfJgIJabJJ.s? X7 *K\[pRbkAIgm+')8q*TVX 5ZnŏlK jG"ǑJ![g+,6i: ÕtsLVsFJТ,ZA:{&Dƛs+F8q[.Yi88?`6juҍi#SmU‰ t!0I#nΓ'7'¢,N\ЦCq8%ۜxeƲW[[!V%- [ H:`9LH7ǒC!$\AAnLhaBAdsIAAç8ڜdI0aeAn9vv,9AAp0! x!(09OTtRr!ZFAKO惨uq,y8`w-"dC% xh Ip"q9Bĵܙ4ӱ J,99뗢 !r'hem4T%Vk'ǒCf:Q>R Aău"4GKxEOKA~Iѳ#uA A &2trzua<$|eY ,L@閠 AN YÅ'|>Y\\ޒ!!3 fkېݢ=\He:§\ƒr!X~:$ۏ TZAkbkgǒCiiѼ2ٙV:ᇺh4k=KS{@=1wpNyED~= =Fޏ HXr0ith -5y5zFK|Kg4r$v|,TY&P}&]ބ9LH;)JppĥLAƝ%I&`tYL $$W4]'_JA2k&`ׂh`.5j-U+&L3چ-}V2c]1] g &Ļ_ "3F) lT 'ɹj&R_Qt75@Fl9}>E.9fJg%rF:&5ٜW]J* 2AORtz=L A5yL6QLT`0XR2 }I)iH`fMY'+\A4AaB cbUʐ'jc,҈xtGI"vo9"t A+S9k5iL:0m})WYY)y$gyA !܌0a0eaHbbٿw,Y"8D#; *8r Az4J@{o!?: =ar.!|IAzhm91 ZF+ÄtoHBM>+J;rA`s\h\pWK+|1<Bdknw' X 5R<^ &]yAN=Le5Z-ԔB1=Vnu6;Ch6~2, Y˒`tEk,č)E2sly`)`,u.xHjtov5tAfRnGe+GNR9.oIe,mv+';Gps֕Z.wA˒\u9jK lbrK4Slt*ࢃl5ҥI^*_2?4Zlrנ72qmI5F_lX 5J$5l&gPy>rTr3ZYdUs|:f1htk˙`By&Uj2@0ggJYLak/ӛO,jv̂J'M2T E^5H05HmPh c@;`?9>YL,&W. ESä(q|PoUɄ =R#~yvg) aƒryOGcWVYe<A?0h Nmܤ kAXrHڔsA.`,9A `,9 {eA}|Xr  I  ]ÔYkH&AoEQeAroHM19+g{уQHίүxп\U_yov QNUOK>LaB M7~֒(E ^!iO `ވz|KA[$u+k̥sų|돞 YȎX=_Ke?LrP֒gES^.iIq7xYiZd"z( }rU Sϛ,K6߻KngK4mZ\:r =`B{W.L`ۜ$'.!`BEKA7AӀÄ  0! S{x{wy}=L k &AAzͤZB AAj3)`BALh0!  4KpBWALZ#mvL^Dh6B@Erh4,iڽ6GzI~6ZK Au`Ϋ,g)ԧ͆*&{`.MM!!E9_kr}5S72Us~( Hd9rzX|Be0"@Ae3Z %A,]4P6_m-٤VM]^gh,/ ,=6S'Ae0lԈΫfܚţ3.t3$yVO)IK "9΄2\a`OĪ̺2ΔJP`Czti+,^VFAo'ҪUP- R w\`f%[7ͥ"U5 *\TsQdsmյY2*[ eާQi# *J\ט99WmufڠO)JK&R Ie9,Lب/+UK4+ׅͤӘ8cXB~T FlJ! .5ΓLʭ%Ӫ7AxIKI̥ @C*tPUSc>KNZsd>Ql0t *7liRRx *TVX 5Znu0q]|\s 4MӴ`bY{)" Cf0! & JLLD- Hw &AL({jj:LL(a(BA$?5VbywqLE=_AAzEEFFFFF ˖-cH+..nmmu8&AAz8=++رc /iiirté G  7ono)Ih3!ቴ|T&`fbV+A;3"!fܸq(8p'{G|(@]q 7ޫNmi;UD&roiT^-[;6`=hokCCMQHk(p0g&5岄ʡ%:6k Ad?PP__/[yoಪ Ы@s󥆆WG5ʸSşHi z"G7xxۋ~]p >.wXR&#f_B$X1:j*a(TCEFBd{=""*"zg^'q?osjJo~Sb]/r{ 򩗲w'&%Ok1q7c CnL:לv݄ PF@2|p`=Lqg86}0DmԐ лĦ31M̑ĨCq}z]8tH)u3TgO{c_OC}ח\LHp']lIlb"aq@d$۽ӫlQt{J38SM p8$p)"wG=E`~wf`x c"ZN*xm&}8s_#01'',<ו@3,XƜm~ihX?UuZK0ۯ_*CM<:CCD3}]vf/'?ԇnJ 0~}j>=4lVIG"##"#)PTP DF@pD8" DRd`l~|_ea(H0GDD뺃hЫb!̙oz0~=Oi/nud7DF0 ﷾Ō]>_pӕ۾0Inzwyƙo8mkѮ1soÎEQ2D}]BB< 'IHHwG+AqSsg+`>+o0k,10DPq`bV^$ߴ9"ک(S@PNP@ED@4@+6yd/>YOEڏwq3poXhߵ3$pw>n̊~goHyy䩻۾{{gGιO~ ׿7J=Zuo _~fFf?zߴQb1{]͠{dd_7tSG0@59v\S0po nČ'0 Fw~ ~ib0|5 z+~0ιoz(A~ٶqg_w>rm_mM #ӌo_/`Ҹ!*aF\5<2UV0S0$52zȌj`]Џ.]/!5ޱCb6/}syb!;L~矎BR"kE{E0P(<˜>"53r8"^'dxh!`9r&^b<VGFimoTDShkG0pDB4pӌ[̸7äE/}d5[u+.|%atv8N qgaoFW'_ȕS7p/6SMʹ[nhl߀uEHW~o~e w\EE _}b{:8!گ{ZVxF_WZ6k_/1sXʈE/J裺f$'/46rOo<`{vO#R)zgq $˕?ŏnaQ K?a=g~??^/|%D ]+Ggˢ:jw_~k;הp@Y_oɴ~7=:3 Ի*0L=0cwf8y5{Fܿdu#.vqsG0 %hZǧp4@:LÁ إ"{K D2 \~㴗nxnW(c/c017~F_$=Kn_ٙXSg3;_>e#/cW1 0QTS>?/U_m/n4=|lmwރGּ=ܛ"~Dm{KG1co(Hq0im^*"(D녳9n/l?SW/ "(d*(qMrN3.\(a¯nl}{0|Ghks0P EA4@A%GA""=nw0̖=;sl?x+ 7ig;b >o<^ p]d3 43ŋ N Ӌ-1eq49\7Eps5{Qg0aVlnz…_<ۻWL ^ C3z# #faϟa z8xȿc sC m901LelM3|#îI0LGS1P}aJK:z_?U=]{(H{Cy/Kr0`cok|2X|\{p%p&%mo6}CP{ʜښuN@`M galۈ9LPgFyhh޺|c|\B^a\ۨFAo3v?@E Qp@D3 Q *r@ 5!ֿl??wv{%pSprp0O{,@TpDR\&bks|M#>Į306ps7aΛ0{{a_ߍV t5:楷V};?sht\mׯY)ܡ3H;XW-{ `w v@&w7_x2~GR= `/u kL/t8h=V;b_~S)?ȍqy/x\/!$Pp!Ow=meCuEQSۯq}c OD=◯KOh }\x1ItttDDD{{;PZCA zA< ôG8|ܹs)Zf[K8wYL+_lZ~$/6wQÑU{4:~M'3vaugvps?_ڴR`;1'g`m޼䇼}G <S/1/ Ҷ+p0B g֬Y#Aaڴi'Orrr㎩SJY)Y_twg3/xjWI|u4zlO;xԻCg/Tm " {|ziǫ?OWq9Z񓛆\{rV 7T/1upj{i1*PsG̟nAQ|ffcQI/4/yЬM%7Z,4c:,ڄ Havs74xㅼLZKj?՟>@N^xͯr޶9`5u~|Eo~ѵFENJ yѵ_ԟ?]՛7,zӧ뫫k6s?G5,S"x8E3"2>5y##fqÎG͙)+Z> 3o(4N*z{k#c#'@i~"G$yy\6BԨ 4G%q]׽V=:PTl)VqĢ^}:>?RZMtԸ'Ro>z܎Q6l,LyԸ [zÎ7l=nQN?ӹ#".wocD_^W_ǿ̎95o=yU2?mJeVVWןh|^:z~y%xųtzv((z렾$)~g Bɋ>ŋ9{U ˖-[o_رc۶m[nUh0Mzx][(Я{n۷% r\v#Sw 7<޶nm`_W]7mrނך(lX2-!Çy'7'^t^ g˶M-HNM>czpCOzj~4ˆz]+^I#M-?/oj;I )vZs=tYލgK?ugN:wK~o/F]5hFW?vgƂ3;D#nE_6,fpOhJeo _};i^c)s&8kDং{Sp 'C3&34;9iS/'a`Qm_ˢ>U`"(o%gG7|TgR]D[G+I?tغ#&^yXW .G#E1Ś҇ܮs^֍8şs-s|ez1ӳW੿n~v؁ 6[1395c{ 3e+({m9*Jq۟n?aҾw| $ouθVwqlqӶTqwk> ^ZkmZvǿb7osDZ3gK:#o~~Ňԙcg-1mz鞽hvt@D/uNS_tQͻ6nbe nUK@˅W}tJTUG[gc)u;OQ8K,~`_ύTJsKLۥXa)cT̀cUqS\L$s7}ؿW$7^2I@E8Ԯֈݞ'>yۙsM羸t:G!׬Mҷ cM-su{SkM2f潆009[fBa˄柞W9s优7hٜR+}.]P}h˺N|WX_'^.{/2`Fa=oy,{5J>JgF,\:9w-ކC HCôpX>q-gC~{Pͥj~ɎiMwmqǴ֯6~k?#/mh<ǶCgPGw/56[͹1_E<>wg?4a/6765^jt=h&*e-F2_$%mpSxS,6y~'KN1XOF)i0+&i8E2^GO>bǦ&DS^lk9E]NyQ9/v2C^?%>uKK?ԥ~.Kq&,@e<1cL?g잖_]iHrޕq݈;3#$O` >Zq AsY~=0ғxZo?M3i޺ͦ3=5dDҞҹփn6boZ<"o?woq;VGܘ7mރj͟6@Sl5G=:ϙ %<~7~?܎?>r{_}wO7탹 4Wl,kmlٱW߈O?Rs! 9$;AqC<~GXp-9ߔVߟ<15A{'?pË.nW~wSc(hhwW91jh_7[jbz~]gR#9u=_LO;hu+ «皦%^QͧcRƺ3YW ~t̓hǹ긫QmP̥kd@$;ًj됩+鸨{RcР^]Z׿h;ÛaZ<2ʔ#`pmO5=sԑ1 z x5{Z!j׭?|Dž{7}{7 uh!Jlj72H  v6 r\{{n(p0 p8Xr---`ܹ/cgUf22rt>H=յ;H1ynN7]-3].1}AOIc\PtkWGڀSi 7]={#Knu#}1r#t17>>l12-OˆϺؾ##}12ͽV|^TOyk 9|N#{̘Iӵdddxoie2< <<_N>udABƼy%O_~… gΜlW[[{_xuuu.OzzѣG~Ǵ?p_YS?{msYHݨW_:>{- ;$ڻs?`&ֱԫW蘘ajjj&1B Az.j2uQ ɓ'[ZZZ%v?x0! tKEI_f}_xq w7  ƒ  L  h0!   ҙDyҥK]` H# ]}[rBa06ťKzlAgs$    L  h0!     L  h0! Vs3|zJxќnXI/Z7gVA9s=.%E<3^7G7y ="zçzO0L%UrOgh|VϚSZ)zA>2Οv}Ɨr){cm;ޙGM҂WvrM9!be ~w:58eT'+rWRAgVT%4Ɂ"sQp[‚v%A?GL aֿ)sǍY -*,6Sy sօ{fT_SJ?Ўc=BwYk,sbqc>j B(drkCpyɃf!*zMrPCO8Upsq@ݳ yVs=;nhoe%tnO {\˼S}wz){{S=d5FZ_ВhxtO9>x7:zfYM&=w%zD1.X_|~/]3@goVouK?Y:O|[]{n޲OCaBäl߼(!ZuuO[+SzyUg?l=w;"&-rsg#nzi},iDQ驍1pGF|YoxH?Y{~Wf'U;'q|Nݪ4?^GC 랛f=i[~0^LU^ۙҮ$5$y%6:Gn?aҾwP>}^_X|]lPˎrng?I01Q|;;h3K' f^S?#. eg[y$qqNGd[S9Z^^ˢVGUb#IOkJniYzfc4(ˏHԓ<`F1\ֻL+a젌)~ςҮF6K۹m=tlʴ6\e3nTޞ2 p *]i G- LuCAKq,D0,^_e{yAoiYFځT7e/-b/gcgc,(~ӸU+}޸kuyKyG9>. gճߞ9^lie"(#J} a]k&aqxl(d+']L/|M!q}^P^퇶C\r-n7#7m?8kY:bi=~oqz6r]SreFQX\59,)x΋y)u \IW]%S\ԼzE)7;zQy+x6e~lX&nL-jgIK834` onwT' Vq',VgKJ1խ]k]L^9kSv;.t#Oe!EKꏛ rIϭWQeݥӒ gn0(<~ŷڴ!0-"THϔ]<&H@}A퇶^j0.?86lIk)#Z+c~~pÍllsʱǖI/=L&ڭddP9qV&US7+&*I}1-2?nl+gy1o q3p _E3%s$jcB/}=vWIeinljjlf?7545[9i&:(\Irsh틷11Tp{K+eL| :01JSPdRc 7:[O޹~#ا.aPNgQ^Z?Ϟ嫏CoL≗ -ǒ& T ;MNF^11^i%&aO׀vS,z\ .w1ﰸJ ’|PJ$dQݷ45764m }/skupHT8|}~}ܟJ!V%uOt;Zz 0v(Z(Sҗ%# APc_槝7kPPQ3{b[[{(jnljr/In>{T%m-Ϗ'Mp9͎(jw*ETl/OiԌ}JOG·(ԝew]阙+l?`~,S2㡰b]iLd|ٛKy<#!Mq Ͱoo>sĺmo/Si\˯e_L٣~P1Qv*?P?c7y"wԅF5R r͍9 T ֓b(ۯԽL~F\vQL Oteqʕ+M'!5PQ1z1MG+Tl?.m ޗ);C㸖~QV#Ҷ[!-)kNL uOc CWE䫖atHjnyVDޭlȌ~'uD3_\{LilѲs0B:ْzGgc9 YƄPI-#( =(g<<7z\ocxaoPY]]]Ӷ%9ppeǮ977pU@k纺qMD;;{|z0-[ \ol6v-@ dK0m=},ӶЋ3e霱.y|t;www&r0{ Z;))}B+V3wH5&.L#3w,ҳׅ`L`[ۘ;SChRvw{B=3%zǽ-Gcx.zN7Z#g+C=}qt0gJm~}CFNסw&/kw əMw{ht?N'ȑ<5~N7cB띰.γp>zzZIɤKy/e(c#UPҌ}32}bAeԽhUu`BPR;ԕz=oQc1(m^Zzr| [-):P󗒑6'Olnnniii`v;?do?ۼvj!֟{odNg9@߽۱&zC>` tU 2rf=^T藍ٹ~ŋô ǵI4n0uu#  &  g`rEO`0+P̚<&Rϝ[H`Բtx(-9.R郁ջ\Spu|YkxaU;_f?YE8 ѰSZY -혀"=<ׅǤ:~|Qڙ8joP?E!e'3nw%䙏NJ9z!0]eOamRӱjW:ͽGks^iE6tVӇkjOhT?\)vEʩ /N (a81q>g G En|F赟6D=W/]" -W ^QX46NbkYZ6TS%zvs鄹`Ol~ᙷ9=0 &?ǬE_uF՞E#:#'J E8E_-QG3Aq!Qkbwa9#-?0uNre"@l>CCp'-˄yVEùծ8伞grz(]cWыtso+ʡSω \K޶[f8_mxds̬6m _тC~z1^?|KBZ_= ]%eTZݜ KE?46rkQ?Dxε(Q-OGZB8VV:(in2߀¤}Xt2W!|@@9| 7]i(/]6Rhvi).g^[_Jnieٳ尥iu:ZLw>fକ9@yMXM7\<4X JF-L|I)]lVe}gmU Ц,}-.Ųo3gKpؿ땥;V?%#G3/ϟ0EV9sJzv'MZ̆ni/֗yTBSz^>FM\OqC`<A{K sH+M:g+6;EQTO~ ij Z 9>M&g]H5?2m|L\ _&>&x<x'Eջ{{l|v3sEXQh{jaܣ=7#46566;GvFG8'vWoj4ă[Ca vGK%0eJi}9;%`ܵeDFV3LB uqG߉a[ccZA;1ˢWóKN$1؛r-"W {ǴR\{V|/S0 _DCĖ t+JDN2^آQʵ AE`)?ι-/aQ;Bm$aagIDeoikuW]i|NQ%>C_z.0kwD0TQ& =LP= l/^=2|gF˃W #oo,_]~ܢͧklaq)r{`а$Gv+??E\xB{T4"o"ֶׁ>q?82E]ʶRR18nΝz (u!g@A6RZ m݋3O}PmO=zm Wݝ:w[{f~6b#~qnĨ']f,qG~p֗h}=HB8&0 ޽ymdֲí1Ǎ$9j=@Y?9nh YorW}&*{,dWNjs(-Ï#$47^v̓G?D,;Oi^}fgl=/Fy.7&U[zl2#C=^Q"qT;}Q*sI"ƺd[D=e SҦc|/*"IG>}@TF]?Jcw&jc&>ˮ 9u>,D9ǍϘNtKAƫt3& i qٕJxBg 潷%{t5A::c,LaMcJEsPw2 ('SLa;oE,w @`@sGNL `M  ^@[1b?C;Q˴F'=t9M=un=s JKЗz=viއaXh0u/`ᲅwj?_N߻X]ݗo. :X1u#1g-\w|I>b5 `o"jLN^vEm#(N6_%`6/-E@<:iuؖM9f˕kuj=^EpڡwpS)2:-;V\mε rI㴓훕߂U[z~#{sȕj7IT~bQۆ@c}[oWf'EtzY @ K?2p0ϳ,yxqz@CPL ld@}wk~ ژ|C|~vcv2 1;og-אZ-06 搸QӻU24j4n0H/q n,Ywg+ElE7|{z"̓Y1.6÷ yc{3gC6&.r^.X1c4{ l ]z"_'R,)6'1ԛ q̯_Ξ,TixUB)o,J&-,3 $Y__%9OnK'n[F^s@Tb9g~:A0 ;wYpP mg&lS/]BjG(ѪhD4n,P+l`[\)c8/`@QR1 ~)B4~Ӎ>b.bG'W_ο86֗{Ǒ*~[w]z3ϔۊVϚmQ5ǨVKIL3/CO{g|7/<'q4oxQt>E)C Q)$p+<GY+M҈鄨RBZOt 6%^+5=s~3c/WDzNl3 O{h(c֯6Wq}5KzݴCs彟k>{K;<7g4WKni|jBLoi}cOR0X_OYmj?K]8u@AVi754JD3u+#;Dڡ\b gsgψfXf󅯵Krct{wrlpc"o:QΩbY``gka%V /JohZ!oa~̉Hqs<$ˀ^i( bh3L+61j;5()acFv!-Ҷ!ߎqȅ]_.mJj6f >{͵X~o-'C3N(-pfh`޶Y( %Gioڶny@T3 0.uo1k70Dsԯe)M\[-J sC3\bi$0 s~Y󗞙s3AZlYB-9 c%{g]`4-OM kn1W5/ a_<|- !\n8Ir𳪮A ssR7f^lyAzk7w vzXe%sGl]|?6MY>SH͗`IGV}@+;=ؼ;)c4p[o/]d3\+Vsrln#ۡ1zYl7.2Bg0_TZiA65U9}\#Lsa!^Uv?C/g=%?k$?HE{[p9s@8ĒUkmGF^^44cL.1)nqPQIv\کyf N#iVO,I^9r$ gWF.3g>uδ:0QQpFIs 2'˔+Zdz ?WdWܥ]&T_<ƐW&ָ(bסԍw 'v]v{=sCjglU^krmqɝ`8w JHt\vU-WJ<µo=᪸sF.'/ kJŵ#T=}V8a/ Pwx7wڎ(.cb2g3Rx*QveߊXngP[>Mth <С풣ޕO/ol+tXs9?,7cDG=;hܺWގ달(nKGɷ`wѝN0sbIL\/Poŭ(U.8ljI>pJ !,,6u^E3qE=+YJn/ <"<~+{$7Gd~/g>0jgOkoPd L0j=\L<׽Qzg8/xHrUq$֖p߄5ϊ};#]{eHxQWHB~HjXІ]5/VW2exAA~\EqtBfoپcSI8e49j?މ,s;ɡ=˱uܟ3wQO@/쑼oùf@^5>}˺fߛ~(8)MLȥ1LuQ3{*}᫐1?SG`/whi#\2O5G07gͳ^,?O7Fs+ƚmf' oNf6 #e>8`/{L"_?lZ%mgJ)&mߕN.PO;KI2"|Gy]iJ4XAqi@mq1 @z}E8=89ֽcW*l?7iq.*arjUէV:?{+1*I9TcN%I'lRe՘=z쪷}NQ9Qި ^¢5 9wlxyJ%M?S4(p哣KpetX& NJ4G.mR[XM9{v-\L/L\9:|ёs6;55O V P@3܈Zv@4˄gX<^. 46GȘ7ZHw-_2^~(ͤ fCn C=e ʶS%Uf+ rTsz<˽5.DIR-_1qž["m[fji~(6BL^6o3$o|7QέjrW&&%abN&MuX[o}?4"t_ԭ;puMCXb Dra;Ÿ@C#`N眭nr>ݝ9[. Gmsu:u1Nk׶(TKXU(x <Jfk|fk\jݟD߻uzWϼ6ۋ4!ym*Zsn0Tlzfgc^g3;< guu4kqz|~>?7$m;2s0M]Yr9pLP?~A=*u&1aɳyO$/8Oab9s6SSxK{rNw,&̚Lsq#@h '.« Цnً۫XN>uKGr(0[Wc 7l|8\gkmVH'^<f'?&Ii$'< {KN f U3_һoۻ~PZek {~zo\ }ʻsȭރKG4u̟|޸]-9wdI%05'TmUWk>Yuy[?2͜Nj~NMHIop9.Cט #֞&L½iGG==r7nkO^e]ML`~6hSǾ\0 AAe_FAq.:9'&Nֆ(ĞEAp*笶oAsĂə ַұQ<&~ b" KGvRɂ.D@GFQ\oT#ACӕ444B uww0 }:z_|'݇6Tlbٞ84[-քjZ5;iU|?НD o/|{Auoi,;ۘlcaC8Sg' hכL&T__yV mSl(b6?;#F+Ͷ۳ɹz'YkOү u@ mϋN#}h8|Ga6ZhW[#p Y97t- AxJKKFOR<Lm 4Mqzw0i_w6~Nj[X{Ez&i}7o^5PGdS)eoHce?Wy.!)OMsMH3yZy^-== ir?\с Lӭg1K~X?T\p8?5F| $FSaۧR9 _9g9/RnR9cY uz[4;R?cb{uMf}`ߘmΆ/~XT ߅dre#aj3VtLI}| @-\ ,w =9mp8 3[9߃r'Fq $421QXc ȝ.ڌ>ǵ_,KPj{߱cɠEmqMJ@0ɷh}M9|n;o+${};mp[o%/CADP]O*Hkһ{c!H\qdVbw1A(Ǹ?cxjY0O#߳# W BAQEQyyg#sJ%jGr  N  .AAp   &AA\0!  AAʼnKJDQ0D wHIcm#`_nDA|$   &AA\0!  AAL  `BAc ;Sl޼ӂ -&AA'8ä|#ydvWM9m/?!J@]|썔4qftyկcH ZMfV~A|.׫rX7~Vݦ8lZ7*M6u\ӵns *O74706)uT#g _k8DApaIV^9.q/blӑ/$_^\^WWnBd5͍gΗUX|[SnŁP<<І=|:˫oiC{|F<4{ .0bׇrw.2r~/wѾ|sg7hj+/.7f'3yȼϾsL " rgcjG 3ϙ3mYsFS{)YN4T͟ _PD`GUϵi]'Ws0Y{ Fڨ'w1$׋ӵgުҪҥyWB~2g} ǾD9qsd A+L_¹uՒrےyJ$ŹkC%?5=2_:1G y ~  ŴIOoƜ P7}Pv|xSGjMS3<᳏X[*KR{7C;Q!D|q] 5qXrjǟ}m:.aSMDiV1<_'6i{buʸ2ܐ9uR<,TKy =g`%M߻4=3|dTY" fL␤/FDJ/eqx'[%.fnP̴G@oA_^- ~R) - !(N_<@=dZtk?@Q4~V @I坲|zrP;5uF ~yp" w>Grӗdpנn }%=F- i#v=65~XuǹS7f9uԠ]9; _4ٞA]B_:m.snHXXנ7P"ث0*oi=[pW1/=V;/qӌ{Dtw Y]Y"P׮~/ ^ Χ4HY"WHNRzL4_ڝoUWIKdZ-Ћru . *` jI^ptLn3rgBiL)ԙ~@z/?e*ֲ^+..Uh㺆2z$]{GbK*-Me{/տE_i_26 &&}Y &/ݪJ|LjԸ',h]9I7FJƯ6|E$֩jtҝȪ,`0ad%5%o}C[hOGrfh gFV{_%.-7SguZü`gP6}ԨqKX+i*$qPRD!nM+ NTڿǧ<)S @S H\.5gWj4L7yKX([) WhK*>?J1y{sUwASz\}s.U)\/.\M-4@֖ZՋ/Eۺ$eC8`V?犺~5jMi͆fyU ey7XEܤ2֚"'2[7Fic喲º5G{~5ʱƒ=w;~P<TV+/HTs^Zigm`2x.Vv&3?y#oA`h؟۬`0cٯ+<$y}A c 4~pmmӃwV4\|KGm9uVMYK >+ӕb7տrЕh'= 4|RFSK-D;˪ |?gHBפV@#s}d) k{D&tr}׫޴P)i15Yd=EA; z]XX{Ec%5yb W%<$-# ?̤}Mp<v4}ع;/6'\Ȳ{T P؈K {?=ʀMnqn{ {BZWTgnp!ޭuҼP_-+ FGGKJ5fV_sw{v>!Tz5RhNTK! s:Ѿ24o(}tj߿\Nj"OGzY;&~9mFAA: BAQEQyy hZTZw`iՔ䵫9 OPYE%}sRsCCI$5'>%n!O?qe~"uKd8kp@S_$\0! a/L&ނGy A z_\YL "<<`c֐;\! ݻjh"w{ X+'AA9<== +D"<==W\b^EFbߏBAwL8`}L&L  ?}+88X"`Ats9  K)q -;IENDB`terminaltables-3.1.0/docs/favicon.ico000066400000000000000000000021761300101162000175600ustar00rootroot00000000000000 h(    :dT^Q2,)gikX``nfkWVV510E33Ui* SSSZZ[VIC/Yw4?D+6:K]iRes5/+SSS\[X?A> A0FJYb7ADxz+'$SSS]ZT7:> )kEe*++SSS\YR;@I2sl[s024SSS[XTDEF?fHc9Xu$ SSSUUVRK<2Go%O'};f{2=Dqc},++SSSRRS]YQCEG?+Mf{Wkw'# SSSSSSUVVC=6Q[ndz,$SSSSSSRRSONNPJ?555PMKSSSSSSSSSUVVHFDC@>t412HCAYZ[SSSSSSSSSSSSWWXKIH:54[[[svvmrtVWU9652,*RPNYYZRRR~UWl@aȼJwY Bterminaltables-3.1.0/docs/githubtable.png000066400000000000000000001163471300101162000204450ustar00rootroot00000000000000PNG  IHDRL pHYs   iCCPPhotoshop ICC profilexڭgTSBKЛ JFH(cHP "RPD2ĂmPl'Ƞ*Gxz9s @ @H* e'$&r@&DFL9pˊ+ U~j~/9%RL ˤb)V LIBbv̔I LIL6 @qt{`ҥ4&؈feg/Yʿ3E+x.@䈅ll eH4f- U(en S! b^_S,ˊbdz@ʉbɒ(H87LQpjN@ 9S?ŹS:OKdQi@sgI3b3$(S(V^,UxA ='7ZW*QܐiH06Ă8-A84uoxD!eT6GijŶuHHLbOwE,WnZKhh5L;@e`&!؁7@D@ $"AdA>b(-aA##',\pCQ:@4=D @($IF"CuH)RT#&r!dy|A12Q>h(.DѥhZnB+Z چEwP9b,L\0?,K0 +*Zz[{}p gsbqF-Wji_ Ty-=;{VgJy~L~AAAcCaaaᨑQQQcq&&&MMM5L9yͦf^fKjn]̳w߰@--2,j,[N]}\gf귢YXZ5[ Xì ۭ_64{mm6mڪنvھݶگ`#1qc7'g'SӈsN~KFKxW_5']?9Iݎn~}x9 zxp=y=ٞɞ{=^^\Zކ|z>>>}^J|~s[T< 4Ll r Z 8MU!CiѡաO,$ahxHGsG@'b[Hȥ#̋W3YmT~TO4#zq11cƚb5}/'NXp5Q+QؑDLKO0 ]hpˋ Zh2>9>@Wn;Iٙ2{#e<҆=ҷdxeTd o23d~̊jȚ [I'Dj,%K/[nK/Js9RT,&3 ̭,nE˯XaWVVvXjjdu5k 4Z[MaYu:t XXRܿ}q? ~`j~ɕRҊүydSO6nvڼ{ ahݭ^[Tok+g߾x =;(;d;aUFU[VgTߩiݩsΏnݲGgO/{{ VkR[QG˭{?n.?7k՗k57975>m5\p!C-V-ZY_{$HQ-nj<8^҆hmhw$v9yW_N꟬9~jigΌu^M?;ؽṄs;{!¥qe'\it9vש7:u-[osn_3Nػ  ÂGG%UW<~Rr'kO> #珯CE*=o>98rC//_f׵фѡ77o7|}Xؓ?|sϗ/Ǘ}%~f{Gb iioɎ d '{48uuT({Dzxjox59iv^v|Ļx9o@ ޕvAGG:Da 8:ciTXtXML:com.adobe.xmp 2016-06-11T22:39:44-07:00 2016-06-11T22:50:45-07:00 2016-06-11T22:50:45-07:00 Adobe Photoshop CC 2015 (Macintosh) image/png 3 Display xmp.iid:92c72340-cd77-4426-905a-bb7b37058915 adobe:docid:photoshop:91dc6525-70ee-1179-ac3d-f90aca98f8a3 xmp.did:a20b9801-e8da-4a55-9718-601d6c7687a8 saved xmp.iid:a20b9801-e8da-4a55-9718-601d6c7687a8 2016-06-11T22:40:39-07:00 Adobe Photoshop CC 2015 (Macintosh) / saved xmp.iid:92c72340-cd77-4426-905a-bb7b37058915 2016-06-11T22:50:45-07:00 Adobe Photoshop CC 2015 (Macintosh) / 1 720000/10000 720000/10000 2 65535 501 144 Q cHRMn'sxp1\'%W5IDATxy\T30"aj[`bZ)-f eYR)[e+-ˍ\ Y" n";1@QA}s{}9QD"H$T*{9{1t:M6ёH$Fll,XbB*@T*c41e+H$D"[T*찳cƌL6 !9: #%H$I=B믳|r#=ZE *B J6j5*;#ZJ700 ߔ7'~ÃMeW>P˻S3tmCJeSi;3=gk LڕH}:t75`f8xZfٻ7j)'mDz1[ӭWZiEEr{U;)r%Μ޻ ʾnW9;Ӝ\ת챋TđpǣhjYP7pNj Gu챧9p M̾YL:;*{&'{@NmV;Pb @ra ?wԘ/XT2Ї[^ hѓ^ ѓd0LL5Cmk:@`00j/Ap=b`/W9~~{ Q@OM+i%'}Fj9_L#ѧ:Mx{ʡwo8Z)[*P QѧlK=z$ 0 4 qOÅ_oM{N.%thOaC7v$Ubg j1S v2fajmN +/Ro- _:nadR`NcP#d2._#{00ըհ|e[ Ğxk;c p6=8fx_ũH=~0N,)%ѦGya\Ξ=8;;_J̩8;;gbOԌ?qvv|MlT6'fgg2`܆sYBV_l͵4q41rU,X_4<O(J_qG4VGɱrv?@X~ޘ4%{#<9jwC*+,18ϊYXK6.XAj}A>~1zQ:x8X@bv,Wms3aCCK)!ʒzf?,r$x 1p6>oo>w4sI8|'"o4<̽،r'Ó^.MloA;7-'s {>X$zU<9Vd쭵_g>(+>>9 n.Cdjps1Ǡ|㥵ܨi=hr KU??ʧiz$Bh~ {"{?FM/oBXzvtĔUȫ{,厎HoHɄ%ykUue^-PFvܻV<v?<g1wO k8a678uf%ؙ $DIZL $%>}9~A| Įf5=8+RhRhd2p1oߓQB(NbDl[$ϝFvܯ1s`2Yz됱}ۦv %(_-b|ϟ팩̶ ͍ӱ6l<>Κ, _EfxFݼfw 4^jM&Be<9j zs?*b1%`rb·1̛1UnѦ=cbq8m3Zu{7}h4rd2a(lL&JÚ`Ҩ< bxiLپt<)2s|f][5΅M›]UW0dVmVD6ە UYYTv T$ng@\><ݠ7M=)E3o0Tz =1?d la_ ЃaCQsu vachf)1 ZЩcOg)mAN|6I3b4] d]x"g -.ٹևO7^9o9?l "8A=s\ 4)\4aWcl4qnN#rTٔt%Fh탛ZP!vWF\ YK_}%=1eЩ!⃴xfk4'eRѯώ͛de<ߐA}4M,a*ԁjlTEt!."Иͷ(|lRngl:7*jgڑ-msް+c8g3 MsO~M}#'(J&|疺+߾1˃UU]nz3qQm_8ٛOfUo@x0RX36yKB`//-D]aRa1uV)]!wl2Z*=8 e e$a W݁3J{]F.^̾/*x1؁V{jʒ)[U%Ć;wbш:A9zLvE"9g7Bsٺ Bɡ0/̳񬝻!+[bl{ϖm&Bhni+vkq6J"ԃVHa?;l?CN 꺶 !v>Α!-?~ѝ ,WPHl#G,K3Y= ӫb;Nx,ْHsabx4o{:yH^~Nw QH:tb!B@Do`j&d$f \Rw<<0=86ש$ϜkG##<<|Qm -9}[< X|&. FhTiƛM]-:\q3wƈCi﵀$Pֱ|`>d)/;vu;v\ibޝlZȷ3&[K E&vL|*_f2 w&濛pT<NL~|'"f pk+8ؙQ⹶)̘O1቎Pɶk |s~ԷsDP Ǵo-qhvSLx v'\ *@$$$To von~T'Iݷ5;ng^xQAg S>4C '9S5{ ݱzPþ4s6KG1gSsdyb M_c7ɬbhF]ۚ ՖEj[iݷջ`4bѠZO;4QW~E09ڤKA$]R;z||F|b{Tj;LFIZ6^bDVcIeWh1r,D"M:vXg$J8{{rn"m+qiM5űŝeГ5_C4Uo<=hPSFTh29ݗtJdffr& ̰n/d5c{#B_ICaMYՒz3 nt!$fr*:>(8;ё\8ݕNg_DIqi %ϳVW10^ٻYuO:\ dXZ驤e[e[TNs!$tR'F XI~ho.&rgd4iҨҨqMۂ{y~kݣ͋oec,]& tYM5(5ߥytK.?.+kF/.%;B{Z~Omm.Vcd=,VFsWyh;!*C+'^ܹ=:ЬE_zFuWtzYgpCv\}U; .cԭqI*+<ҥiFsj锲TknRN$n@1+ cwiK{Neuhs~$#7B?a_V{"0gvr!7˸tv6U[9c'ůcEUUYG[<_0=[Wq6l **͛7ͦMظqcMz ]|XRy >ʲݗ }^ca/]yh;#B:c fֳB"m .bj\UC=%L~Tpo|c?!onoZ񖞗 N`۴Y#YَFprEphcx6%~|Ch߼KK^sC\aԍ) 4ړ_P5k r`H28V`,Xv=#v1O= y̳[+ߪC![U2`>Ђӏ?̞!3 muW0&WZ!wLi}A0!*x-В&P擛S<Aáh ggW[&A{ew0mAOAsZHl__`}FX9~LCx/Ҽy#3s-Xl3^xYޞ!`{طn ~g;%;լ~g;):!2Wq<b3e; d,#Prv;G'É'kp_%8}LЗwtƩCGAa\@?Qpqt4vllRåayt1c;ǧ_(6>%k/JJlFRE;L1 [|>j'8$Nv?:x$b ˯P;v94ٔH#ΨFqv^!iY Uxt'{)$WWrEFvqҷcp G$Ty~ޣI=wogs9C8pZd0m愃 ?K @ R3XU93f(7uΔ@0O^̊U\"BG& Nwup88[@~&Q~^D8#F x+jp/X>f Q!)׃H~ywͧP$rO?6~Q2cfBE;6Ѐ]q$9 Gw8nB av %AQa<ާԊox%de9j>Veǔ}šP9/o㱧8"J--@^{0ZטU &A`*@ Ք;s*#^@IRqNcKs "j̚|ǮW&/e>`h{gJ8R\ssU}$pۥQ?V;XEusT"ߕ>i~Rg7S Pn4lӺ^ $+)-e:ªlyx{|n09tj29Rʙ?dH7;3axxu `V:{XtA0bg4/2"#QevغJm'ܠlh8|w8k)sܺ:;F ]g#j[eaSɩ#hE 0q*q/\ϭٱ̺u0$'8 yD7iޯF$;w h|%vF蒯H $<&Fdpǿw XQ?GSM(IӸ.@4?qK y8s濒">`č?$5ن q+>yq F{7Js2V 8s3]o.u5G(1QnBPS8w_jOWAd>KZѸIFiqw[u"Ҿuge|o/MzxOsa ESq+71ٓ_2@5!LyoJN= SqIj/݀{cϧ9w}er/Qlp&hq"0D)}\i@Sw'طCZŚy#E\rkc0ʦ}-ƻh`0R0L!0Les]ڵ5jxW%O@|8G1L_ j膉e;+̉w|Y#[ \$VB_^Jvl-.}[W, W/G5r.֬N qp;Px~,l'z^[rP^k_w|p m/'sU,ׇ_VNA[׋@ݥ߁ABgk_"P_XLw5;wkzWoUsHlpȥn,5jT׋ݻw,-Ν;'N8!D||8v8}HOO.\999… "%%EZ*m#ȘK/Y\) l97}ócUS,ё{!FCƎ%'uۂ6?׎΍q#9Eq4ˈʸ?y' ;GtX+`,ypM|WgӽCGxI\r`Ã޹#ZFSV*b4)((W^RzU"סoETlBoPH(MgvM"HKKJ8\iy hrrrhܸLD",I]JJ$DrPuRjbҟ`u2 B[>ӓd_O`p  %X3_rs^'k߭-ň<_7cb{Fރb6n~{\_|sHI.6lC_{S Ka7_1L'έF~L7aJ#}_dzΈ{yѸ"r1q:)IgTtZ[k96:l0DW\d%--M"Q\\lu1 `0T(;})ԁ7={18/R0JyeqHmOdJN.lM H?eQ բPGg:T[nw󻇿:=@ю:Z I,+Im۹4 7oYu5秱0" 9_xZT /M';e+؊jr OVMHۯqO &?;#N2ulg95擿wj:68OZXi] E> MXlfϞGȃwg\ ]ȣ,7[cM9upknR^NAmoFê7t _l?eRהlu{^gJ4BD[t$ЊA⦴EMn]?bq@qZ5Mj7&u= %=o~d{F{ bkϜJZiXJzLo9c֭MI|Zw?~o슋L9tIHIhbq^)U߲bgӂ>?wDM{ZgprulSrWN%bWIz[ckNe+Xdzjً@PWI.yަK%V~+sKOXbWq<#lMa}E5\{dNآe\yaZcpaĪd,{ᮆO &ũu`;ut"B[ }%E4]|xp_(CƊ`}5(c[V& jY~_AI IuoMuUZjRh=nׯRMy]Ou&[|uSs2SV3=IhH)D"T^v4i3G{QS#beP$Dr]I cUD"\gJD"H$LRj3:F}o=K$:SeRʤ˃$)ڃlfԢJ䖠ٞ%M|%7%wc D"oKnJ%9D"I t,njJ. !^ V=9|3e{\׉dN|tγTD=,e,/ןk͹M6y9\U{`lr\uj~E6-A=i?|;AgDrK]'~_xy$=<9Y"RI&H$IjUwD"$Ȥ.H$LD"HWR%뫳?_Wߟ&tJ(cC~ujLS9zC #vfm7:c;y1N&g))bqv CtNo7_1&mqő4tD$&ntUmn~{_1hX?{A_uz?tv{E"b3CGEFRL V3>, tHM?&7lⶉx\ӢReDnXQƹFKTgX~=L Pi]-iqt{j4&;4gS2˸EIOO%)4'rqY=ePnՀ֫ܦalZ9p.+R CB>n|2e sO!ӜNJ"5=աXnw SRHJJ"11Ä_o-TRSٲ-kN_7VR{v3vw>u!_s4%tFL,kbv't yJ_grpA_;1o:fil*ß9y=sCS~f_ԲS*Gk:ǥ.Sx>?~ p2r [+穭vur^WG72z(.W^.xuOׂdM?Kۃ0?zze YޯxzҚ+#o0PW#WtgDHGE}i'm۵M[?<@B84o{fM4t̘͛shl~/a{=C,bhP{LU}p,{9>}5Z.0Zm|!M;c fY!em퀞-3&@25eF9k 8Ŧyq4gf^ iv駻96k;xZ_)ZŹA#q*G*\C^2>6$M#y󼸰goRh詘綳gv7/F}x*O(ȹ41MiWƹ8_~*9nHWQ$c݋s/Mv۪qm>^/;j:#_fLޖVS/pOWѳӒDB7GބΔp\;D'ȲW= Y_@~^A| EܰT1p>6]}Yo(=<}wuԱ8"=uQ\dIRcxp+M  "-ychEo0VS9~X}\2g#=-W\?iayvnTJ9o 6ൈjگ6ӦO?Ё_F|iňI5ڡi] E> MXlfϞGZ._[G~Ƭ3(m:ѩS'wKY[<'[֓{wDFYjr21Z]MPǦ)׷v^ԋx*I!>D# N1W.qʎyF-m `sRL^.=^tib'plч.cpaĪd,}BS9#ؽUrI*9+W7퉉,~.<Nbv&(|X ǯY]qq P\ӂ>?w,{y (688;~|+ xpK?¹3`~ =r*qN:>[e|={h,sD܎g/NW[>c'߈sK?֭[9ڶ;.xKe5cAʴUYH9o157:[~PҒNUl ǥSx.W'nDCK/\T)0gaώK_kl8Cbur'mE}J AX:@~b~DDP`-z5_jb׋@'NWԒ]PTv׷y}>8D qqEpHqɫMI$DRW4J6D"H$ ]$D"I]"H$I}K5TQR1(UvQUnV6kmZ'88az&JיQiK7Y$M/9$$n6!dWVS5ꫫV]:r,DzzHOOrTUڬ)A7YJ/7lD\\"g+.wCV1}<~XxucW֧ܵ'Mlun1b?N{F\la~\U#&3U'w!ROfRJ"9nV1/}.Ӏ&3zVb[#>}XUSPQT33!汿jꉪ8]LVaO)jWLe'p}"3qv+)( *=S'S$&ֲz*f+[Þ>X3K0ڕDrsSk*my~{y8YT,Ćo3뒶:=5|VfC͊YIn,Y(@+_  &P'ٮi\gjE-`&=(N}7zuatqx^q48Z[joHZWc6_1/a؞xHQHJϽ"5ްA&&<>*mJq h;2ζ+&ju47JNd_F؟6kkfW-xߩ#,{7]۞\o6ELغIi@!~ s߹Y}25UPJR#m_6Ϛ8 *m 0 0rMdCuMپlz˿Mƹ)?s > $XreSMr]Lm,h!u:r.U~v3?sz$FՇ8W8ێyju%7ZLN-  AlY`=KI 5[ Ŗ 0Ҟ4?} qw>wJrjڛza]mLϖ\ 1(̾s,Jb83 (zKTTS8W>kFX|s%uhs;B&3a#OYdVԟTה)FԅEqINɹo~ R3XY\I5KIMr66nx[{ F!:ҦgC4>ϋ)_pF-h'SڈOSi'q4[Q O)2ζ\{TL-vp4\_Cê L]]U&\R{~ w $..h}&c{>k^Tھ[ԇA2Dw*NpWORi/qFA ZXgE;z~V:jRu%uPi0\X1cF-c?0Yd0u,V75W  ޢdnXo_Y鴘熩՗8[V#2ζ>kFX|TiO\n?`z8cR9Pͺ\M]jcJ.(D fVijx5YnTi=9=4jK$DrZ650ㅕY.H$s&ubԦT.H$I=GI$Dr$LP`J ؐ`.Tn6j5_Lp#8(FHmL"dXzQX=%d4Y,Ƚ ꯽ڗ;y2[]V9I gFljq&Ę/ ߛ,;qWTq[GEjZJ.LgSRDJJHO?lV &ȥ^'J7={oe#&318zud=gy{n{E`ߺ 5%j&4/Ϗ%$'YUTP#tЪ(/\#Į`:=HQWRtϰȝ=peؙ{sFV>$H-vyv r2` 2ky>]IذC1-UHj*_+N iDOr.#hIQ 5RSttw!ޢ 0wd2㪆y9^0No\б0s9){XnEo߲ YAA$~B7?0{ǠY1scQSVH$2dJNHQ_@~ޥ p\*Ϟn5u5j_Ap TT h=DqEs2^eLfUY.X%<{zC,_1e;_OKJb,Y#6&HnZjm=~vGe؊6ӦO?Ё_\QETkΥoZCOkg6ٳVtZYeN7v)/ttiň V\*v1ciﱲuג- .rFFYD٬WV.$RsL){;p<>LmϨodHdR/p6=o~d{F{ "~`~Jʩ];Is`v,zr$Ffg'rpǏd]Su57_@{ SRig?F>SpITa|`"t$5/;Iԟ<4߇.wϪk+ker y_l3|` 8u=;vp,}1 6&Hnd-p$VYUW_S59v+^3f5_!!b'E&REM"H$*T HNN&;;www|}}qtt,ۮΪI5D"HGIIك?oɩuOMIn"n|ѢE28JNjSaLޚOgaq|ca|n$ߒmߠW ]c:OΚHrԑnBzkczXw4鹜XZ36Wn>F/ʮٜ˾ i=lN++ #~y~_3I$Z`2j*{K9Gow =MC,:cԬuvPp& d.ӹ8 )g1G}k?ǥE+7w{xrxAO~-=[>GTDx>{Р9dts/o8/Tp8E1nd|pns-w#* _q,+Ͼ: %ٯ-zyEJvg筮b,ay}=Гv4a+g +A؋SZu]G2zBu~o˫D"3?c(vYBrcL\sY9ƭE|c˩}4{7?8~I ":\ ٔN&kM`ܢͤt!&HM?Y6RdjuHZEG7} <,s޸ ȝt[CIJmKW;h4WF#n Mt6/Mt4:ee4#Т|h?~}c,ݖ~^ֻh殕7:ػ[# i==q׷a\'[8@o7Z]W膨 E{yr@jT|Ձ^ޏfu=6?pU3ܻ\=# PjHmAt*>%@ٱJ*g7ƫ+h3"#?Fom=+!Ҷ`q)uj\5_?zܓRPonoZ񖞗 N`۴Y#YَFprEphcx6%~|Ch߼KK^sC\aԍ) 4ړ_P5k r`H28V`,Xv=#v1O= y̳[+ߪC![U2`>Ђӏ?̞!3 muW0&WZ!wLi}A0!*x-В&P擛S<Aáh ggW[&A{ew0mAOAsg%I}I!J*T۩!u-S7rTs'V;5l^1K0eDV*N=#ReT y"WSFBeA=ݎcMP@9x%gsdx?Hz2E[cǴ }{Gg:?d}pd-Y s}WOHcGH& M+J K3s|8GOl&+@TE;L1 [|>j'8$Nv?:x$}{9}_s2&p{%`G %Y&sʜm1e_72p#o#r ޶|ط4$Qc@ LPS@KQYYKZ$UOXR߃ WDRǓ:9*RESiUV-}9o }~W쌯&#p!SGّiK "-ychE jg45@۶Sg%,Y™+(uu$K0eV^8W܂MQ۶co0Ed٬h_9id,΁!S \bM[ʃ6`h~^R$?n^qO-q Y䅃hߛ}jpFRr-.sD" lb>}"+#e^1G qJE sxbv,&n( h<+glB}'_rdxy<1I^v*K(%_u~Iy4Mɍ/G*1GɄr eY+#쟣)ҏ{}i\d J%ם-RmsCn3pRSl˽c֔kXaJ>jӴ&V"Ԅʂ._#K^tJ]6rΏy?ԝɱn۾}My;tCt^Eѐc }ʣ}=-la&'Fs83iqFOO wv鰚eRH$WԷo~qI]Rqwfeg/Yʿ3E+x.@䈅ll eH4f- U(en S! b^_S,ˊbdz@ʉbɒ(H87LQpjN@ 9S?ŹS:OKdQi@sgI3b3$(S(V^,UxA ='7ZW*QܐiH06Ă8-A84uoxD!eT6GijŶuHHLbOwE,WnZKhh5L;@e`&!؁7@D@ $"AdA>b(-aA##',\pCQ:@4=D @($IF"CuH)RT#&r!dy|A12Q>h(.DѥhZnB+Z چEwP9b,L\0?,K0 +*Zz[{}p gsbqF-Wji_ Ty-=;{VgJy~L~AAAcCaaaᨑQQQcq&&&MMM5L9yͦf^fKjn]̳w߰@--2,j,[N]}\gf귢YXZ5[ Xì ۭ_64{mm6mڪنvھݶگ`#1qc7'g'SӈsN~KFKxW_5']?9Iݎn~}x9 zxp=y=ٞɞ{=^^\Zކ|z>>>}^J|~s[T< 4Ll r Z 8MU!CiѡաO,$ahxHGsG@'b[Hȥ#̋W3YmT~TO4#zq11cƚb5}/'NXp5Q+QؑDLKO0 ]hpˋ Zh2>9>@Wn;Iٙ2{#e<҆=ҷdxeTd o23d~̊jȚ [I'Dj,%K/[nK/Js9RT,&3 ̭,nE˯XaWVVvXjjdu5k 4Z[MaYu:t XXRܿ}q? ~`j~ɕRҊүydSO6nvڼ{ ahݭ^[Tok+g߾x =;(;d;aUFU[VgTߩiݩsΏnݲGgO/{{ VkR[QG˭{?n.?7k՗k57975>m5\p!C-V-ZY_{$HQ-nj<8^҆hmhw$v9yW_N꟬9~jigΌu^M?;ؽṄs;{!¥qe'\it9vש7:u-[osn_3Nػ  ÂGG%UW<~Rr'kO> #珯CE*=o>98rC//_f׵фѡ77o7|}Xؓ?|sϗ/Ǘ}%~f{Gb iioɎ d '{48uuT({Dzxjox59iv^v|Ļx9o@ ޕ!vGG;QG:ciTXtXML:com.adobe.xmp 2016-06-11T22:58:27-07:00 2016-06-11T22:59:57-07:00 2016-06-11T22:59:57-07:00 Adobe Photoshop CC 2015 (Macintosh) image/png 3 Display xmp.iid:850862b2-e121-459f-8cf8-f17dc9923b60 adobe:docid:photoshop:3cbea44a-70f1-1179-ac3d-f90aca98f8a3 xmp.did:837c888e-355f-41a0-9864-8fb861fd5ff8 saved xmp.iid:837c888e-355f-41a0-9864-8fb861fd5ff8 2016-06-11T22:59:57-07:00 Adobe Photoshop CC 2015 (Macintosh) / saved xmp.iid:850862b2-e121-459f-8cf8-f17dc9923b60 2016-06-11T22:59:57-07:00 Adobe Photoshop CC 2015 (Macintosh) / 1 720000/10000 720000/10000 2 65535 705 395 A cHRMn'sxp1\'%-IDATx}\&ѹ,c:tWģC_qk|Z=`r 5Be9 n@'@כ($jRPM6b8|euƱIdpi7xpu|>K( !""""92n, {;wtƏτ ;{=>ժN0ٳq2Ν;ĉEDDDeZ8q"Ν=(g"NE!""""7 """","""","""","""","""",""""r#wWXkBn!~rEDDDGqy#@ fΝ;ߎjUObܹsW/>C/ye_,"""""Cx6jr|>Ij`"@+6+*+.|Bf _U"""""9_Lx뭷#.3~xn.\̙3X,dXV;6"""""4Zr-zz뭃kc>cB`?~oo K1Z'$$ x7[׿>l[t/EDDDD(_ ܫ+ oG}Dll,oF("v׾vP'N?P(j%66hն uΟ?G}ą pB_нp}%ޢ[oz ƪEDDDdHl뺃[n;o&L`̙1hnzzzlu]$$$b6!""""#7ޑ¬`„ }#V[o .pEDDDo#MŧNR͊g#W/~mݦ3jV c~?U ðlvnE^"""|wbccZ*!\p3gpL""""ו䮂hƍcĉ* Q,<jtQQQQQQQQ`ϗ5gmy/3?9w׎/ff}˾wgd/Z!  aXnuSA0=#! _n~ˑMm_DDDhzWay{.PWH8~6n|B"t֖|;{]y?4xsȿa{&~կT7+oƌ}8,_z,C[𳟽|Qz.YoQ2_9ϛ#a#¸ w9~$w<}&{:)YV?l߾wN짤vu~rJ~POogFa``q&?ALZkK*;˗q`:s˨l|s!L;˗~fΰd9^θ[>71ʗ3^R[^GZ+ (]DDDD!LrwdowO>xʤ xwOMc}L:ΝH;&_3I&Jld-t!`oi%9v {s65vp`^N_̞17({L;\Eifܘg@ٳXrK1# ڡMȧ4 K3SAny1k""" S0o7| wLgpE˛0~@==ٔ;>MeBЩT460H)L9$ZHeFl,$hXDDD?;cXc1o>sШba; X,7r7SṎʂrf[/ֲŷDf/m`2ƀ6co$bb{%fbǙܨ/aL:$cXHKD-,i(M#?Δ)SnʃaiN vzK|VhF.+ȸA Wa#6vGDDD&r@DDDDEDDDDEDDDDEDDDDEDDDDEDDDDEDDDDEDDDDEDDDDEDDDD>%cǏQ{"O2E%%Wڒ\Oq!""""; """","""","""","""","""","""","""","""","""","""","""","""",""""2&> ?~\,"""eʔ)*C\|Soi:(((((((((|Zݴ{ {o;G=mb ,7.bqB㘻nE̤8g/X.k=eOOY`c:ɉL\  zk 8w^Ϟ'jb $}[Cl_kAbڀ'|'k_]e(RIa' Xj˄/\vV!k %Kx{qgVrC!, Q׿߳^\)!I6;ahkdu6 橃,@b κwNcL,ܻj$63KתDL#r3iqzk`m.op>U,ױlƧR?&j?m} ۘm9;h-ae셼ڜ/cp.57CY<>5kᑕu#,`K}*+Vց%W_wZupq݌}Ab|n&{KzHߊOU.=thI#EˆGT\>e.^fnZ<>7r\-%lOh+#b] Q3p߆DF~uy޾lij%iQ7|:\jC#Tk ہ/'Ḵ:k7ɑϗc.4 {ƠQA0_c_B#@MH("%_ 7ް;T ?}}!yFrxi%3#9)-*t]ʴ}YCxB(sat#-O-}ʌ8233~膟d@;gN'-ERj[غ#-ed|'jE;]2#{~Bݿ6{B"]#7'_Z5 8RZCAndҳ,]UzTÒD,Rfʟ$kO&w5.=~q$SX͝>j~ 1}cI/[ޏOkĥ :Ik*##/df➇~@Kg>x|x~ ?`Ig)r,=Hz)K+֣JaJq.9-+iOtR%99KHYHII!ƺz͞O\Z?6k;>2Zkĥe{{fA۶rOlsE^tk/?)~Nb_߻/E6۽_DHʤ3u ӿLED!u7v s!s+$Xmv \ K~\n7ָvX,.>z6\4ã"8na#-qA2cȶ\"B!:S]tY REF?qp5U$i~!={g--ژxQVo,FJWo|ukiiʪ~::a;+F)s|!z?GƥFll,6#\<*ux':`aˌ<6m{XDhZHfW|݋h|&p`KwzkPv/|lY %Oȯk^ژ9:]쬪cjd_n3]?m@e_=M_<Isݚp.y_$t^SI_񣖹h$E!3ҙx:P8s0: 0=S0 {S_-y墻9)њTvw0=fOum !B!?6tz 2 Z9@Ak\`V8-M9 FuwGdexݑy}wCOw. ?qm\} I /ѐoxhMHM?+rnZNC(U)K1}9"Jy% 6ox9rsH}Ϟ񘦉a@鏜PpxMfU>,B!7s7Xޒ4b~!5B?%UN!iH6&^8w4tw,]_²-`\-tJ¼>ƥ+lݷo_ uzVw=|OZu\Dn,7H=[;Nl?3y 8"}p ,{&L b!>5t^Eշ.f PPd痲٦"z X%}-? ̒T[s.ؿoK.\]LMjz:$*ް)pPwwO0"un1I֨!0# بwMqZ\YBAAIN˦f;g%EO`2h!jc!78ﯦSuLF6dgg͊AEBX;`:7QJłl{ CO4Ηi]3onhڕߡ`,!eQAsCk3hO gWhÂ%>tRR>/XƇX/yZtk^A|n X,~15!wp2O(:!Ȼ&>RőSBݬiwKRȅЅćB!N sYC=sY<)qvS/qFR͚#|ygĉҿK!ci˯ׄ+%% VԹsw$%FBs?O'|33y?вw\(DSF<9kT p9zQ~P(ĹnΡ7_!lv\.u+pTYЕW?[9aP-j^mxު{{4"ol 7MkJxkWMsÈzomSNP_ZJ6c&푗m` ?xj;+֥G3 5FOq ^m,aed%cFG3Uz'>SO8I$~ޘaD3xss2smM9oZC[Όnx'y'1I$ ̀"4J:yEn87=oEo7]4@OE~2>;U=Bk\+d\94񝻽/ŵ?f=H޻bDiBVuvv߫K*)}>iY-[9GU H-krU{㒖m9ę;XZo*ȼa>.%?6΄hy|%-͉ϩ %Hql9m!}{9I/xzI]Q@䁆Ҕ^4 yX~8KpmYyC]]$ J-z۴: 9;j!qE)3~c!7T`+8 WC,oOބ+ҟC9:ޢ%sW28c~q,o]pg̠}grG5|RK2O&79Z3'C gXEX&~ė4(MؚJՂXH5FV;q+(S~$`Y@o=NEۏ2@04˞eAD#WGlJ1?TʌhK1lS$''"С.g7zu,%Ӡe"#o#gF4K?XHyX& xuz #˝|*YܷٳS/g dJ3/#?%:>RA铛Kxx^CON&zPZ.iQ) #ϱp{WFO:{g# uOQ7tĦ)/P gDg'~FQZ<Yg-f,YS+ yK?9X_d KzA+. 6E_Cg-XxpYЁ2M/P:fD[8{?3x~ B.zyeXDǓS X/x~Sƨu_wCb!z6lid:Go݄>b]Ǜ:Nu9D[,t,d=_~#`UaZ_9ut܀,Q>L27I?k @41 ۠_ b.+ڞ 䪭o`L0l6lƘv@0fb3{U;ET,I_˾}?}_Z梾Qn5f?fð4Zi\y<ڌkѩ6vY;6 )o @KJZ yST|xm*w1T?Þ+̧t_EOTFh'黸A?v)"8g_lr1,vyOmɤ """" YaAF 2T""""7DDDDDEDDDDEDDDDEDDDDEDDDD!XDDDDD!XDDDDD!XDDDD3|YUҢ|)S/=,U2>C6>GT9R!ϻχw)D ^@f \^ߙar&jgzy`&RKf @`?9gBho$ZJ_N %&&|8RC[:7m&&R5]}&1X: BU. =aߺC5cJLL e(N--p}>i{v~IL % nN ^k^=9(+lBwBsҚI %&nnƧ{lܗ. - \Ԯ+>AVBйKĀ~֋o9W{+^R'W{#5})]+f۾ݸ܊pmf3LxZ-/aQwqlޜ}}1:m0慉T6~q4ؾy+8+2l+\q+~}KdOu6*c [g<իI~D"_3Y-\|*U.bzR6;ɭ.5 iƵpF-4֛xg6[bIVS;?Ll\$$ggqիWv]gN|U4#eߵunI9?δqn%OpS|.[9X~/nTlʌu`7Lgk+g WMz v Z000 -Čbv%&8F}}{H8f3{lҲ4ytN{/v]wuz6W,C{k#ivy^B3Nqv.x9βfLOK3x{_ xi,[O!' <3dg;q:8v5ds1,&-,,߷/y(f̞MY}k_Yvܟryp'EnL6VZÜaù\)&#uL/;#Fzmͧ͵̞=77%c;N+vVhge3c8gp 9y8.:Zl߼]M/iT6{ݐfgG8sbgZxӲ)klvs. 9\<i;QNq;4'O1۶~e||g-Ǽ:g3۹s0qmIf&f{ffm HaȮ܌m3ÆJ'eߤIYUpξ,kԧz +;Ag{QZQ̧Ksٳ6JͰ"Mq^ i l߽E30@w;Mfo m$-rZk.ӈe5E;q'9QFC&;$3A?.W~δײYHEE).껨CRyu-V n;M䮮&t-Un wA 3D9XHt w @PEu?p:wϥWGDRRZZp{`\VW"ni)+JI[]14pD8G"6ký#*yE[i[ͷz;QȈMZJ=Ĥf3@ڎ}ˢCNQ)Kbh[MVm;`mwd9% ~ LVEA.9.RMVV-g@w+]%d-/mwj&{!+RN܀cTFRs6xD\~3gXIiEyT]fιX+k)+.4G*sytO\4A-B}'-~i$nGĸXE0ݸ ,ʥm'5%f冤 ;+|\c50=xqQNݢۍs~P߼vuj- +*(KTMpvYx&anqmVMv KXnug vQ,]n&z3[wg ^^s;VL:[ܐXߍS6U$z]Q=;mP(Zx-\xגh@ڲ2Vese)OB4 ـԙIZAZ b`\>7Ź'Q{=cz5|a/*6 ڛ(s 33o?b)ӒEFY)q6}!of\,kj+69HH^XJg"IqZ5a 9X J21odVuj潍$F$:Y^]BG9n3}j*&19z@hi$*H/}[h*v7 Abk\/nx054]lTlV vIMexe&`^V9-+HTfGh=`llFe<u%%SPMu(5j63x^S~/~`IFJ,Ufs.\A9-Ɏ'&H cC.H2 deQH_@R)f\:0M=~~~:zYZ"|c[#6#gbONKh'# ykZM}ƫ+t![xR6/& !u 9$>SB]pl-yl/N ߢHH%/3o&>CIe\/ bshP10:p$:…XՈ1j$} ⣍A=) y^E2H)puc3A?=J$L!M3 COR#/iPVN}kBj\џixuNJ~n;Jg&i^*eO wEEd6O]n)+G|%]a"Ƕ8 ZF_JKeV'qC $6#|a2;aD_3,δ3oⲧ}G FjXLg~;ϴSY%%O>M&u[q4$ RRf>}+XV-zɨl\*Z xxpkňqZۻɌS#:ċK,\m6` )X-nh>;i;o-x}^v=Gȅ|@z~w;mmm~Vy}teՀ!;00H+Zr饽E7_sܟef]@RNAO=s?bfFٲ\v5uF)pDfdLrhldgrvCDdę$H_QDFblK,j6?*9)q[ l۾܆A--%:+<)au+!MrAľpS!6|>m;ȭy岑 rrk4SԜ$ #TԭcmT$ю yx;۩*1fu]VncDhKH! $eF s _GVN:}vYn5Cൺ]@Ii+htq)9e8\:.1!vh)YN}s;^o;?J=eh\̂#ukFh۳ջzOqRsXyQ>2SѹK#xs3ٜ܆606dF m6cuZAeđ@AC5.xuwb< *қ(+! TZ(鉼jL7HTNnӀ hJ+YVR YR28K#[_%eٱ>H:rC=!HG`lxPT `Ff\y#ujwTUBH=ݫ.qL_od ldnށ/?eTGšDw`# -) mz\ VLveAL&G "^}i9v`0llnibFxIqE>}ZO{q}j& XmUmA+7TLL0?G:ޫy΍VNI j]w}JK)ϹꖣzK@`}3MA.< +WR'Zk\qûqoAð]_< 2//75quۉa\~ 10FXjsm窕a`>} \'W q!Ԉ+WXVW~-ܢ"`````````O5ǏEA!"" _җTST"""6M`````````O˸a'wì0n\Iߝ3%6:]tu[6k2kz4dWc!l :N8yPK?Ϟjbb([3!QgWdQmD8{6 J6} 5PU#Q(YT7|^bSm9V1zZ#fUOo\:ľCضMqeؽg~?TE#so^E~Kk?"}xf:YED>+n99%<`*_ΟtSsWH3G3.+79+xQ-`޼yXG4+21o<2rV졓XƷYI g w<}xϐ/7| fiL<ɓ'3)(ؼ}8Mh/+ y<z'!Xݷyg$cF;~|'Ș֊Ǟdde݇f8NJyXlAI,ڛs\'Y|솁}\NßI}n6/|#UՊǞ;>3.;!FDDG_7b`c0i}/a p&`B5lyF5HTS}WpjK2t#M0UL6g %ʀOQT",&v1J!~ʮ9,SYm'p'/w ǰS&'sKs/ĩ hvl@W\CmdPץ^&}_c ccȮD#}[Rـn^b]3 ̓/}U/eAssOva.QJy_ EN"SwO̳'y=,7<.Y ^Q4ج]:L|N[<@ ɮ~slzyQaLoM1|uPSJi=NC}%%w/{ rNg}?60]V2L/1f6uw<]QQpwm|jLm1,^97HYL:a&R;'!?](0zk=LӮnhܻRg1jxقz,j־`=_x^܏VjҴm 0mO0 G}lil1$̊!aBiDWwY_6_2br!^zEՔڰG^<6`c(L_3(ݤYa />tuMYf]wpVfMmą{‡aS)^WYKnޡة1-&we,bfM")Õa!p4l6;6B6'&s&GA7 MGn?9##1ܓ] +yj.UhBc9lp0OcՋg5L3L^xi?/ /x|8y'L*f<Zk͓#Nwr]1mFj퓀n?h%~'6d~$IVNi/ s0~+yoeno3j1u;ztdf:ߟ,J`?Y;ɓD~v)0%c߃_S>^8n܇qg;&yOvu5@:nz&v?y6YPIΙϋ/=m'󿿀/o0sX|W81sX=g.xa j^~W~×/SqxmbvywMzC'9yszawز9 o|n |_3 ;jR8!Jwm4nتJWd_![m_~IQ mp4Z@oQ~/A}t%D.6{qn"7;<bqCk)_;M@j7a5b4`Cc9KY匼1qv09~.~iF8~ӫY&2 642'1FxX\6a6&b=ڪ`ʭ`UvI>^\˽ۜl*eSd[+u^캯K>C'g% FZL27pq/} `bjd̯* _tǴj4ȗ|0}/N*A16ixU4͸7o&f VeO˪̀aS}'"cɶn8f/‚a/Fk&捠ap4Ըj`JXD(((((((((|J&:uJ%-j*W2eJZ8~ڃCDdԾQӦ"""","""","""","""","""","""","""","""","""",""""iwZvAm9fV5͸X 3nDDL|n1qDq\r.~V+v{k b!v4sfGi'&3>: fP&>_7 D mP'MGy+qqc:αu9 y" c>E[_%smӞÎ]֤l9JԂzv;F2ࡄ;/YԻ򶾊jśdeXF~輟jw95lOmҼz%.;[63㺞{giʑ7atR)1C>Qx?u:מEYPȫGvc}"rxrCXhoelܾ4g-ϊdi$mvx[FqV6=֌sl9ZÜaùp,҆Zl߼CV/i"'/}LhiT6{Qmvvy:RYg̠˃#=)>Fǚ)vem뱗cWN?}畆MsI0'..8bǨrCWI304[kðŒ~779޸gsZi4d p|g-Ǽ:g3۹o|U$s:6lt^*Y|Zt]D\3}U{8vwL<7幹TNRns)<m0ESCNʼnQHHk`ԊTWFj eҹȬTf\:HVI $Uk<u%%SPMu(5j6 h`IfT{͕$R6 ot5kQ8X@z 6`dLKƏtC0F^XVv?mތ3pa-BHH'/5o0C?C]hBʌ xyiWӷM;x= $."8;}F*#;Mp#'!:5?\ssKgf&. S >M;؛Rrr̠>%@~nIכq)KQH"'=R :]FP l]v("57砩n'D6\ٜ ?@6kjlKS/~Ɇ )偄Bvצa@K4ȼKɌvrT.&v};ɪrۅ۩n #hw5S]-x+K&jv]8HI'#g\4fP'۳廀$6.荾>K 4|rݔEKaѽsR'4Zhfr6'c\w-ܙO?eɃ\כ ]gw'98ؘ>r=*vkpvJQ '#Υ)bN C^Au.aE%1u]A.QvS.Q#]R Þw~w]^[;KI#f#:F–oQ3wC1 Axw<ܥ;̸v#FlGΠŞ4TP 95 s6H 16 w쩎AMtFjԕ+"JãFost{?\. ݙxQ}> "6%M@QfJۊ$*QfU*7% {f^1v.' [gQqb+3*4rj^al^M2s*H|: B+xCjܝH *j`7l\6Hߦy@K PK =혙q}{gCxA,НI~~hr{c%ށ#߆&_uV?%$)Ki"; Gj:Iv4ZOwL2)3TitG^NδSY%%⏴M&u[q4m'e߷2eRoⲧ}GO_̸s62$niykKo+&?Z!:1K>K,pN?.Q܎祹r5 @3|X^ی磽2q&oȥ G2rwql@6쬧ا1qcvhZ]ζN4/Kg0!6g3/I蒋Ч~so.mx}>x-zcSܬb::([ˮh#0S.L-r@Cnm|,[Nu.KCbDN[[[_{rRUEm1|c?;'Abgvv_;3+ʜ$bHtgK=m^=d6ZhuT yٕBKK5e;v,ǫY'l凤:im$09G:o\Iri>{u@*Y@BF!傢}x,?p̎%N ``U~c0j|oyiYƲ:xijh$H$!:}٤"urWR 8sHu5`X!?6<9@Qi*ʫX ''[!#\zԢ TBVdGj#skSR]@Ku5%VR^QRa%%6;]NUGK7kP.aO2Sѹ7 9R ̌jwTUBP=ݫ)%gey\ Q9šD.5hH[5H+Mw`9u%#燝š q7#> -) Fz\ *.XJz2V/ a ynꚪ GyČ́FL3iG]-#sA2 ;$}yAcflCn0M V33ZO^O?yyo FHPi`WkYM3@0hv|z `F*YWNF^ @jV'7C=r3Ƨ57˵Bn,2r@Fd^ֵp[ Ÿ>a؆xm4:'/W01W~H00F1kY0TWv=[ hj9,r3EEpCIRR/WHSQ\'TW;Ǒ_[6bHN)T9\G ```````|YUҢ|/}K*iԩSj*QFOCBBBBBBBBBȧeͰ]G ya]sl78Hd5:~7LJ Ʀ{# t$8q2ק):څi=b Nf0Mf ø9i5gVuwfMdo'ݜά1#tp築,A&N>pL@08hiՆ1p?'ϞjbbCSDDnͳY6q"={Ypu٫ {>ȹdߺ'W\jVsij7L>ʢ|ڼG݉yu=&>DG7ȾGʧ#&r/} 6ΧhT6m`!w1;敆F~ZŶ6sфrȶ/=OO֑]?NjG-cED & yd楊Tf3k+7_Qab1,zß0ڦTu}mS8tMn9emnrP@lY/z{̡{Tf{s.5'a}@ /?twҾCX`B3y":X@mO:/kX _vxt:IԤHGvL]\W~TdQ9&UDD>n9ݿt?Yȣᾒ*'#y7/=MW 9+xQ-`޼yd<Ɓ'9c̛7yXXtRixi:< Lg]CO2~bڽsЂ=Ȭ6 #o<xu]8{ 111L&&2]䁧)XH>y>/go"""CaG]7wW?x7-Ģl^\]t~&h|q&^\+)/9ϓ(auy9_zm5> ]o6S-a+\OE~4^:?٫^d✇(Y8+j(>̆[:m9{ D#,{sNg"3 ELW[!.sN]<߷$]A\Jm0L,Cbqҏ|[97=G#|miÌ1sy<\hLAfcWprm1g б* 笤a&0ilƀmt: ,bγMgL‹<<<»(w=߈,/&~Ƥvo5l7&q\i&.@Ú{78}% _8pyޓS/vYL;L@d@g9FP;WO/q20fCg2`FM  Fi7rN 1-f~'N謯it 0+ܟ_Lj""r`cRsmXQ~hyxhz 8 {?ކ ?;;cy҉I@<謾>''6G]~"' 靟hN "_ƘpEYLض':JÀv pr_xѽmn_6A'x"­Ӱߝuyٵ7fԤ,bDM0r.ԧAȘݸaL 6rֱIOr'*z&>ăL^ӁҬ|> /,SY9y/=2Q@z'$(bpCs/Zd.mž ß;G CT80L|>_`[~i?y ,;Ii<}OU2L}6:ΟHѣ:t(I5 e>NuyE(0k`$PXEQ^s(o,"bysx4meM_nl)5_Y`k憗JXU]l~U٥9ub> i3Ii-cOMwc0j?A{&0gq9%k2x/t?sYHދ1`*J#NdeUL䇩/?tͅ?lMK46^a% FZL27pq/}4 AQQ،GLӌ\h?g#]dq+<0 f}-ծ|.o`~c:u*ǧ$8O+QcmWmi÷y40g"ddq7W306Ʊ$6_cޮu8>Ml*sbVιBEDDD>Ʃ.bL*0BBBBBBBsM,ԩS*iQ{Py|~B)ST""""rCtQQQQQQQQQQQQQQQ```````````````QQQQQQQQQQQQQQQ````Ӹ?*QOݸ"""","""","""","""","""","""","""","""",""""r͌S=y~T ?2 """sA= cXX,B!x o̊jO!"""7  n_v{HDB4B _.C\ ' >F?H\AXsas """qc~,"""yϡ~^. '"<T?s+pӍ gΜ}""" O g|+z̙3yG[66c'.?vgRɌ9)w ~wj;f яy58[{oo#jy/aвwT.K̈́'`#cMwn' פF7kqN@)k~}:?_NnGDD/Hc.gso%-3?;n?aXo߿`ꟍmQ~~|VcnOOOi‡r?o|7.܆NLJr{B5wNa7߸`HO/e|Z%A;Ɔ:g.*AO1c ظgW,;YPɢ)=Hs7^dxgO|+.0µ,衭~]'`̼XoK`? s1M̦|ndÚ>k6E׷G@!Jt8@7:}7. R_qve3's|ϝcWN|#+S;2,XqvހC 4/E[<׳%(&f$vwgR-U?M7o?nc~*%aWs /.waղd|@πOa t/0ZWx&|^w6Ӷ y;I|KO~j6>ǻm_/ w 4N0n—9?^W7Xb? BA0?a K?M?wۄBy{~zO74tcZco&X1ϝ '܎ >w`~p#0?O|Kwr+6?B8,f g/axÿgQcz{0c(B|LGd}>>=!y8 #\.17c#\{'DnǸ Bp|p+Ig^i,י?;G,(dpˋx^S'>8,2z:qdGÉD=9+d?Ckcя4~Qx6fHwqu!z9p%$?} =| ff,+x.p䀇G9^xa#o̿tzD0}8p0vܿ 7޽ ͻ{7muLy.OL`=q$'k<' s{) $e35_6ovq{\xÅ9\G$>=3w-灇s8':X˛=_Mc:s )}/n?ߥwI)$\3_h`äPp5I8 55o.spH'&8gq2kmsF~$76P[CŏwRLs|1* "/`94_jη68&EnEʔ#d7t`ߖqD.{mQL]Dʔ8)vn> y2Ϲ5[1.\`-̛<[#K?aOҽĎ.Glm>w -}1)ʭG={?3kӸg6q>oC_gA|ar^x>`LXnep;p?k̝Dk8R~t!or,L#E|kо~r|AS~{'H||-o'2bܝ6e +WT16pE\^}Q5#w}R]:DkkLN8rg 5^WǎdVZìkFlrۆJ*SK`@@ KS>w.ooGP#zzhkL4 Ŏ =/԰9Պ>N6j֕0u #x{j}':U:'Ql^P:1^tU:qg%^Ň 9FvCmN^6#mކW |t?V6d6MeSǨiv0t:bװab4bd _[Kmm-8 @YQ8kB"/\ԇI+"ٹ gEeo-"Ia|3CixF?~{}^Xz9KD~>=}S?q$-CA|?v3D;9`z_ZnW13[]LXW*'7uq"9t:5Bn dK'D,ʕ :Wb~Dkk+MMMN.'<wg߾|6Qݳ(_^Ҡ/4Q:T)pqN@Ŧ I!ђ [n PSUBB93S6Ïa r?J:mFg3'z:Ghxkl'{b%5?~`7G(f]V:ikӨ,.yq|]P);c:{q%UU;8gP XUkk+4Zq1=4oNl8&65Ա~Ni@qjS㱻ޓ>J)vsu8iGWVyJ-y๭th=!J[K]ѯܼ ׶oquMs8l>Qp ǁĻQ`s%ul{1?ZظB 8_ݛe^s_SCRB8'x rG4TxtݿS9׳|qSK?ElwjXv \LaaPv0PncE8 NkZEaĻ;Jut_uGQ]ӏ_ } 9:y !.׌o]8B13D"+VmTjr}`4/HQ/ b~k"jdp~OD2b\? (_"+X_|xr'YVsWߘw=rx"$NzE)uvm>@z\*x "qxX[UJ͖M?je厷U :eۛPuz [pqߖr^J?oq0}#o#c=DkGƮλ[C MwͶ?~X,h\C#1#}'q_VBq9sOĒ3҂JE^RB!XLzi KI!&'_}6R B! REqB!?d* )iJB!}O*JB!) %Z B!pJZig %WB!ĢE9T4zGJB!G2,YV +٦RXf[TB(>> from terminaltables import AsciiTable >>> table_data = [ ... ['Heading1', 'Heading2'], ... ['row1 column1', 'row1 column2'], ... ['row2 column1', 'row2 column2'], ... ['row3 column1', 'row3 column2'], ... ] >>> table = AsciiTable(table_data) >>> print table.table +--------------+--------------+ | Heading1 | Heading2 | +--------------+--------------+ | row1 column1 | row1 column2 | | row2 column1 | row2 column2 | | row3 column1 | row3 column2 | +--------------+--------------+ .. figure:: examples.png :target: _images/examples.png Windows 10, Windows XP, and OS X are also supported. View source: :github:`example1.py`, :github:`example2.py`, :github:`example3.py` Features ======== * Multi-line rows: add newlines to table cells and terminatables will handle the rest. * Table titles: show a title embedded in the top border of the table. * POSIX: Python 2.6, 2.7, PyPy, PyPy3, 3.3, 3.4, and 3.5 supported on Linux and OS X. * Windows: Python 2.7, 3.3, 3.4, and 3.5 supported on Windows XP through 10. * CJK: Wide Chinese/Japanese/Korean characters displayed correctly. * RTL: Arabic and Hebrew characters aligned correctly. * Alignment/Justification: Align individual columns left, center, or right. * Colored text: colorclass_, colorama_, termcolor_, or just plain `ANSI escape codes`_. Project Links ============= * Documentation: https://robpol86.github.io/terminaltables * Source code: https://github.com/Robpol86/terminaltables * PyPI homepage: https://pypi.python.org/pypi/terminaltables .. toctree:: :maxdepth: 2 :caption: General install quickstart settings .. toctree:: :maxdepth: 2 :caption: Table Styles asciitable singletable doubletable githubtable .. toctree:: :maxdepth: 1 :caption: Appendix changelog .. _colorclass: https://github.com/Robpol86/colorclass .. _colorama: https://github.com/tartley/colorama .. _termcolor: https://pypi.python.org/pypi/termcolor .. _ANSI escape codes: http://www.tldp.org/HOWTO/Bash-Prompt-HOWTO/x329.html terminaltables-3.1.0/docs/install.rst000066400000000000000000000016711300101162000176360ustar00rootroot00000000000000.. _install: ============ Installation ============ Getting started is pretty simple. The first step is to install the library. Pip Install =========== The easiest way to get terminaltables is to use `pip `_. Simply run this command. .. code-block:: bash pip install terminaltables Latest from GitHub ================== You can also elect to install the latest bleeding-edge version by using pip to install directly from the GitHub repository. .. code-block:: bash pip install git+https://github.com/Robpol86/terminaltables.git Clone and Install ================= Lastly you can also just clone the repo and install from it. Usually you only need to do this if you plan on `contributing `_ to the project. .. code-block:: bash git clone https://github.com/Robpol86/terminaltables.git cd terminaltables python setup.py install terminaltables-3.1.0/docs/key.enc000066400000000000000000000062601300101162000167140ustar00rootroot00000000000000?+ –H k)Y 7>"Ÿe Fwr8yt"{oŞb`WxD}wvo@ϠMQQ?Hg:ic2%(:Di9srAӐ+b}p$yH:V~{8v9|{G-S7`ixp}-zvhq=s :CH&BjS"\Q󇘪YY)O;2\]n,HT ȿ7Lko!Ya6=}w_X9YTkh eX*a!o)YB쑶L ZAkqX>"ՙ*+qF&vfvNi⩽ RmVr&eߍq:f݂0l2(ĖIǭ/QэA4[uB@GkDD!|3K YovFrCO3aqS@`[ޜX K~͹ SYO$Q] N"ϯ)O q_¶DAsJ+Xm " x^"t)o]ۉw~ ňH1VY0"i)V (eܹmy⭜"&c 3p!w_Me]W&'1t1nQ*-.]& Vz Ф-$U .TA]c\)xi* ;p4,#JϿ : bdRD&)=tA}#_c $HC\I !%YCqomv]hJ&S57i(rUPjIu!(kaPO~p-tfa<zP vۢd [גjƣQaēk&MsF$hCn7QrlUHcoٍh|ƾLD;gLx iQBgCFv,a=[L{U k$ SBPFg*W͚;mO7\s >n]zpZ1< L, &\5﷿tZL h;A7Jt1{Gn!qgBQK~g.6NWphJ]K)h S>/;f =޶a?PZJJ?RUwt*X+*4"_:t|gE֏702cw\?Nz{L\3mF0Ի\E{qi>ْ=dm|ᩑ2⨞E@0&+Ǿ$ X-)/o "'͕ˆ8S`}Uw|RP tNȿ- ɾ$DfU<Gvgt1J iXz2|Wz$N\d<4W~kTKԸ0ݧQ\W"M?y; paGJ]m4w]*Cd+/]{.?>62~-|Y[տ6I et$yj d9J\{<ɣͳWC,d`01wXp:گ#>Spzk^t6$tMs4uq RWG{$L"XiWn/}۳<.Li6=C2pFAp(Fc#J; 80rNCVHA-\(2D[0mZs0 m18e`4/$ Z *C qۃ5[B\? vklM-ߋuΣ1#n{Ser'fU [H_0c⥤LDߑ!j!N ηRp>{g'b ǿ ?sS-H}I`r#nweo?}}_hNY?'l-I_ ,"F4r43s.LOɲ`cEE>^c3(+d]RV^>o`y: 2hB,c]laDY_@d+B8 )5O+=8vg@;jRm䋌85(AApUL</_K)*xqşYl곛J{-񩧃CkȰ0l7=Sÿ#{b['0M7# {VW/.==Uۣ%\9\==/i3poF;/N5yVYY.:s`_$9 8EXcf>E:.UUb~85@#֋ѡ:ro߳~-6b *βZ29Cnقý"}O*hNterminaltables-3.1.0/docs/quickstart.rst000066400000000000000000000101341300101162000203540ustar00rootroot00000000000000.. _quickstart: ========== Quickstart ========== This section will go over the basics of terminaltables. Make sure that you've already :ref:`installed ` it. Table with Default Settings =========================== Let's begin by importing AsciiTable, which just uses ``+``, ``-``, and ``|`` characters. .. code-block:: pycon >>> from terminaltables import AsciiTable Now let's define the table data in a variable called ``data``. We'll do it the long way by creating an empty list representing the entire table. Then we'll add rows one by one. Each row is a list representing table cells. .. code-block:: pycon >>> data = [] >>> data.append(['Row one column one', 'Row one column two']) >>> data.append(['Row two column one', 'Row two column two']) >>> data.append(['Row three column one', 'Row three column two']) Next we can use AsciiTable to format the table properly and then we can just print it. ``table.table`` gives you just one long string with newline characters so you can easily print it. .. code-block:: pycon >>> table = AsciiTable(data) >>> print table.table +----------------------+----------------------+ | Row one column one | Row one column two | +----------------------+----------------------+ | Row two column one | Row two column two | | Row three column one | Row three column two | +----------------------+----------------------+ By default the first row of the table is considered the heading. This can be turned off. Changing Table Settings ======================= There are more options available to change how your tables are formatted. Say your table doesn't really have a heading row; all rows are just data. .. code-block:: pycon >>> table.inner_heading_row_border = False >>> print table.table +----------------------+----------------------+ | Row one column one | Row one column two | | Row two column one | Row two column two | | Row three column one | Row three column two | +----------------------+----------------------+ Now you want to add a title to the table: .. code-block:: pycon >>> table.title = 'My Table' >>> print table.table +My Table--------------+----------------------+ | Row one column one | Row one column two | | Row two column one | Row two column two | | Row three column one | Row three column two | +----------------------+----------------------+ Maybe you want lines in between all rows: .. code-block:: pycon >>> table.inner_row_border = True >>> print table.table +My Table--------------+----------------------+ | Row one column one | Row one column two | +----------------------+----------------------+ | Row two column one | Row two column two | +----------------------+----------------------+ | Row three column one | Row three column two | +----------------------+----------------------+ There are many more settings available. You can find out more by reading the :ref:`settings` section. Each table style pretty much shares the same settings but there are a few minor exceptions. Refer to each table style's documentation on the sidebar. Other Table Styles ================== Terminaltables comes with a few other table styles than just ``AsciiTable``. All table styles more or less have the same API. .. code-block:: pycon >>> from terminaltables import SingleTable >>> table = SingleTable(data) >>> print table.table ┌──────────────────────┬──────────────────────┐ │ Row one column one │ Row one column two │ ├──────────────────────┼──────────────────────┤ │ Row two column one │ Row two column two │ │ Row three column one │ Row three column two │ └──────────────────────┴──────────────────────┘ You can find documentation for all table styles on the sidebar. terminaltables-3.1.0/docs/settings.rst000066400000000000000000000052271300101162000200310ustar00rootroot00000000000000.. _settings: ======== Settings ======== All tables (except :ref:`githubtable`) have the same settings to change the way the table is displayed. These attributes are available after instantiation. .. py:attribute:: Table.table_data The actual table data to render. This must be a list (or tuple) of lists of strings. The outer list holds the rows and the inner lists holds the cells (aka columns in that row). Example: .. code-block:: python table.table_data = [ ['Name', 'Color', 'Type'], ['Avocado', 'green', 'nut'], ['Tomato', 'red', 'fruit'], ['Lettuce', 'green', 'vegetable'], ] .. py:attribute:: Table.title Optional title to show within the top border of the table. This is ignored if None or a blank string. .. py:attribute:: Table.inner_column_border Toggles the column dividers. Set to **False** to disable these vertically dividing borders. .. py:attribute:: Table.inner_footing_row_border Show a horizontal dividing border before the last row. If **True** this defines the last row as the table footer. .. py:attribute:: Table.inner_heading_row_border Show a horizontal dividing border after the first row. If **False** this removes the border so the first row is no longer considered a header row. It'll look just like any other row. .. py:attribute:: Table.inner_row_border If **True** terminaltables will show dividing borders between every row. .. py:attribute:: Table.outer_border Toggles the four outer borders. If **False** the top, left, right, and bottom borders will not be shown. .. py:attribute:: Table.justify_columns Aligns text in entire columns. The keys in this dict are column integers (0 for the first column) and the values are either 'left', 'right', or 'center'. Left is the default. Example: .. code-block:: pycon >>> table.justify_columns[0] = 'right' # Name column. >>> table.justify_columns[1] = 'center' # Color column. >>> print table.table +---------+-------+-----------+ | Name | Color | Type | +---------+-------+-----------+ | Avocado | green | nut | | Tomato | red | fruit | | Lettuce | green | vegetable | +---------+-------+-----------+ .. py:attribute:: Table.padding_left Number of spaces to pad on the left side of every cell. Default is **1**. Padding adds spacing between the cell text and the column border. .. py:attribute:: Table.padding_right Number of spaces to pad on the right side of every cell. Default is **1**. Padding adds spacing between the cell text and the column border. terminaltables-3.1.0/docs/singletable.png000066400000000000000000001267331300101162000204440ustar00rootroot00000000000000PNG  IHDRb5 pHYs   iCCPPhotoshop ICC profilexڭgTSBKЛ JFH(cHP "RPD2ĂmPl'Ƞ*Gxz9s @ @H* e'$&r@&DFL9pˊ+ U~j~/9%RL ˤb)V LIBbv̔I LIL6 @qt{`ҥ4&؈feg/Yʿ3E+x.@䈅ll eH4f- U(en S! b^_S,ˊbdz@ʉbɒ(H87LQpjN@ 9S?ŹS:OKdQi@sgI3b3$(S(V^,UxA ='7ZW*QܐiH06Ă8-A84uoxD!eT6GijŶuHHLbOwE,WnZKhh5L;@e`&!؁7@D@ $"AdA>b(-aA##',\pCQ:@4=D @($IF"CuH)RT#&r!dy|A12Q>h(.DѥhZnB+Z چEwP9b,L\0?,K0 +*Zz[{}p gsbqF-Wji_ Ty-=;{VgJy~L~AAAcCaaaᨑQQQcq&&&MMM5L9yͦf^fKjn]̳w߰@--2,j,[N]}\gf귢YXZ5[ Xì ۭ_64{mm6mڪنvھݶگ`#1qc7'g'SӈsN~KFKxW_5']?9Iݎn~}x9 zxp=y=ٞɞ{=^^\Zކ|z>>>}^J|~s[T< 4Ll r Z 8MU!CiѡաO,$ahxHGsG@'b[Hȥ#̋W3YmT~TO4#zq11cƚb5}/'NXp5Q+QؑDLKO0 ]hpˋ Zh2>9>@Wn;Iٙ2{#e<҆=ҷdxeTd o23d~̊jȚ [I'Dj,%K/[nK/Js9RT,&3 ̭,nE˯XaWVVvXjjdu5k 4Z[MaYu:t XXRܿ}q? ~`j~ɕRҊүydSO6nvڼ{ ahݭ^[Tok+g߾x =;(;d;aUFU[VgTߩiݩsΏnݲGgO/{{ VkR[QG˭{?n.?7k՗k57975>m5\p!C-V-ZY_{$HQ-nj<8^҆hmhw$v9yW_N꟬9~jigΌu^M?;ؽṄs;{!¥qe'\it9vש7:u-[osn_3Nػ  ÂGG%UW<~Rr'kO> #珯CE*=o>98rC//_f׵фѡ77o7|}Xؓ?|sϗ/Ǘ}%~f{Gb iioɎ d '{48uuT({Dzxjox59iv^v|Ļx9o@ ޕvAGG:Da 8:ciTXtXML:com.adobe.xmp 2016-06-11T22:30:49-07:00 2016-06-11T22:50:07-07:00 2016-06-11T22:50:07-07:00 Adobe Photoshop CC 2015 (Macintosh) image/png 3 Display xmp.iid:31bd1695-39bd-455c-bfd5-4518a8464330 adobe:docid:photoshop:638d2ac0-70ed-1179-ac3d-f90aca98f8a3 xmp.did:9bcb1f02-1587-44b9-bc45-9897a7c5926a saved xmp.iid:9bcb1f02-1587-44b9-bc45-9897a7c5926a 2016-06-11T22:31:50-07:00 Adobe Photoshop CC 2015 (Macintosh) / saved xmp.iid:31bd1695-39bd-455c-bfd5-4518a8464330 2016-06-11T22:50:07-07:00 Adobe Photoshop CC 2015 (Macintosh) / 1 720000/10000 720000/10000 2 65535 501 172 Q cHRMn'sxp1\'%h)IDATxy\TU30.Ȧ"*kY`j baЪYk%ofVJRܲ,! [] DTP@Eu`d%̥|2 60 J u@* $H$WrnnnM{_18t.( @y,%Di:"[15spǠj|98V@o>Qn# Ԛn;S (`@lN.o< 0MKOOo<'Eնʹ= [[cyYw*h Pur.pppDig(+XGu?p$yQ'{{lM())f =yq;G{kJ@(.*@Y(((J ]C<7|z|d<=`{Sim)5/ΣͫGm`([[)qVBvMC\0lboNJy ///cOѡl 6|*֞QvGGlϧr~fOJc6hΨI#pR>.3eQD\ P)A LFa<6gy?K57@큶 |<&Ac00׆6wUAO>1T'd?a}?9G/;=| |B8tgPDbB)>5ntU2.?Gy\ΡC888:1r*:}6gkLH~bQ/ycPЅz (;KL0YPYiʠ +LKJR JPR`gzo\ҁwyn;Y-8Wk/ z' o>-Goƻܦ>o sƤ{X}ꠞ?܈%=h6:>Oz[Ѡn φ5z^lNolp'}Ӟ,oAwo [ ,q{# A 2k"<&uc2ˆgPN/ɗPS쨂Rgơ> ?nZ('_A<=*B`}ٌuI˃zCPxC (lm' l۾枠^S2hDޱxzv֖crKG* M[0+LCJJ aj{v")bn`ceГxd7x1I< 1^ A|RVu8laʭt[2d4ڕ1\HOWb0\+A>yvb_yY^Xq[ͺ Sϴ6ȶilʏgfK"5ҩy^e(M7mbC0gRcvYL{}zβzWtEhKҞU|x;Kra=MNknYβ7ʙ ɬZxSEh| Fw1hفLuRb` ܻ,` znd`@_!i K BGqI%;*f-bW|1X4k(6Vy𹎀t}itƀM 7_AZ9J서)l=uE ZCpuPڭj*L+ PJ kc2zV */kI{W*= 4~ܕɯ1;U o+Y6B $l3P0ql# :v+k~J qW&d?^beüi3VUTUAFcE"e5r.iF"nɴ'1DrĩsGq*pSfӽtPvxo fq &㪪*=;֦A6EiDUY)(<:wt`/2O/3{Fn>t#Rz1^*-Ekr>fjZp9G7@L [yfsAZ^)mi>^~dL+ 1N&z:m8 qjH.KOʯ B c/a Lx4*O CY*J;U_۳66SFe8;^뭫¸SoѦ* F[߭LOaJ[AiR4g`JF, 6v(  B :І TƲv8 J̓4`l]a0`6`0JJLRٸ"rJrJգe(o¶*EM|"rղuOLCmԱYV>su%RAqzk xsz D9PRRPҘƑ`m_W0UJxРݕB/7멹 pmWRzpňze}4h CyI!iwxc ^솪{ Ym¢_Zec9*JHܻ]B0ȥEKqup=5Lw߃pumtv}BH=)eLPkT^;'g_rDݏϔ?>sS <~Tr l+K Ja|OZcW^hհu?k}}ywaa5.kџ^; :PghP`M{'m/yV cmAEOrOx6FLrqpmap&{{@=Ny9X󃩽&v*]6;MFLgvTb/W7jw <Ǭm2pj]}newt6`oU6>rp.}:cMoW+y V9'`u(3Ơۙ>}3ݽy{/;un$P{bh;J7-4#13sgWY"7vg FF#笸K狣TվOSc`zbzoU^66+b1s *eok˜acP7T5 ˴Nec + eBՐ!m_9%zlpk]ԗ(+=v6sr` F(i=(V`%0Jze +% м@{U$(H$=z43 0}-VRI?H$ˌ &V&L"HdPH$D"D"H$yfi D"Hq'))IZC"H$+Ν;ر#;wFTRQQNRZZZʊ*ر.H$ckkJ Z-NNNQRRMRP*TUUU?F_:H$:A%D"Jynj9&*+BTV/VVV_|P6m +WaF(ox/ZIӀ,ҙgMdUPG1SQ^f̏WPSQ?[ \{aϥh6t-Uh<;uoZk*KǙ/qҺUEBy6tHB,. [~/UT߳j9l7,jNl"~ avgeojbT"GxꩧBjժVWg2FgJZ% 9vl {5ޜO>O^^E%OɾWhz>vw8zmz߄3>;>5O~+|h-K׻MraW+8 E[ڽБy\8|O0yč՛L]=/1z8uxVhëV愛++<|?.aRS %:1ыH$-cʕ( uƌC`` #F@2j(F`]Ŭ vnJ$/M䥀ٿeNJE%R7k2eI7/dΚd-AQ6l1?c_J𒃌BeӠNN/ʞ_YgwugCVV:ɩ5@@(щHMN&=lH99 \Mp_~gw1%#_(m;C.c992k>g`t>5m԰C' 6GѯfBrNݹig7GvCw% S}ٝQӏiF@č_s Z=Dd],оU\"Ů]dϞ=DEE{n z{UcwPVFЧYl\g£xh6EaUo8t{בoßTz@P# S0uUǿop|5&$/Sʶ^pQpn E7130>x+p7y]oP ci^| p7wxz]0Cs>QwMAUrIzmRNh:c@(pO|PۚƟ#(EEǹܐNNͤCA¬:~(u1NR^3ѡvI\!"͝qŽ.#:M`{-7`#!/7zgWUUQ(  o\ iӦzJΈMb&N9Z$j5wLcEj:KEj͒ AKʵA"8H6"((Hh˜K;ku}5}اKg:oTsr: }&3vp6Zk&nxֹVZn#}/4Et""Wqz.K/wFt\r|9u Ku~ ml ZƮ;w{koc]nA?xE.WSOiӦU޹s8x";;[9sFʼnxq)*DAA(**"--MIYڦ.,Zf6:ݟuV ȆP  u 3M{ϝvqtvnF߷ڢ,*sdFTfF̳) ]ٿb̿'p.uwM_^ī ރ}3 Kl۶ ^+սs[[[j5*:C/UUU2x`zU"WA6Șba'%+kl @FF...( _X^4EEEɠ.H$eUD"Hڠ<#H$DrliIJ$)v5=qCi.S-ǜtdFV 1.׺Dqf_ͻ!z4W"D"NnNZ`LZ0ϭoEbxxPXr1gcv.ˎe ^=S"&U)j}BXniCX0;XD"HNq}L Hşa:z96M"-?}0 _=ڶ8tQAnѮvY 0EwofN9XGٜxX3r0W?Kһ3xI$ƦBʪ\8/^Z.m#|)t㈎@B{ߠ݂=}vA?ju%q\$DbGG+/ʙ[t:]/E:8wB?iZu!/viq(^8H@؟uV,"`8_LeAuYl(L;$VODWX$&}goǙ߯ Dᕖ:ffXdtb]H1s!kd۪'X"XȊ,BEHp4t;[f8蒯Y%p .׺r\LFF(++墢E ^_,77WNj ߎ'})Y猿"#VedgXKcO\=W=´}Xmvj3`ZEEI0JdGaD\\0f UTd:"9[RZ[{5 ߲D"HZJ<3H=t>wt'oc687v.]v>*2j <܉wbHk.\5Slp pCϞٓώdsI zf`b3 DcvH$uԅ^4Wd%REzVJɩlvӱшPM_^52q`3޿b4' f*roۛ/͔&]p9eRQj7YcQpd=&s V/"~ ;f>wM]k}s,6: 6hw͌)Ȝz95?8gu3M1IiigeadQAu|y,Gbrs,mf5u9ə:ͥ`ρPϙ#?u=4[YREp9|bKގ/BÖ@QgklGf5zΔ\S\k~8s11]2ڹ9X(e1/a }զϗYFho<,dtKW?\-5 a./,IJ/brBkZ4o??hLfg9=bLxz99laOZ6Y>ҭ/燗C|S `!͉jAfR_gb=O/,"-o~M[Fo/Ϫ3.r-DʯU4HoWо:5X>$|* t5}4k5DSG"{b#x0xf:^%z%abxa9K'!+zc$|LGl҃#r6Ӆ/5Q)̟k~7'w|Kk29Y[#czPwûq<~0N?ll Oy{b5O1?}>]So~ `V`D, wohJȆh8yQ^!󐢙7Os-DrϦ`٤2:fƞ=Fb16Ƃ5; _Է Ь*Ϊٛe&{YSlro{M~n1L^KG&Խrz% YmޞΎxeϋKoQPo K"8i+f}YSHx BpT-߽#rXKXd9a6ZYȚ4JFA1ձ9ŀ`gpm1Y?f_J7ÇC&i[.Dz_k M(щy^ToLSaGNr4G~κ@q%aI#&y9a k 6=]noO厶!]=b,Fott"ۃ7]q=syڠj擪ӑFZZYY'?/N-~O`%H,N4hc= @#b2- rٲIZf8|P, LzlQU+/3#bNQ Y'X&xqj+6(^k{Ք9-E]~ۉ= ?$v'W]9=ɥyjoEzVrsK3L^/z}\V~WL}q`r*i/㚹fYY$'r6n;z%N%|\nVڣ f/E_oFmh w9wY ,O|&LzV:[/jkCrm$%-d:|y-U?NzV:{W1sןPf:O:&cHb_Ĝj_6KBT t|G8Irۮ鞩|JmL,M& a͑yտmA ySK=@ԠN߉onj->> n.ǿ঎ޕ;}/K}x7lk{PăiWdUPi]y Wk}|Ԁ67j>->pct%Wvݕ߇uby=)mNPjOaSGJʪV ~>f6Wp4UGʖ\\s͖c2َs:JϘzeU*,{sӋ/td]AL`#mʊoFq]Ԭߚ:'^mԧ>Bz\}|cݺujoOzQK!3-pL8*%5[:]z2 'T4:BnbDZ쬚;D{uu ͙F)wV4E~3,+)w2}98i}8uAr#_g蛻/þT{ʲO/p~N !d3nZڨ7ؓDCu| 6KMoamD\\^n; l1=z;MO O?XPAP11@`=uPSnMۍ=]n{R*9I+*Bgs.TqNbO"4͖S_]gT ~.阔]TV2/fڜJanꫪ+yԜ C~=W?KzꢢN'uNꊌ: bs'Z߉!aGeDޤ}gT")`H}}b3̳w/os-;SZ/(V5eA/#(z~'.GW~msQmPbKbO.;)L%̟%KQ_Cnk?΍K͕CgOzgG9$=g;QrFŚ]|r Yz=;򈽁AV_ɋ]W3M-!_xYSҼb2T;g`'f1JaAko͞jXNe)fV_}|:p U&(HiFVsY_$z"4A"8Hsv_3Z$>$G*f7ݯBm=?Qrt`zeo,${p{Kl#thնvP;\l/uVڀޟ _iS:IIIfNzjdddB@hrQkBeQQnnnD"\/Ƞ.H$ D"H&[[68α:"N&(7"c:ChC7Y[F%裢"VHH$27EQqmsޝge&C7>ciŒ3<)J_\9=%?F#+U͉Ŏf? 4D5s.sֵjWz|%Nvf=?Xn-bU\"當V?'pid!=GT:֞cf˩9)IJvD.;Ϲu`ݗ]~[j{%N5wPbۈV>ӆS[y3mD"&%κǑQ1yw0obffoob^\&s[{9u< z;^ek|{f슳LX@lco@Sr#J-1{ /7ɧx"n{ŏq>+IJ}Y==RVZÀ'p:oLY*tFz ezԮ5<\()fn-h7f]3fyvů zliXک̮&hD\?2`KgXm)7pg:{}夥UO*Ai.ӌs?20`3t~j @۹-]hpo񌂡c2ߑGĦ<6n=zOT vEPTX-VK$UP~.Vowֿ&QPLIyu| m)W\0`6_=>ۡ hfr@wtPRD"D㉸?_8/G%KWW"V]U&RklX86Q qlV1ώiSR(RWό"{Yxt'#3qf=ic<#kFbzw%W R]=xug*Z[YU|m3_vJ\=ڊ;Z 6j1<'"Mr-#I$,mD"HE"H$%D"Ƞ.4&k6v6VKtlV8К 5E@8|X$eMBOdLzE5sŁģ"j.q09CE-WG+~κ ̅kuf#/kw$-&_<6S"j.q w1Wu щytΚ"1?_dee,_T>v4 ˂V;_Ds~OGubYP+VXgTg{[y8S6;ۛ g+2tGEPB59C+y ~!.NdݛcŒ\?v6K;[^.&9YTPowE@Je@`W]gX1Y"7MxJ]/?Sp2+?0ko_[?Sӑ 4FzV"BX0;+B`X?D2.鞩|2|w=deZِo$LN@W|}345B?`G͖SwlYIg6r/);Y~a;<ɩ&'Ζ@KoMZn\SVgmligvn?ҮZ|=Sף!$8wa1S\k98s1'ZY,='W.yyB7vo"g߉Hx e1/a }I)mlV[†1],.mSYާ6Ϝ835.yi|yaOoZÚ5oC/8$O+^8]܏|+ D7|->/_Cm,Bֿr.zv%Mʲyt{+c7нoĺ3/jhXbc=/#U#3vhl׈{|e9-=/x*;8* 73b*y&rz7g렦,d]3wFOu9@YNN*ƃP=e'{ gg8vl?wګQe6+M䊧Y|{T?;탈 bV.1ߞ1mS`z! ,S2ȈrFǞ$j*K r8G:TI`ӗ. cA11@a;7)ۡ:3YCGo@Y9Z6ݔW_mv:hr}$ tgtCv+ m:]XEL5l3ץ,N;{IRX_g^lq:?Q0_f8zF1 4]&rtrىK?eu!=ꔿ}ZD/y{cGrXlODLVXmxMRZO55e͒],w5VH.^~8fyjKE~`K'iM-|ck&7fXՠ:;_g媳+o{%?W\"lId$V֘loh`h֌9.zw,,:ž}HJK;|M}kr .aHÿ&+Qvq 1 62ސZcDvds<6'\ϞF1s.쟍Lh7>T?ϖ@ Yv2o!+|l=a͜DnZ^¦g4#1>ߍWb"?os4{IV&_2uRz7v=#+E@@;3sKV5loO,3dm?YK+N>LRZ&I28~8g8`-Ӱ)L z2d"ZԮ3L~iQM|Ru:2HKK#+s^7穰#'9K%uA+>Swtg?I&qKE+ZéH;z}ӱ٫pܲU ^:rlýDݎaDI$)7iVΞ5`2GudzA/W<-wlN<ώY9Nk2XMj%x\wK/_n#Ƞޔk$glt%$5ۿCی޲\g7Lٓm!&t+ԎRt,@{qDi 7Q:Bw*(.0؝ޯ:\r3~sAC#/OYz{AB~?#ߨvGi{8D-wu׷]I.UOz-/a6@ j{zi5i8Ʌ)%V~rWbSZ=(Hā .=HPd{V\@A *ؖ6:b:GI_LسTv]Tz(QZa[e%h>.mSd'UTI까ٓ h&ZAz<=j[BW 4ZIȖ+ ݗ5=t+Y9jg^'^Y[:v沍I$So2xIO=r)X=p!w43jys|!Q/tGv5~J*ƪ8!Ƃeçv6F`bޡqGb~Z>~3e3mL"Ƞn*m:uQfPfm(*ϩ,+T驝PPUGBJDTPP.qWX+VPqpy7L6²b=jWZT_0 {!wkO e2ۮu<{D ]| j2GЇ_ |1Drj.PfkϤs_M}po)f_K0ܒGں .2Sxrc؄yBl'x.jKG'D~Kݗ\ __ sϵͶk==ÿTM^yuV)LJ ,cҊxkT J~^6d]"Q`LBRRJ:GG&Tʢ "K=9?ZNtXA[yRAiS[ J#.irW4R_0+ ~.aÿKm zs%/TP튈-CwL*\י \\\P(( s\GQQnnn#=\2Lɔ^!_"Ƞތ~M+:GH$DL9)$D"4M"H$%D"Ƞ.H$DuD"H$2K$Dr]:NZKrgD CD"\wD"HdPH$D"D"H$%D"44D"H$W/PXȟ₯/2K$DrPRRB\\eq7boo/DO1eʔ׬Y##HٳuE*++9{,u3uD"HR.7u]{f724 ̂)qD_ eڽ=pT(Jˌ>q*ӟg˶ko7 mFk4Ȋ:ry75xpAEu8i]q"wM!ۆS:XٝAaK {Vb[-fUE#H.Y}љ҄V4!ۮ#Xau&ԛˣ4IpqR21[ףt}˸ܥѻoE8&y- y[GClQ\:$WvŮSP gSqqa.yyyO>O'a9~7CڠЗs~@M;18;+QePdwGugN?:I^ !7~M+`㚾w?hUmM5C{) Þxzš#ػ}Sއ"8icO*dsǨ酗#!]ϛN2>ҭ/燗C|Sy9fP퀍 ۶zw¦ic"]FK#9xT@m}x'$+U;m3 MD֊oQt96a$yzT^E!\T1[Cx L( &M^@l"/GuXڰ#y(>!C,-3v ޤs&М(xԝppSP\R^źDJ;趦4JQqnG*7&h3{F0ĢJ1vl̫W%Ltt]"\+A]{^SxH6k kӾ 45D5dat gFLRR\3߹DWBHA~"-~;]Z.vd*=o}er \FeڟȠ]?;q)N eL7'gP9sa뤧X܌Kw@dӜdRR.8'9ONcimq4Pe@FȾ wcST rdbt':([Lz~ܙ3.=ㄨғ v֯MIDrPߟ+| cU oeLBKDloi&~|AOU.fm|EӅDlAxMĶK>qCPP3≬.sr,8Nyg;^vGؼ-:: yiVoNNg#Mo;8|r~nWA,N)sj\GRwzǝ.|Y@Qc{Y8W$}g:}k-ǚϷO0fwڡ?ٹ#}j ϑaQ3rD/ptqdi<0=r% %_ 7|sD"i2K$ȯ5yvPwqqNr1,meDoDwԩEEEdjSNWze|H$jޞ={Ҷm[J%Jmҳg:O:e :''KD"H|`7Dܹpppm۶.H$5įkzdggDǎQTJD"\$&&c+;fSD"HI @~WDIENDB`terminaltables-3.1.0/docs/singletable.rst000066400000000000000000000014511300101162000204550ustar00rootroot00000000000000.. _singletable: =========== SingleTable =========== SingleTable uses `box drawing characters`_ for table borders. On POSIX (Linux/OS X) terminaltables uses ``Esc ( 0`` characters while on Windows it uses `code page 437`_ characters. .. image:: singletable.png :target: _images/singletable.png Gaps on Windows 10 ================== Unfortunately the console on Windows 10 changed the default font face to ``Consolas``. This new font seems to show gaps between lines. Switching the font back to ``Lucida Console`` eliminates the gaps. API === .. autoclass:: terminaltables.SingleTable :members: column_max_width, column_widths, ok, table_width, table .. _box drawing characters: https://en.wikipedia.org/wiki/Box-drawing_character .. _code page 437: https://en.wikipedia.org/wiki/Code_page_437 terminaltables-3.1.0/example1.py000077500000000000000000000017761300101162000166050ustar00rootroot00000000000000#!/usr/bin/env python """Simple example usage of terminaltables without any other dependencies. Just prints sample text and exits. """ from __future__ import print_function from terminaltables import AsciiTable, DoubleTable, SingleTable TABLE_DATA = ( ('Platform', 'Years', 'Notes'), ('Mk5', '2007-2009', 'The Golf Mk5 Variant was\nintroduced in 2007.'), ('MKVI', '2009-2013', 'Might actually be Mk5.'), ) def main(): """Main function.""" title = 'Jetta SportWagen' # AsciiTable. table_instance = AsciiTable(TABLE_DATA, title) table_instance.justify_columns[2] = 'right' print(table_instance.table) print() # SingleTable. table_instance = SingleTable(TABLE_DATA, title) table_instance.justify_columns[2] = 'right' print(table_instance.table) print() # DoubleTable. table_instance = DoubleTable(TABLE_DATA, title) table_instance.justify_columns[2] = 'right' print(table_instance.table) print() if __name__ == '__main__': main() terminaltables-3.1.0/example2.py000077500000000000000000000052301300101162000165730ustar00rootroot00000000000000#!/usr/bin/env python """Example usage of terminaltables with colorclass. Just prints sample text and exits. """ from __future__ import print_function from colorclass import Color, Windows from terminaltables import SingleTable def table_server_timings(): """Return table string to be printed.""" table_data = [ [Color('{autogreen}<10ms{/autogreen}'), '192.168.0.100, 192.168.0.101'], [Color('{autoyellow}10ms <= 100ms{/autoyellow}'), '192.168.0.102, 192.168.0.103'], [Color('{autored}>100ms{/autored}'), '192.168.0.105'], ] table_instance = SingleTable(table_data) table_instance.inner_heading_row_border = False return table_instance.table def table_server_status(): """Return table string to be printed.""" table_data = [ [Color('Low Space'), Color('{autocyan}Nominal Space{/autocyan}'), Color('Excessive Space')], [Color('Low Load'), Color('Nominal Load'), Color('{autored}High Load{/autored}')], [Color('{autocyan}Low Free RAM{/autocyan}'), Color('Nominal Free RAM'), Color('High Free RAM')], ] table_instance = SingleTable(table_data, '192.168.0.105') table_instance.inner_heading_row_border = False table_instance.inner_row_border = True table_instance.justify_columns = {0: 'center', 1: 'center', 2: 'center'} return table_instance.table def table_abcd(): """Return table string to be printed. Two tables on one line.""" table_instance = SingleTable([['A', 'B'], ['C', 'D']]) # Get first table lines. table_instance.outer_border = False table_inner_borders = table_instance.table.splitlines() # Get second table lines. table_instance.outer_border = True table_instance.inner_heading_row_border = False table_instance.inner_column_border = False table_outer_borders = table_instance.table.splitlines() # Combine. smallest, largest = sorted([table_inner_borders, table_outer_borders], key=len) smallest += [''] * (len(largest) - len(smallest)) # Make both same size. combined = list() for i, row in enumerate(largest): combined.append(row.ljust(10) + ' ' + smallest[i]) return '\n'.join(combined) def main(): """Main function.""" Windows.enable(auto_colors=True, reset_atexit=True) # Does nothing if not on Windows. # Server timings. print(table_server_timings()) print() # Server status. print(table_server_status()) print() # Two A B C D tables. print(table_abcd()) print() # Instructions. table_instance = SingleTable([['Obey Obey Obey Obey']], 'Instructions') print(table_instance.table) print() if __name__ == '__main__': main() terminaltables-3.1.0/example3.py000077500000000000000000000022701300101162000165750ustar00rootroot00000000000000#!/usr/bin/env python """Simple example usage of terminaltables and column_max_width(). Just prints sample text and exits. """ from __future__ import print_function from textwrap import wrap from terminaltables import SingleTable LONG_STRING = ('Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore ' 'et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut ' 'aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum ' 'dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui ' 'officia deserunt mollit anim id est laborum.') def main(): """Main function.""" table_data = [ ['Long String', ''], # One row. Two columns. Long string will replace this empty string. ] table = SingleTable(table_data) # Calculate newlines. max_width = table.column_max_width(1) wrapped_string = '\n'.join(wrap(LONG_STRING, max_width)) table.table_data[0][1] = wrapped_string print(table.table) if __name__ == '__main__': main() terminaltables-3.1.0/setup.py000077500000000000000000000076151300101162000162270ustar00rootroot00000000000000#!/usr/bin/env python """Setup script for the project.""" from __future__ import print_function import codecs import os import re from setuptools import Command, setup INSTALL_REQUIRES = [] LICENSE = 'MIT' NAME = IMPORT = 'terminaltables' VERSION = '3.1.0' def readme(path='README.rst'): """Try to read README.rst or return empty string if failed. :param str path: Path to README file. :return: File contents. :rtype: str """ path = os.path.realpath(os.path.join(os.path.dirname(__file__), path)) handle = None url_prefix = 'https://raw.githubusercontent.com/Robpol86/{name}/v{version}/'.format(name=NAME, version=VERSION) try: handle = codecs.open(path, encoding='utf-8') return handle.read(131072).replace('.. image:: docs', '.. image:: {0}docs'.format(url_prefix)) except IOError: return '' finally: getattr(handle, 'close', lambda: None)() class CheckVersion(Command): """Make sure version strings and other metadata match here, in module/package, tox, and other places.""" description = 'verify consistent version/etc strings in project' user_options = [] @classmethod def initialize_options(cls): """Required by distutils.""" pass @classmethod def finalize_options(cls): """Required by distutils.""" pass @classmethod def run(cls): """Check variables.""" project = __import__(IMPORT, fromlist=['']) for expected, var in [('@Robpol86', '__author__'), (LICENSE, '__license__'), (VERSION, '__version__')]: if getattr(project, var) != expected: raise SystemExit('Mismatch: {0}'.format(var)) # Check changelog. if not re.compile(r'^%s - \d{4}-\d{2}-\d{2}[\r\n]' % VERSION, re.MULTILINE).search(readme()): raise SystemExit('Version not found in readme/changelog file.') # Check tox. if INSTALL_REQUIRES: contents = readme('tox.ini') section = re.compile(r'[\r\n]+install_requires =[\r\n]+(.+?)[\r\n]+\w', re.DOTALL).findall(contents) if not section: raise SystemExit('Missing install_requires section in tox.ini.') in_tox = re.findall(r' ([^=]+)==[\w\d.-]+', section[0]) if INSTALL_REQUIRES != in_tox: raise SystemExit('Missing/unordered pinned dependencies in tox.ini.') if __name__ == '__main__': setup( author='@Robpol86', author_email='robpol86@gmail.com', classifiers=[ 'Development Status :: 5 - Production/Stable', 'Environment :: Console', 'Environment :: MacOS X', 'Environment :: Win32 (MS Windows)', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Operating System :: MacOS :: MacOS X', 'Operating System :: Microsoft :: Windows', 'Operating System :: POSIX :: Linux', 'Operating System :: POSIX', 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: Implementation :: PyPy', 'Topic :: Software Development :: Libraries', 'Topic :: Terminals', 'Topic :: Text Processing :: Markup', ], cmdclass=dict(check_version=CheckVersion), description='Generate simple tables in terminals from a nested list of strings.', install_requires=INSTALL_REQUIRES, keywords='Shell Bash ANSI ASCII terminal tables', license=LICENSE, long_description=readme(), name=NAME, packages=[IMPORT], url='https://github.com/Robpol86/' + NAME, version=VERSION, zip_safe=True, ) terminaltables-3.1.0/terminaltables/000077500000000000000000000000001300101162000175075ustar00rootroot00000000000000terminaltables-3.1.0/terminaltables/__init__.py000066400000000000000000000011661300101162000216240ustar00rootroot00000000000000"""Generate simple tables in terminals from a nested list of strings. Use SingleTable or DoubleTable instead of AsciiTable for box-drawing characters. https://github.com/Robpol86/terminaltables https://pypi.python.org/pypi/terminaltables """ from terminaltables.ascii_table import AsciiTable # noqa from terminaltables.github_table import GithubFlavoredMarkdownTable # noqa from terminaltables.other_tables import DoubleTable # noqa from terminaltables.other_tables import SingleTable # noqa from terminaltables.other_tables import PorcelainTable # noqa __author__ = '@Robpol86' __license__ = 'MIT' __version__ = '3.1.0' terminaltables-3.1.0/terminaltables/ascii_table.py000066400000000000000000000052561300101162000223300ustar00rootroot00000000000000"""AsciiTable is the main table class. To be inherited by other tables. Define convenience methods here.""" from terminaltables.base_table import BaseTable from terminaltables.terminal_io import terminal_size from terminaltables.width_and_alignment import column_max_width, max_dimensions, table_width class AsciiTable(BaseTable): """Draw a table using regular ASCII characters, such as ``+``, ``|``, and ``-``. :ivar iter table_data: List (empty or list of lists of strings) representing the table. :ivar str title: Optional title to show within the top border of the table. :ivar bool inner_column_border: Separates columns. :ivar bool inner_footing_row_border: Show a border before the last row. :ivar bool inner_heading_row_border: Show a border after the first row. :ivar bool inner_row_border: Show a border in between every row. :ivar bool outer_border: Show the top, left, right, and bottom border. :ivar dict justify_columns: Horizontal justification. Keys are column indexes (int). Values are right/left/center. :ivar int padding_left: Number of spaces to pad on the left side of every cell. :ivar int padding_right: Number of spaces to pad on the right side of every cell. """ def column_max_width(self, column_number): """Return the maximum width of a column based on the current terminal width. :param int column_number: The column number to query. :return: The max width of the column. :rtype: int """ inner_widths = max_dimensions(self.table_data)[0] outer_border = 2 if self.outer_border else 0 inner_border = 1 if self.inner_column_border else 0 padding = self.padding_left + self.padding_right return column_max_width(inner_widths, column_number, outer_border, inner_border, padding) @property def column_widths(self): """Return a list of integers representing the widths of each table column without padding.""" if not self.table_data: return list() return max_dimensions(self.table_data)[0] @property def ok(self): # Too late to change API. # pylint: disable=invalid-name """Return True if the table fits within the terminal width, False if the table breaks.""" return self.table_width <= terminal_size()[0] @property def table_width(self): """Return the width of the table including padding and borders.""" outer_widths = max_dimensions(self.table_data, self.padding_left, self.padding_right)[2] outer_border = 2 if self.outer_border else 0 inner_border = 1 if self.inner_column_border else 0 return table_width(outer_widths, outer_border, inner_border) terminaltables-3.1.0/terminaltables/base_table.py000066400000000000000000000230121300101162000221400ustar00rootroot00000000000000"""Base table class. Define just the bare minimum to build tables.""" from terminaltables.build import build_border, build_row, flatten from terminaltables.width_and_alignment import align_and_pad_cell, max_dimensions class BaseTable(object): """Base table class. :ivar iter table_data: List (empty or list of lists of strings) representing the table. :ivar str title: Optional title to show within the top border of the table. :ivar bool inner_column_border: Separates columns. :ivar bool inner_footing_row_border: Show a border before the last row. :ivar bool inner_heading_row_border: Show a border after the first row. :ivar bool inner_row_border: Show a border in between every row. :ivar bool outer_border: Show the top, left, right, and bottom border. :ivar dict justify_columns: Horizontal justification. Keys are column indexes (int). Values are right/left/center. :ivar int padding_left: Number of spaces to pad on the left side of every cell. :ivar int padding_right: Number of spaces to pad on the right side of every cell. """ CHAR_F_INNER_HORIZONTAL = '-' CHAR_F_INNER_INTERSECT = '+' CHAR_F_INNER_VERTICAL = '|' CHAR_F_OUTER_LEFT_INTERSECT = '+' CHAR_F_OUTER_LEFT_VERTICAL = '|' CHAR_F_OUTER_RIGHT_INTERSECT = '+' CHAR_F_OUTER_RIGHT_VERTICAL = '|' CHAR_H_INNER_HORIZONTAL = '-' CHAR_H_INNER_INTERSECT = '+' CHAR_H_INNER_VERTICAL = '|' CHAR_H_OUTER_LEFT_INTERSECT = '+' CHAR_H_OUTER_LEFT_VERTICAL = '|' CHAR_H_OUTER_RIGHT_INTERSECT = '+' CHAR_H_OUTER_RIGHT_VERTICAL = '|' CHAR_INNER_HORIZONTAL = '-' CHAR_INNER_INTERSECT = '+' CHAR_INNER_VERTICAL = '|' CHAR_OUTER_BOTTOM_HORIZONTAL = '-' CHAR_OUTER_BOTTOM_INTERSECT = '+' CHAR_OUTER_BOTTOM_LEFT = '+' CHAR_OUTER_BOTTOM_RIGHT = '+' CHAR_OUTER_LEFT_INTERSECT = '+' CHAR_OUTER_LEFT_VERTICAL = '|' CHAR_OUTER_RIGHT_INTERSECT = '+' CHAR_OUTER_RIGHT_VERTICAL = '|' CHAR_OUTER_TOP_HORIZONTAL = '-' CHAR_OUTER_TOP_INTERSECT = '+' CHAR_OUTER_TOP_LEFT = '+' CHAR_OUTER_TOP_RIGHT = '+' def __init__(self, table_data, title=None): """Constructor. :param iter table_data: List (empty or list of lists of strings) representing the table. :param title: Optional title to show within the top border of the table. """ self.table_data = table_data self.title = title self.inner_column_border = True self.inner_footing_row_border = False self.inner_heading_row_border = True self.inner_row_border = False self.outer_border = True self.justify_columns = dict() # {0: 'right', 1: 'left', 2: 'center'} self.padding_left = 1 self.padding_right = 1 def horizontal_border(self, style, outer_widths): """Build any kind of horizontal border for the table. :param str style: Type of border to return. :param iter outer_widths: List of widths (with padding) for each column. :return: Prepared border as a tuple of strings. :rtype: tuple """ if style == 'top': horizontal = self.CHAR_OUTER_TOP_HORIZONTAL left = self.CHAR_OUTER_TOP_LEFT intersect = self.CHAR_OUTER_TOP_INTERSECT if self.inner_column_border else '' right = self.CHAR_OUTER_TOP_RIGHT title = self.title elif style == 'bottom': horizontal = self.CHAR_OUTER_BOTTOM_HORIZONTAL left = self.CHAR_OUTER_BOTTOM_LEFT intersect = self.CHAR_OUTER_BOTTOM_INTERSECT if self.inner_column_border else '' right = self.CHAR_OUTER_BOTTOM_RIGHT title = None elif style == 'heading': horizontal = self.CHAR_H_INNER_HORIZONTAL left = self.CHAR_H_OUTER_LEFT_INTERSECT if self.outer_border else '' intersect = self.CHAR_H_INNER_INTERSECT if self.inner_column_border else '' right = self.CHAR_H_OUTER_RIGHT_INTERSECT if self.outer_border else '' title = None elif style == 'footing': horizontal = self.CHAR_F_INNER_HORIZONTAL left = self.CHAR_F_OUTER_LEFT_INTERSECT if self.outer_border else '' intersect = self.CHAR_F_INNER_INTERSECT if self.inner_column_border else '' right = self.CHAR_F_OUTER_RIGHT_INTERSECT if self.outer_border else '' title = None else: horizontal = self.CHAR_INNER_HORIZONTAL left = self.CHAR_OUTER_LEFT_INTERSECT if self.outer_border else '' intersect = self.CHAR_INNER_INTERSECT if self.inner_column_border else '' right = self.CHAR_OUTER_RIGHT_INTERSECT if self.outer_border else '' title = None return build_border(outer_widths, horizontal, left, intersect, right, title) def gen_row_lines(self, row, style, inner_widths, height): r"""Combine cells in row and group them into lines with vertical borders. Caller is expected to pass yielded lines to ''.join() to combine them into a printable line. Caller must append newline character to the end of joined line. In: ['Row One Column One', 'Two', 'Three'] Out: [ ('|', ' Row One Column One ', '|', ' Two ', '|', ' Three ', '|'), ] In: ['Row One\nColumn One', 'Two', 'Three'], Out: [ ('|', ' Row One ', '|', ' Two ', '|', ' Three ', '|'), ('|', ' Column One ', '|', ' ', '|', ' ', '|'), ] :param iter row: One row in the table. List of cells. :param str style: Type of border characters to use. :param iter inner_widths: List of widths (no padding) for each column. :param int height: Inner height (no padding) (number of lines) to expand row to. :return: Yields lines split into components in a list. Caller must ''.join() line. """ cells_in_row = list() # Resize row if it doesn't have enough cells. if len(row) != len(inner_widths): row = row + [''] * (len(inner_widths) - len(row)) # Pad and align each cell. Split each cell into lines to support multi-line cells. for i, cell in enumerate(row): align = (self.justify_columns.get(i),) inner_dimensions = (inner_widths[i], height) padding = (self.padding_left, self.padding_right, 0, 0) cells_in_row.append(align_and_pad_cell(cell, align, inner_dimensions, padding)) # Determine border characters. if style == 'heading': left = self.CHAR_H_OUTER_LEFT_VERTICAL if self.outer_border else '' center = self.CHAR_H_INNER_VERTICAL if self.inner_column_border else '' right = self.CHAR_H_OUTER_RIGHT_VERTICAL if self.outer_border else '' elif style == 'footing': left = self.CHAR_F_OUTER_LEFT_VERTICAL if self.outer_border else '' center = self.CHAR_F_INNER_VERTICAL if self.inner_column_border else '' right = self.CHAR_F_OUTER_RIGHT_VERTICAL if self.outer_border else '' else: left = self.CHAR_OUTER_LEFT_VERTICAL if self.outer_border else '' center = self.CHAR_INNER_VERTICAL if self.inner_column_border else '' right = self.CHAR_OUTER_RIGHT_VERTICAL if self.outer_border else '' # Yield each line. for line in build_row(cells_in_row, left, center, right): yield line def gen_table(self, inner_widths, inner_heights, outer_widths): """Combine everything and yield every line of the entire table with borders. :param iter inner_widths: List of widths (no padding) for each column. :param iter inner_heights: List of heights (no padding) for each row. :param iter outer_widths: List of widths (with padding) for each column. :return: """ # Yield top border. if self.outer_border: yield self.horizontal_border('top', outer_widths) # Yield table body. row_count = len(self.table_data) last_row_index, before_last_row_index = row_count - 1, row_count - 2 for i, row in enumerate(self.table_data): # Yield the row line by line (e.g. multi-line rows). if self.inner_heading_row_border and i == 0: style = 'heading' elif self.inner_footing_row_border and i == last_row_index: style = 'footing' else: style = 'row' for line in self.gen_row_lines(row, style, inner_widths, inner_heights[i]): yield line # If this is the last row then break. No separator needed. if i == last_row_index: break # Yield heading separator. if self.inner_heading_row_border and i == 0: yield self.horizontal_border('heading', outer_widths) # Yield footing separator. elif self.inner_footing_row_border and i == before_last_row_index: yield self.horizontal_border('footing', outer_widths) # Yield row separator. elif self.inner_row_border: yield self.horizontal_border('row', outer_widths) # Yield bottom border. if self.outer_border: yield self.horizontal_border('bottom', outer_widths) @property def table(self): """Return a large string of the entire table ready to be printed to the terminal.""" dimensions = max_dimensions(self.table_data, self.padding_left, self.padding_right)[:3] return flatten(self.gen_table(*dimensions)) terminaltables-3.1.0/terminaltables/build.py000066400000000000000000000116371300101162000211700ustar00rootroot00000000000000"""Combine cells into rows.""" from terminaltables.width_and_alignment import visible_width def combine(line, left, intersect, right): """Zip borders between items in `line`. e.g. ('l', '1', 'c', '2', 'c', '3', 'r') :param iter line: List to iterate. :param left: Left border. :param intersect: Column separator. :param right: Right border. :return: Yields combined objects. """ # Yield left border. if left: yield left # Yield items with intersect characters. if intersect: try: for j, i in enumerate(line, start=-len(line) + 1): yield i if j: yield intersect except TypeError: # Generator. try: item = next(line) except StopIteration: # Was empty all along. pass else: while True: yield item try: peek = next(line) except StopIteration: break yield intersect item = peek else: for i in line: yield i # Yield right border. if right: yield right def build_border(outer_widths, horizontal, left, intersect, right, title=None): """Build the top/bottom/middle row. Optionally embed the table title within the border. Title is hidden if it doesn't fit between the left/right characters/edges. Example return value: ('<', '-----', '+', '------', '+', '-------', '>') ('<', 'My Table', '----', '+', '------->') :param iter outer_widths: List of widths (with padding) for each column. :param str horizontal: Character to stretch across each column. :param str left: Left border. :param str intersect: Column separator. :param str right: Right border. :param title: Overlay the title on the border between the left and right characters. :return: Returns a generator of strings representing a border. :rtype: iter """ length = 0 # Hide title if it doesn't fit. if title is not None and outer_widths: try: length = visible_width(title) except TypeError: title = str(title) length = visible_width(title) if length > sum(outer_widths) + len(intersect) * (len(outer_widths) - 1): title = None # Handle no title. if title is None or not outer_widths or not horizontal: return combine((horizontal * c for c in outer_widths), left, intersect, right) # Handle title fitting in the first column. if length == outer_widths[0]: return combine([title] + [horizontal * c for c in outer_widths[1:]], left, intersect, right) if length < outer_widths[0]: columns = [title + horizontal * (outer_widths[0] - length)] + [horizontal * c for c in outer_widths[1:]] return combine(columns, left, intersect, right) # Handle wide titles/narrow columns. columns_and_intersects = [title] for width in combine(outer_widths, None, bool(intersect), None): # If title is taken care of. if length < 1: columns_and_intersects.append(intersect if width is True else horizontal * width) # If title's last character overrides an intersect character. elif width is True and length == 1: length = 0 # If this is an intersect character that is overridden by the title. elif width is True: length -= 1 # If title's last character is within a column. elif width >= length: columns_and_intersects[0] += horizontal * (width - length) # Append horizontal chars to title. length = 0 # If remainder of title won't fit in a column. else: length -= width return combine(columns_and_intersects, left, None, right) def build_row(row, left, center, right): """Combine single or multi-lined cells into a single row of list of lists including borders. Row must already be padded and extended so each cell has the same number of lines. Example return value: [ ['>', 'Left ', '|', 'Center', '|', 'Right', '<'], ['>', 'Cell1', '|', 'Cell2 ', '|', 'Cell3', '<'], ] :param iter row: List of cells for one row. :param str left: Left border. :param str center: Column separator. :param str right: Right border. :return: Yields other generators that yield strings. :rtype: iter """ if not row or not row[0]: yield combine((), left, center, right) return for row_index in range(len(row[0])): yield combine((c[row_index] for c in row), left, center, right) def flatten(table): """Flatten table data into a single string with newlines. :param iter table: Padded and bordered table data. :return: Joined rows/cells. :rtype: str """ return '\n'.join(''.join(r) for r in table) terminaltables-3.1.0/terminaltables/github_table.py000066400000000000000000000053311300101162000225140ustar00rootroot00000000000000"""GithubFlavoredMarkdownTable class.""" from terminaltables.ascii_table import AsciiTable from terminaltables.build import combine class GithubFlavoredMarkdownTable(AsciiTable): """Github flavored markdown table. https://help.github.com/articles/github-flavored-markdown/#tables :ivar iter table_data: List (empty or list of lists of strings) representing the table. :ivar dict justify_columns: Horizontal justification. Keys are column indexes (int). Values are right/left/center. """ def __init__(self, table_data): """Constructor. :param iter table_data: List (empty or list of lists of strings) representing the table. """ # Github flavored markdown table won't support title. super(GithubFlavoredMarkdownTable, self).__init__(table_data) def horizontal_border(self, _, outer_widths): """Handle the GitHub heading border. E.g.: |:---|:---:|---:|----| :param _: Unused. :param iter outer_widths: List of widths (with padding) for each column. :return: Prepared border strings in a generator. :rtype: iter """ horizontal = str(self.CHAR_INNER_HORIZONTAL) left = self.CHAR_OUTER_LEFT_VERTICAL intersect = self.CHAR_INNER_VERTICAL right = self.CHAR_OUTER_RIGHT_VERTICAL columns = list() for i, width in enumerate(outer_widths): justify = self.justify_columns.get(i) width = max(3, width) # Width should be at least 3 so justification can be applied. if justify == 'left': columns.append(':' + horizontal * (width - 1)) elif justify == 'right': columns.append(horizontal * (width - 1) + ':') elif justify == 'center': columns.append(':' + horizontal * (width - 2) + ':') else: columns.append(horizontal * width) return combine(columns, left, intersect, right) def gen_table(self, inner_widths, inner_heights, outer_widths): """Combine everything and yield every line of the entire table with borders. :param iter inner_widths: List of widths (no padding) for each column. :param iter inner_heights: List of heights (no padding) for each row. :param iter outer_widths: List of widths (with padding) for each column. :return: """ for i, row in enumerate(self.table_data): # Yield the row line by line (e.g. multi-line rows). for line in self.gen_row_lines(row, 'row', inner_widths, inner_heights[i]): yield line # Yield heading separator. if i == 0: yield self.horizontal_border(None, outer_widths) terminaltables-3.1.0/terminaltables/other_tables.py000066400000000000000000000204401300101162000225340ustar00rootroot00000000000000"""Additional simple tables defined here.""" from terminaltables.ascii_table import AsciiTable from terminaltables.terminal_io import IS_WINDOWS class UnixTable(AsciiTable): """Draw a table using box-drawing characters on Unix platforms. Table borders won't have any gaps between lines. Similar to the tables shown on PC BIOS boot messages, but not double-lined. """ CHAR_F_INNER_HORIZONTAL = '\033(0\x71\033(B' CHAR_F_INNER_INTERSECT = '\033(0\x6e\033(B' CHAR_F_INNER_VERTICAL = '\033(0\x78\033(B' CHAR_F_OUTER_LEFT_INTERSECT = '\033(0\x74\033(B' CHAR_F_OUTER_LEFT_VERTICAL = '\033(0\x78\033(B' CHAR_F_OUTER_RIGHT_INTERSECT = '\033(0\x75\033(B' CHAR_F_OUTER_RIGHT_VERTICAL = '\033(0\x78\033(B' CHAR_H_INNER_HORIZONTAL = '\033(0\x71\033(B' CHAR_H_INNER_INTERSECT = '\033(0\x6e\033(B' CHAR_H_INNER_VERTICAL = '\033(0\x78\033(B' CHAR_H_OUTER_LEFT_INTERSECT = '\033(0\x74\033(B' CHAR_H_OUTER_LEFT_VERTICAL = '\033(0\x78\033(B' CHAR_H_OUTER_RIGHT_INTERSECT = '\033(0\x75\033(B' CHAR_H_OUTER_RIGHT_VERTICAL = '\033(0\x78\033(B' CHAR_INNER_HORIZONTAL = '\033(0\x71\033(B' CHAR_INNER_INTERSECT = '\033(0\x6e\033(B' CHAR_INNER_VERTICAL = '\033(0\x78\033(B' CHAR_OUTER_BOTTOM_HORIZONTAL = '\033(0\x71\033(B' CHAR_OUTER_BOTTOM_INTERSECT = '\033(0\x76\033(B' CHAR_OUTER_BOTTOM_LEFT = '\033(0\x6d\033(B' CHAR_OUTER_BOTTOM_RIGHT = '\033(0\x6a\033(B' CHAR_OUTER_LEFT_INTERSECT = '\033(0\x74\033(B' CHAR_OUTER_LEFT_VERTICAL = '\033(0\x78\033(B' CHAR_OUTER_RIGHT_INTERSECT = '\033(0\x75\033(B' CHAR_OUTER_RIGHT_VERTICAL = '\033(0\x78\033(B' CHAR_OUTER_TOP_HORIZONTAL = '\033(0\x71\033(B' CHAR_OUTER_TOP_INTERSECT = '\033(0\x77\033(B' CHAR_OUTER_TOP_LEFT = '\033(0\x6c\033(B' CHAR_OUTER_TOP_RIGHT = '\033(0\x6b\033(B' @property def table(self): """Return a large string of the entire table ready to be printed to the terminal.""" ascii_table = super(UnixTable, self).table optimized = ascii_table.replace('\033(B\033(0', '') return optimized class WindowsTable(AsciiTable): """Draw a table using box-drawing characters on Windows platforms. This uses Code Page 437. Single-line borders. From: http://en.wikipedia.org/wiki/Code_page_437#Characters """ CHAR_F_INNER_HORIZONTAL = b'\xc4'.decode('ibm437') CHAR_F_INNER_INTERSECT = b'\xc5'.decode('ibm437') CHAR_F_INNER_VERTICAL = b'\xb3'.decode('ibm437') CHAR_F_OUTER_LEFT_INTERSECT = b'\xc3'.decode('ibm437') CHAR_F_OUTER_LEFT_VERTICAL = b'\xb3'.decode('ibm437') CHAR_F_OUTER_RIGHT_INTERSECT = b'\xb4'.decode('ibm437') CHAR_F_OUTER_RIGHT_VERTICAL = b'\xb3'.decode('ibm437') CHAR_H_INNER_HORIZONTAL = b'\xc4'.decode('ibm437') CHAR_H_INNER_INTERSECT = b'\xc5'.decode('ibm437') CHAR_H_INNER_VERTICAL = b'\xb3'.decode('ibm437') CHAR_H_OUTER_LEFT_INTERSECT = b'\xc3'.decode('ibm437') CHAR_H_OUTER_LEFT_VERTICAL = b'\xb3'.decode('ibm437') CHAR_H_OUTER_RIGHT_INTERSECT = b'\xb4'.decode('ibm437') CHAR_H_OUTER_RIGHT_VERTICAL = b'\xb3'.decode('ibm437') CHAR_INNER_HORIZONTAL = b'\xc4'.decode('ibm437') CHAR_INNER_INTERSECT = b'\xc5'.decode('ibm437') CHAR_INNER_VERTICAL = b'\xb3'.decode('ibm437') CHAR_OUTER_BOTTOM_HORIZONTAL = b'\xc4'.decode('ibm437') CHAR_OUTER_BOTTOM_INTERSECT = b'\xc1'.decode('ibm437') CHAR_OUTER_BOTTOM_LEFT = b'\xc0'.decode('ibm437') CHAR_OUTER_BOTTOM_RIGHT = b'\xd9'.decode('ibm437') CHAR_OUTER_LEFT_INTERSECT = b'\xc3'.decode('ibm437') CHAR_OUTER_LEFT_VERTICAL = b'\xb3'.decode('ibm437') CHAR_OUTER_RIGHT_INTERSECT = b'\xb4'.decode('ibm437') CHAR_OUTER_RIGHT_VERTICAL = b'\xb3'.decode('ibm437') CHAR_OUTER_TOP_HORIZONTAL = b'\xc4'.decode('ibm437') CHAR_OUTER_TOP_INTERSECT = b'\xc2'.decode('ibm437') CHAR_OUTER_TOP_LEFT = b'\xda'.decode('ibm437') CHAR_OUTER_TOP_RIGHT = b'\xbf'.decode('ibm437') class WindowsTableDouble(AsciiTable): """Draw a table using box-drawing characters on Windows platforms. This uses Code Page 437. Double-line borders.""" CHAR_F_INNER_HORIZONTAL = b'\xcd'.decode('ibm437') CHAR_F_INNER_INTERSECT = b'\xce'.decode('ibm437') CHAR_F_INNER_VERTICAL = b'\xba'.decode('ibm437') CHAR_F_OUTER_LEFT_INTERSECT = b'\xcc'.decode('ibm437') CHAR_F_OUTER_LEFT_VERTICAL = b'\xba'.decode('ibm437') CHAR_F_OUTER_RIGHT_INTERSECT = b'\xb9'.decode('ibm437') CHAR_F_OUTER_RIGHT_VERTICAL = b'\xba'.decode('ibm437') CHAR_H_INNER_HORIZONTAL = b'\xcd'.decode('ibm437') CHAR_H_INNER_INTERSECT = b'\xce'.decode('ibm437') CHAR_H_INNER_VERTICAL = b'\xba'.decode('ibm437') CHAR_H_OUTER_LEFT_INTERSECT = b'\xcc'.decode('ibm437') CHAR_H_OUTER_LEFT_VERTICAL = b'\xba'.decode('ibm437') CHAR_H_OUTER_RIGHT_INTERSECT = b'\xb9'.decode('ibm437') CHAR_H_OUTER_RIGHT_VERTICAL = b'\xba'.decode('ibm437') CHAR_INNER_HORIZONTAL = b'\xcd'.decode('ibm437') CHAR_INNER_INTERSECT = b'\xce'.decode('ibm437') CHAR_INNER_VERTICAL = b'\xba'.decode('ibm437') CHAR_OUTER_BOTTOM_HORIZONTAL = b'\xcd'.decode('ibm437') CHAR_OUTER_BOTTOM_INTERSECT = b'\xca'.decode('ibm437') CHAR_OUTER_BOTTOM_LEFT = b'\xc8'.decode('ibm437') CHAR_OUTER_BOTTOM_RIGHT = b'\xbc'.decode('ibm437') CHAR_OUTER_LEFT_INTERSECT = b'\xcc'.decode('ibm437') CHAR_OUTER_LEFT_VERTICAL = b'\xba'.decode('ibm437') CHAR_OUTER_RIGHT_INTERSECT = b'\xb9'.decode('ibm437') CHAR_OUTER_RIGHT_VERTICAL = b'\xba'.decode('ibm437') CHAR_OUTER_TOP_HORIZONTAL = b'\xcd'.decode('ibm437') CHAR_OUTER_TOP_INTERSECT = b'\xcb'.decode('ibm437') CHAR_OUTER_TOP_LEFT = b'\xc9'.decode('ibm437') CHAR_OUTER_TOP_RIGHT = b'\xbb'.decode('ibm437') class SingleTable(WindowsTable if IS_WINDOWS else UnixTable): """Cross-platform table with single-line box-drawing characters. :ivar iter table_data: List (empty or list of lists of strings) representing the table. :ivar str title: Optional title to show within the top border of the table. :ivar bool inner_column_border: Separates columns. :ivar bool inner_footing_row_border: Show a border before the last row. :ivar bool inner_heading_row_border: Show a border after the first row. :ivar bool inner_row_border: Show a border in between every row. :ivar bool outer_border: Show the top, left, right, and bottom border. :ivar dict justify_columns: Horizontal justification. Keys are column indexes (int). Values are right/left/center. :ivar int padding_left: Number of spaces to pad on the left side of every cell. :ivar int padding_right: Number of spaces to pad on the right side of every cell. """ pass class DoubleTable(WindowsTableDouble): """Cross-platform table with box-drawing characters. On Windows it's double borders, on Linux/OSX it's unicode. :ivar iter table_data: List (empty or list of lists of strings) representing the table. :ivar str title: Optional title to show within the top border of the table. :ivar bool inner_column_border: Separates columns. :ivar bool inner_footing_row_border: Show a border before the last row. :ivar bool inner_heading_row_border: Show a border after the first row. :ivar bool inner_row_border: Show a border in between every row. :ivar bool outer_border: Show the top, left, right, and bottom border. :ivar dict justify_columns: Horizontal justification. Keys are column indexes (int). Values are right/left/center. :ivar int padding_left: Number of spaces to pad on the left side of every cell. :ivar int padding_right: Number of spaces to pad on the right side of every cell. """ pass class PorcelainTable(AsciiTable): """An AsciiTable stripped to a minimum. Meant to be machine passable and roughly follow format set by git --porcelain option (hence the name). :ivar iter table_data: List (empty or list of lists of strings) representing the table. """ def __init__(self, table_data): """Constructor. :param iter table_data: List (empty or list of lists of strings) representing the table. """ # Porcelain table won't support title since it has no outer birders. super(PorcelainTable, self).__init__(table_data) # Removes outer border, and inner footing and header row borders. self.inner_footing_row_border = False self.inner_heading_row_border = False self.outer_border = False terminaltables-3.1.0/terminaltables/terminal_io.py000066400000000000000000000063141300101162000223670ustar00rootroot00000000000000"""Get info about the current terminal window/screen buffer.""" import ctypes import struct import sys DEFAULT_HEIGHT = 24 DEFAULT_WIDTH = 79 INVALID_HANDLE_VALUE = -1 IS_WINDOWS = sys.platform == 'win32' STD_ERROR_HANDLE = -12 STD_OUTPUT_HANDLE = -11 def get_console_info(kernel32, handle): """Get information about this current console window (Windows only). https://github.com/Robpol86/colorclass/blob/ab42da59/colorclass/windows.py#L111 :raise OSError: When handle is invalid or GetConsoleScreenBufferInfo API call fails. :param ctypes.windll.kernel32 kernel32: Loaded kernel32 instance. :param int handle: stderr or stdout handle. :return: Width (number of characters) and height (number of lines) of the terminal. :rtype: tuple """ if handle == INVALID_HANDLE_VALUE: raise OSError('Invalid handle.') # Query Win32 API. lpcsbi = ctypes.create_string_buffer(22) # Populated by GetConsoleScreenBufferInfo. if not kernel32.GetConsoleScreenBufferInfo(handle, lpcsbi): raise ctypes.WinError() # Subclass of OSError. # Parse data. left, top, right, bottom = struct.unpack('hhhhHhhhhhh', lpcsbi.raw)[5:-2] width, height = right - left, bottom - top return width, height def terminal_size(kernel32=None): """Get the width and height of the terminal. http://code.activestate.com/recipes/440694-determine-size-of-console-window-on-windows/ http://stackoverflow.com/questions/17993814/why-the-irrelevant-code-made-a-difference :param kernel32: Optional mock kernel32 object. For testing. :return: Width (number of characters) and height (number of lines) of the terminal. :rtype: tuple """ if IS_WINDOWS: kernel32 = kernel32 or ctypes.windll.kernel32 try: return get_console_info(kernel32, kernel32.GetStdHandle(STD_ERROR_HANDLE)) except OSError: try: return get_console_info(kernel32, kernel32.GetStdHandle(STD_OUTPUT_HANDLE)) except OSError: return DEFAULT_WIDTH, DEFAULT_HEIGHT try: device = __import__('fcntl').ioctl(0, __import__('termios').TIOCGWINSZ, '\0\0\0\0\0\0\0\0') except IOError: return DEFAULT_WIDTH, DEFAULT_HEIGHT height, width = struct.unpack('hhhh', device)[:2] return width, height def set_terminal_title(title, kernel32=None): """Set the terminal title. :param title: The title to set (string, unicode, bytes accepted). :param kernel32: Optional mock kernel32 object. For testing. :return: If title changed successfully (Windows only, always True on Linux/OSX). :rtype: bool """ try: title_bytes = title.encode('utf-8') except AttributeError: title_bytes = title if IS_WINDOWS: kernel32 = kernel32 or ctypes.windll.kernel32 try: is_ascii = all(ord(c) < 128 for c in title) # str/unicode. except TypeError: is_ascii = all(c < 128 for c in title) # bytes. if is_ascii: return kernel32.SetConsoleTitleA(title_bytes) != 0 else: return kernel32.SetConsoleTitleW(title) != 0 # Linux/OSX. sys.stdout.write(b'\033]0;' + title_bytes + b'\007') return True terminaltables-3.1.0/terminaltables/width_and_alignment.py000066400000000000000000000140401300101162000240570ustar00rootroot00000000000000"""Functions that handle alignment, padding, widths, etc.""" import re import unicodedata from terminaltables.terminal_io import terminal_size RE_COLOR_ANSI = re.compile(r'(\033\[[\d;]+m)') def visible_width(string): """Get the visible width of a unicode string. Some CJK unicode characters are more than one byte unlike ASCII and latin unicode characters. From: https://github.com/Robpol86/terminaltables/pull/9 :param str string: String to measure. :return: String's width. :rtype: int """ if '\033' in string: string = RE_COLOR_ANSI.sub('', string) # Convert to unicode. try: string = string.decode('u8') except (AttributeError, UnicodeEncodeError): pass width = 0 for char in string: if unicodedata.east_asian_width(char) in ('F', 'W'): width += 2 else: width += 1 return width def align_and_pad_cell(string, align, inner_dimensions, padding, space=' '): """Align a string horizontally and vertically. Also add additional padding in both dimensions. :param str string: Input string to operate on. :param tuple align: Tuple that contains one of left/center/right and/or top/middle/bottom. :param tuple inner_dimensions: Width and height ints to expand string to without padding. :param iter padding: Number of space chars for left, right, top, and bottom (4 ints). :param str space: Character to use as white space for resizing/padding (use single visible chars only). :return: Padded cell split into lines. :rtype: list """ if not hasattr(string, 'splitlines'): string = str(string) # Handle trailing newlines or empty strings, str.splitlines() does not satisfy. lines = string.splitlines() or [''] if string.endswith('\n'): lines.append('') # Vertically align and pad. if 'bottom' in align: lines = ([''] * (inner_dimensions[1] - len(lines) + padding[2])) + lines + ([''] * padding[3]) elif 'middle' in align: delta = inner_dimensions[1] - len(lines) lines = ([''] * (delta // 2 + delta % 2 + padding[2])) + lines + ([''] * (delta // 2 + padding[3])) else: lines = ([''] * padding[2]) + lines + ([''] * (inner_dimensions[1] - len(lines) + padding[3])) # Horizontally align and pad. for i, line in enumerate(lines): new_width = inner_dimensions[0] + len(line) - visible_width(line) if 'right' in align: lines[i] = line.rjust(padding[0] + new_width, space) + (space * padding[1]) elif 'center' in align: lines[i] = (space * padding[0]) + line.center(new_width, space) + (space * padding[1]) else: lines[i] = (space * padding[0]) + line.ljust(new_width + padding[1], space) return lines def max_dimensions(table_data, padding_left=0, padding_right=0, padding_top=0, padding_bottom=0): """Get maximum widths of each column and maximum height of each row. :param iter table_data: List of list of strings (unmodified table data). :param int padding_left: Number of space chars on left side of cell. :param int padding_right: Number of space chars on right side of cell. :param int padding_top: Number of empty lines on top side of cell. :param int padding_bottom: Number of empty lines on bottom side of cell. :return: 4-item tuple of n-item lists. Inner column widths and row heights, outer column widths and row heights. :rtype: tuple """ inner_widths = [0] * (max(len(r) for r in table_data) if table_data else 0) inner_heights = [0] * len(table_data) # Find max width and heights. for j, row in enumerate(table_data): for i, cell in enumerate(row): if not hasattr(cell, 'count') or not hasattr(cell, 'splitlines'): cell = str(cell) if not cell: continue inner_heights[j] = max(inner_heights[j], cell.count('\n') + 1) inner_widths[i] = max(inner_widths[i], *[visible_width(l) for l in cell.splitlines()]) # Calculate with padding. outer_widths = [padding_left + i + padding_right for i in inner_widths] outer_heights = [padding_top + i + padding_bottom for i in inner_heights] return inner_widths, inner_heights, outer_widths, outer_heights def column_max_width(inner_widths, column_number, outer_border, inner_border, padding): """Determine the maximum width of a column based on the current terminal width. :param iter inner_widths: List of widths (no padding) for each column. :param int column_number: The column number to query. :param int outer_border: Sum of left and right outer border visible widths. :param int inner_border: Visible width of the inner border character. :param int padding: Total padding per cell (left + right padding). :return: The maximum width the column can be without causing line wrapping. """ column_count = len(inner_widths) terminal_width = terminal_size()[0] # Count how much space padding, outer, and inner borders take up. non_data_space = outer_border non_data_space += inner_border * (column_count - 1) non_data_space += column_count * padding # Exclude selected column's width. data_space = sum(inner_widths) - inner_widths[column_number] return terminal_width - data_space - non_data_space def table_width(outer_widths, outer_border, inner_border): """Determine the width of the entire table including borders and padding. :param iter outer_widths: List of widths (with padding) for each column. :param int outer_border: Sum of left and right outer border visible widths. :param int inner_border: Visible width of the inner border character. :return: The width of the table. :rtype: int """ column_count = len(outer_widths) # Count how much space outer and inner borders take up. non_data_space = outer_border if column_count: non_data_space += inner_border * (column_count - 1) # Space of all columns and their padding. data_space = sum(outer_widths) return data_space + non_data_space terminaltables-3.1.0/tests/000077500000000000000000000000001300101162000156435ustar00rootroot00000000000000terminaltables-3.1.0/tests/__init__.py000066400000000000000000000001601300101162000177510ustar00rootroot00000000000000"""Allows importing from screenshot.""" import py PROJECT_ROOT = py.path.local(__file__).dirpath().join('..') terminaltables-3.1.0/tests/screenshot.py000066400000000000000000000251511300101162000203760ustar00rootroot00000000000000"""Take screenshots and search for subimages in images.""" import ctypes import os import random import struct import subprocess import time try: from itertools import izip except ImportError: izip = zip # Py3 from tests import PROJECT_ROOT STARTF_USESHOWWINDOW = getattr(subprocess, 'STARTF_USESHOWWINDOW', 1) STILL_ACTIVE = 259 SW_MAXIMIZE = 3 class StartupInfo(ctypes.Structure): """STARTUPINFO structure.""" _fields_ = [ ('cb', ctypes.c_ulong), ('lpReserved', ctypes.c_char_p), ('lpDesktop', ctypes.c_char_p), ('lpTitle', ctypes.c_char_p), ('dwX', ctypes.c_ulong), ('dwY', ctypes.c_ulong), ('dwXSize', ctypes.c_ulong), ('dwYSize', ctypes.c_ulong), ('dwXCountChars', ctypes.c_ulong), ('dwYCountChars', ctypes.c_ulong), ('dwFillAttribute', ctypes.c_ulong), ('dwFlags', ctypes.c_ulong), ('wShowWindow', ctypes.c_ushort), ('cbReserved2', ctypes.c_ushort), ('lpReserved2', ctypes.c_char_p), ('hStdInput', ctypes.c_ulong), ('hStdOutput', ctypes.c_ulong), ('hStdError', ctypes.c_ulong), ] def __init__(self, maximize=False, title=None): """Constructor. :param bool maximize: Start process in new console window, maximized. :param bytes title: Set new window title to this instead of exe path. """ super(StartupInfo, self).__init__() self.cb = ctypes.sizeof(self) if maximize: self.dwFlags |= STARTF_USESHOWWINDOW self.wShowWindow = SW_MAXIMIZE if title: self.lpTitle = ctypes.c_char_p(title) class ProcessInfo(ctypes.Structure): """PROCESS_INFORMATION structure.""" _fields_ = [ ('hProcess', ctypes.c_void_p), ('hThread', ctypes.c_void_p), ('dwProcessId', ctypes.c_ulong), ('dwThreadId', ctypes.c_ulong), ] class RunNewConsole(object): """Run the command in a new console window. Windows only. Use in a with statement. subprocess sucks and really limits your access to the win32 API. Its implementation is half-assed. Using this so that STARTUPINFO.lpTitle actually works and STARTUPINFO.dwFillAttribute produce the expected result. """ def __init__(self, command, maximized=False, title=None): """Constructor. :param iter command: Command to run. :param bool maximized: Start process in new console window, maximized. :param bytes title: Set new window title to this. Needed by user32.FindWindow. """ if title is None: title = 'pytest-{0}-{1}'.format(os.getpid(), random.randint(1000, 9999)).encode('ascii') self.startup_info = StartupInfo(maximize=maximized, title=title) self.process_info = ProcessInfo() self.command_str = subprocess.list2cmdline(command).encode('ascii') self._handles = list() self._kernel32 = ctypes.LibraryLoader(ctypes.WinDLL).kernel32 self._kernel32.GetExitCodeProcess.argtypes = [ctypes.c_void_p, ctypes.POINTER(ctypes.c_ulong)] self._kernel32.GetExitCodeProcess.restype = ctypes.c_long def __del__(self): """Close win32 handles.""" while self._handles: try: self._kernel32.CloseHandle(self._handles.pop(0)) # .pop() is thread safe. except IndexError: break def __enter__(self): """Entering the `with` block. Runs the process.""" if not self._kernel32.CreateProcessA( None, # lpApplicationName self.command_str, # lpCommandLine None, # lpProcessAttributes None, # lpThreadAttributes False, # bInheritHandles subprocess.CREATE_NEW_CONSOLE, # dwCreationFlags None, # lpEnvironment str(PROJECT_ROOT).encode('ascii'), # lpCurrentDirectory ctypes.byref(self.startup_info), # lpStartupInfo ctypes.byref(self.process_info) # lpProcessInformation ): raise ctypes.WinError() # Add handles added by the OS. self._handles.append(self.process_info.hProcess) self._handles.append(self.process_info.hThread) # Get hWnd. self.hwnd = 0 for _ in range(int(5 / 0.1)): # Takes time for console window to initialize. self.hwnd = ctypes.windll.user32.FindWindowA(None, self.startup_info.lpTitle) if self.hwnd: break time.sleep(0.1) assert self.hwnd # Return generator that yields window size/position. return self._iter_pos() def __exit__(self, *_): """Cleanup.""" try: # Verify process exited 0. status = ctypes.c_ulong(STILL_ACTIVE) while status.value == STILL_ACTIVE: time.sleep(0.1) if not self._kernel32.GetExitCodeProcess(self.process_info.hProcess, ctypes.byref(status)): raise ctypes.WinError() assert status.value == 0 finally: # Close handles. self.__del__() def _iter_pos(self): """Yield new console window's current position and dimensions. :return: Yields region the new window is in (left, upper, right, lower). :rtype: tuple """ rect = ctypes.create_string_buffer(16) # To be written to by GetWindowRect. RECT structure. while ctypes.windll.user32.GetWindowRect(self.hwnd, rect): left, top, right, bottom = struct.unpack('llll', rect.raw) width, height = right - left, bottom - top assert width > 1 assert height > 1 yield left, top, right, bottom raise StopIteration def iter_rows(pil_image): """Yield tuple of pixels for each row in the image. itertools.izip in Python 2.x and zip in Python 3.x are writen in C. Much faster than anything else I've found written in pure Python. From: http://stackoverflow.com/questions/1624883/alternative-way-to-split-a-list-into-groups-of-n/1625023#1625023 :param PIL.Image.Image pil_image: Image to read from. :return: Yields rows. :rtype: tuple """ iterator = izip(*(iter(pil_image.getdata()),) * pil_image.width) for row in iterator: yield row def get_most_interesting_row(pil_image): """Look for a row in the image that has the most unique pixels. :param PIL.Image.Image pil_image: Image to read from. :return: Row (tuple of pixel tuples), row as a set, first pixel tuple, y offset from top. :rtype: tuple """ final = (None, set(), None, None) # row, row_set, first_pixel, y_pos for y_pos, row in enumerate(iter_rows(pil_image)): row_set = set(row) if len(row_set) > len(final[1]): final = row, row_set, row[0], y_pos if len(row_set) == pil_image.width: break # Can't get bigger. return final def count_subimages(screenshot, subimg): """Check how often subimg appears in the screenshot image. :param PIL.Image.Image screenshot: Screen shot to search through. :param PIL.Image.Image subimg: Subimage to search for. :return: Number of times subimg appears in the screenshot. :rtype: int """ # Get row to search for. si_pixels = list(subimg.getdata()) # Load entire subimg into memory. si_width = subimg.width si_height = subimg.height si_row, si_row_set, si_pixel, si_y = get_most_interesting_row(subimg) occurrences = 0 # Look for subimg row in screenshot, then crop and compare pixel arrays. for y_pos, row in enumerate(iter_rows(screenshot)): if si_row_set - set(row): continue # Some pixels not found. for x_pos in range(screenshot.width - si_width + 1): if row[x_pos] != si_pixel: continue # First pixel does not match. if row[x_pos:x_pos + si_width] != si_row: continue # Row does not match. # Found match for interesting row of subimg in screenshot. y_corrected = y_pos - si_y with screenshot.crop((x_pos, y_corrected, x_pos + si_width, y_corrected + si_height)) as cropped: if list(cropped.getdata()) == si_pixels: occurrences += 1 return occurrences def try_candidates(screenshot, subimg_candidates, expected_count): """Call count_subimages() for each subimage candidate until. If you get ImportError run "pip install pillow". Only OSX and Windows is supported. :param PIL.Image.Image screenshot: Screen shot to search through. :param iter subimg_candidates: Subimage paths to look for. List of strings. :param int expected_count: Try until any a subimage candidate is found this many times. :return: Number of times subimg appears in the screenshot. :rtype: int """ from PIL import Image count_found = 0 for subimg_path in subimg_candidates: with Image.open(subimg_path) as rgba_s: with rgba_s.convert(mode='RGB') as subimg: # Make sure subimage isn't too large. assert subimg.width < 256 assert subimg.height < 256 # Count. count_found = count_subimages(screenshot, subimg) if count_found == expected_count: break # No need to try other candidates. return count_found def screenshot_until_match(save_to, timeout, subimg_candidates, expected_count, gen): """Take screenshots until one of the 'done' subimages is found. Image is saved when subimage found or at timeout. If you get ImportError run "pip install pillow". Only OSX and Windows is supported. :param str save_to: Save screenshot to this PNG file path when expected count found or timeout. :param int timeout: Give up after these many seconds. :param iter subimg_candidates: Subimage paths to look for. List of strings. :param int expected_count: Keep trying until any of subimg_candidates is found this many times. :param iter gen: Generator yielding window position and size to crop screenshot to. """ from PIL import ImageGrab assert save_to.endswith('.png') stop_after = time.time() + timeout # Take screenshots until subimage is found. while True: with ImageGrab.grab(next(gen)) as rgba: with rgba.convert(mode='RGB') as screenshot: count_found = try_candidates(screenshot, subimg_candidates, expected_count) if count_found == expected_count or time.time() > stop_after: screenshot.save(save_to) assert count_found == expected_count return time.sleep(0.5) terminaltables-3.1.0/tests/test_all_tables_e2e/000077500000000000000000000000001300101162000215375ustar00rootroot00000000000000terminaltables-3.1.0/tests/test_all_tables_e2e/__init__.py000066400000000000000000000000501300101162000236430ustar00rootroot00000000000000"""Allows importing from screenshot.""" terminaltables-3.1.0/tests/test_all_tables_e2e/sub_ascii_win10.bmp000066400000000000000000001100061300101162000252140ustar00rootroot00000000000000BM6(NЏl+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+LLLLLLLLLLLLLLLLLLLl+LLLLLLLLLLLLLLLl+LLLLLLLLLLLLLl+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+LLLLL:LLLLLLLLfLLLLLLLLl+LLl+Ll+L+lLLl+LLL::LLL+ll+LL+lLl+L+ll+LLLLLL+ll+LLLLLLl+l++lLLl+LLLf:::LLL+lLLLLLLLl+L+ll+LLLLLL+lLLL+lL+lLl+L+ll+LLLLLffff:LLL+ll+Ll+Ll+LL+lLl+L+lLLLLLLL+ll+LL+lL+lL+lLl+L+lLLl+LL+lLL:::::::LLLLll+ll+LLLLLL+l+l+ll+LL+lLLLLL+ll++lLl+Ll+Ll+L+ll+Ll++lLLLLLLl+Ll+LLl+l++lLLLLL+lL+lL+lLLLLL+ll+LLLLLL+lL+lLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLl+LL+ll+Ll++lLL+l+lLl+L+lLLf:ffLLL+lLLl++lLLLLl+LLLLLl+Ll++lLLl+LL+lLLLLLLLLfLLLl++lLLLLLLLLLl+Ll+LL+ll+Ll++lLLL+lL+ll+LL+lLLffLLl+LL+l+l+lLLLLl+LLLLLl+Ll+lL+L+l+lL+lLLLLLL:f:fLLl+LL+lL+lLLLLl+Ll+Ll+ll+l+lLLl+l+LLLf::LLl+l++lLLl+LLLLLLL+lLLfLLl+LLl+LLLLL+lLLfLLLLLLLLLLLLLLLL:LLLLLLLLfLLLLLL+l+lLLLLL+lLL+l+lLll+lL+lLL::LL+l+lL+lL+lLLLLl+LLLLLLLLLLLl+LL+ll+Ll++lLLLLLLf:::LLLLLl++lLLl++lLLLLLl+LLLl+LL+ll+LL+ll+LL+lL+lL+ll+LL+lLLffff:LLLLl+LL+ll+LL+lLLLLLLl++l+lLLl+LLLLLL+l+ll+l+L+lLLLLLL:::::::LLLLl+LL+ll+LL+lLLLLL+Lll+l+LL+ll+Ll+L+lLl++ll+LLLLLLLLLl+l++lLl+l++lLLLLLL+lL+lLLLLl+Ll+Ll+LLLLLL+lLLLLLLLLLLLLLLl+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+LLLLLLLLLLLLLLLLLLLl+LLLLLLLLLLLLLLLl+LLLLLLLLLLLLLl+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+LLLLLLLLLLLLLLLLLLLL+ll+L+l+ll+Ll++lLLl+LLLLLL+ll+l+L+l+lLLLl+LL+ll+l+l++lLLLLLLL+lLLl+LL+ll+Ll++lLLl+LLLl+LLLLLl+LLLLL+lLLLl+LL+ll+L+lLLLLLLLL+ll+Ll+LL+ll+Ll++lLLLLLLl++ll+LL+ll+Ll+LL+l+lLLLl+Ll+Ll+Ll+Ll++ll+LLLLLLLll+ll+L+l+ll+Ll+lL+Ll+LL+lLLl++lLLLLl+LLLLL+l+ll++lLLL+LLllLLLl+Ll+LLl+LLLLLLLLl+L+lLl+ll+ll++lLLl+Ll+Ll+Ll+L+lllLLLLllLlLLllLLLLLl+LLLLLL+ll+LLLl+Ll+LLLLLL+lLLLLLLLLLLLl+LLLLLLLLLLl+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+LLLLLLLLLLLLLLLLLLLl+LLLLLLLLLLLLLLLl+LLLLLLLLLLLLLl+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Lterminaltables-3.1.0/tests/test_all_tables_e2e/sub_ascii_winxp.bmp000066400000000000000000001403061300101162000254310ustar00rootroot00000000000000BM6(Oterminaltables-3.1.0/tests/test_all_tables_e2e/sub_double_win10.bmp000066400000000000000000001063721300101162000254110ustar00rootroot00000000000000BM6(MČl+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+L:l+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Lfl+Ll+Ll+Ll+Ll+Ll+LLLl+LLl+Ll+L+lLLl+Ll+Ll+L::l+Ll+LL+ll+LL+lLl+L+ll+Ll+Ll+Ll+Ll+LL+ll+LLLLLLl+l++lLLl+Ll+Ll+Lf:::l+Ll+LL+lLLLLLLLl+L+ll+Ll+Ll+Ll+Ll+LL+lLLL+lL+lLl+L+ll+LLLl+Ll+Lffff:l+Ll+LL+ll+Ll+Ll+LL+lLl+L+lLLl+Ll+Ll+Ll+LL+ll+LL+lL+lL+lLl+L+lLLl+LL+ll+Ll+L:::::::l+Ll+LLLll+ll+LLLLLL+l+l+ll+LL+ll+Ll+Ll+Ll+LL+ll++lLl+Ll+Ll+L+ll+Ll++ll+Ll+Ll+Ll+LLLl+Ll+LLl+l++ll+Ll+Ll+Ll+LL+lL+lL+ll+Ll+Ll+Ll+LL+ll+Ll+Ll+Ll+Ll+LL+lL+ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+LL+ll+Ll++lLL+l+lLl+L+ll+Ll+Lf:ffl+Ll+LL+lLLl++ll+Ll+Ll+Ll+Ll+LLLLLl+Ll++lLLl+LL+lLLLLLLl+Ll+Lfl+Ll+LLl++lLLLLLl+Ll+Ll+Ll+Ll+Ll+LL+ll+Ll++lLLL+lL+ll+LL+ll+Ll+Lffl+Ll+Ll+LL+l+l+ll+Ll+Ll+Ll+Ll+LLLLLl+Ll+lL+L+l+lL+lLLLLl+Ll+L:f:fl+Ll+Ll+LL+lL+ll+Ll+Ll+Ll+Ll+Ll+Ll+ll+l+lLLl+l+Ll+Ll+Lf::l+Ll+Ll+l++lLLl+l+Ll+Ll+Ll+LLLL+ll+Ll+Lfl+Ll+Ll+LLl+l+Ll+Ll+Ll+LL+ll+Ll+Lfl+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+L:l+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Lfl+Ll+Ll+Ll+Ll+Ll+L+l+lLLLLL+lLL+l+lLll+lL+ll+Ll+L::l+Ll+L+l+lL+lL+ll+Ll+Ll+Ll+Ll+LLLLLLLLLLLl+LL+ll+Ll++lLLLLl+Ll+Lf:::l+Ll+LLLLl++lLLl++lLl+Ll+Ll+Ll+Ll+LLLl+LL+ll+LL+ll+LL+lL+lL+ll+LL+ll+Ll+Lffff:l+Ll+LLLl+LL+ll+LL+ll+Ll+Ll+Ll+LLLl++l+lLLl+LLLLLL+l+ll+l+L+lLLLLl+Ll+L:::::::l+Ll+LLLl+LL+ll+LL+ll+Ll+Ll+Ll+LL+Lll+l+LL+ll+Ll+L+lLl++ll+Ll+Ll+Ll+Ll+LLLLLl+l++lLl+l++lLl+Ll+Ll+Ll+LL+lL+ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+LL+ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+LL+ll+L+l+ll+Ll++lLLl+Ll+Ll+LLLL+ll+l+L+l+lLl+Ll+Ll+LL+ll+l+l++lLLl+Ll+Ll+Ll+LL+lLLl+LL+ll+Ll++lLLl+Ll+Ll+Ll+LLLLLl+LLLLL+lLl+Ll+Ll+LL+ll+L+lLLLl+Ll+Ll+Ll+LL+ll+Ll+LL+ll+Ll++lLLLLl+Ll+Ll++ll+LL+ll+Ll+LL+l+lLl+Ll+Ll+Ll+Ll+Ll+Ll++ll+Ll+Ll+Ll+Ll+LLLll+ll+L+l+ll+Ll+lL+Ll+LL+ll+Ll+Ll++lLLLLl+LLLLL+l+ll++ll+Ll+LL+LLllLLLl+Ll+LLl+LLl+Ll+Ll+Ll+LLLl+L+lLl+ll+ll++ll+Ll+Ll+Ll+Ll+Ll+L+lllLl+Ll+LLllLlLLllLLLLLl+Ll+Ll+Ll+Ll+LL+ll+Ll+Ll+Ll+Ll+Ll+Ll+LLLL+ll+Ll+Ll+Ll+Ll+Ll+LLLl+Ll+LLl+l+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+LL+ll+LL+ll+Ll+Ll+Ll+Ll+LL+ll+LL+ll+Ll+Ll+Ll+Ll+LL+ll+LL+ll+Ll+Ll+Ll+Ll+LL+ll+LL+ll+Ll+Ll+Ll+Ll+LL+ll+LL+ll+Ll+Ll+Ll+LLLLLl+Ll+Ll+Ll+Lterminaltables-3.1.0/tests/test_all_tables_e2e/sub_double_win10b.bmp000066400000000000000000001063721300101162000255530ustar00rootroot00000000000000BM6(MČl+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+L:l+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Lfl+Ll+Ll+Ll+Ll+Ll+LLLl+LLl+Ll+L+lLLl+Ll+Ll+L::l+Ll+LL+ll+LL+lLl+L+ll+Ll+Ll+Ll+Ll+LL+ll+LLLLLLl+l++lLLl+Ll+Ll+Lf:::l+Ll+LL+lLLLLLLLl+L+ll+Ll+Ll+Ll+Ll+LL+lLLL+lL+lLl+L+ll+LLLl+Ll+Lffff:l+Ll+LL+ll+Ll+Ll+LL+lLl+L+lLLl+Ll+Ll+Ll+LL+ll+LL+lL+lL+lLl+L+lLLl+LL+ll+Ll+L:::::::l+Ll+LLLll+ll+LLLLLL+l+l+ll+LL+ll+Ll+Ll+Ll+LL+ll++lLl+Ll+Ll+L+ll+Ll++ll+Ll+Ll+Ll+LLLl+Ll+LLl+l++ll+Ll+Ll+Ll+LL+lL+lL+ll+Ll+Ll+Ll+LL+ll+Ll+Ll+Ll+Ll+LL+lL+ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+LL+ll+Ll++lLL+l+lLl+L+ll+Ll+Lf:ffl+Ll+LL+lLLl++ll+Ll+Ll+Ll+Ll+LLLLLl+Ll++lLLl+LL+lLLLLLLl+Ll+Lfl+Ll+LLl++lLLLLLl+Ll+Ll+Ll+Ll+Ll+LL+ll+Ll++lLLL+lL+ll+LL+ll+Ll+Lffl+Ll+Ll+LL+l+l+ll+Ll+Ll+Ll+Ll+LLLLLl+Ll+lL+L+l+lL+lLLLLl+Ll+L:f:fl+Ll+Ll+LL+lL+ll+Ll+Ll+Ll+Ll+Ll+Ll+ll+l+lLLl+l+Ll+Ll+Lf::l+Ll+Ll+l++lLLl+l+Ll+Ll+Ll+LLLL+ll+Ll+Lfl+Ll+Ll+LLl+l+Ll+Ll+Ll+LL+ll+Ll+Lfl+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+L:l+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Lfl+Ll+Ll+Ll+Ll+Ll+L+l+lLLLLL+lLL+l+lLll+lL+ll+Ll+L::l+Ll+L+l+lL+lL+ll+Ll+Ll+Ll+Ll+LLLLLLLLLLLl+LL+ll+Ll++lLLLLl+Ll+Lf:::l+Ll+LLLLl++lLLl++lLl+Ll+Ll+Ll+Ll+LLLl+LL+ll+LL+ll+LL+lL+lL+ll+LL+ll+Ll+Lffff:l+Ll+LLLl+LL+ll+LL+ll+Ll+Ll+Ll+LLLl++l+lLLl+LLLLLL+l+ll+l+L+lLLLLl+Ll+L:::::::l+Ll+LLLl+LL+ll+LL+ll+Ll+Ll+Ll+LL+Lll+l+LL+ll+Ll+L+lLl++ll+Ll+Ll+Ll+Ll+LLLLLl+l++lLl+l++lLl+Ll+Ll+Ll+LL+lL+ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+LL+ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+LL+ll+L+l+ll+Ll++lLLl+Ll+Ll+LLLL+ll+l+L+l+lLl+Ll+Ll+LL+ll+l+l++lLLl+Ll+Ll+Ll+LL+lLLl+LL+ll+Ll++lLLl+Ll+Ll+Ll+LLLLLl+LLLLL+lLl+Ll+Ll+LL+ll+L+lLLLl+Ll+Ll+Ll+LL+ll+Ll+LL+ll+Ll++lLLLLl+Ll+Ll++ll+LL+ll+Ll+LL+l+lLl+Ll+Ll+Ll+Ll+Ll+Ll++ll+Ll+Ll+Ll+Ll+LLLll+ll+L+l+ll+Ll+lL+Ll+LL+ll+Ll+Ll++lLLLLl+LLLLL+l+ll++ll+Ll+LL+LLllLLLl+Ll+LLl+LLl+Ll+Ll+Ll+LLLl+L+lLl+ll+ll++ll+Ll+Ll+Ll+Ll+Ll+L+lllLl+Ll+LLllLlLLllLLLLLl+Ll+Ll+Ll+Ll+LL+ll+Ll+Ll+Ll+Ll+Ll+Ll+LLLL+ll+Ll+Ll+Ll+Ll+Ll+LLLl+Ll+LLl+l+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+LL+ll+LL+ll+Ll+Ll+Ll+Ll+LL+ll+LL+ll+Ll+Ll+Ll+Ll+LL+ll+LL+ll+Ll+Ll+Ll+Ll+LL+ll+LL+ll+Ll+Ll+Ll+Ll+LL+ll+LL+ll+Ll+Ll+Ll+LLLLLl+Ll+Ll+Ll+Lterminaltables-3.1.0/tests/test_all_tables_e2e/sub_double_winxp.bmp000066400000000000000000001371261300101162000256210ustar00rootroot00000000000000BMV6(N terminaltables-3.1.0/tests/test_all_tables_e2e/sub_single_win10.bmp000066400000000000000000001040461300101162000254140ustar00rootroot00000000000000BM&6(Kl+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+L:l+Ll+Ll+Ll+Lfl+Ll+Ll+LLLl+LLl+Ll+L+lLLl+Ll+L::l+LL+ll+LL+lLl+L+ll+Ll+Ll+LL+ll+LLLLLLl+l++lLLl+Ll+Lf:::l+LL+lLLLLLLLl+L+ll+Ll+Ll+LL+lLLL+lL+lLl+L+ll+LLLl+Lffff:l+LL+ll+Ll+Ll+LL+lLl+L+lLLl+Ll+LL+ll+LL+lL+lL+lLl+L+lLLl+LL+ll+L:::::::l+LLLll+ll+LLLLLL+l+l+ll+LL+ll+Ll+LL+ll++lLl+Ll+Ll+L+ll+Ll++ll+Ll+LLLl+Ll+LLl+l++ll+Ll+LL+lL+lL+ll+Ll+LL+ll+Ll+Ll+LL+lL+ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+LL+ll+Ll++lLL+l+lLl+L+ll+Lf:ffl+LL+lLLl++ll+Ll+Ll+LLLLLl+Ll++lLLl+LL+lLLLLLLl+Lfl+LLl++lLLLLLl+Ll+Ll+Ll+LL+ll+Ll++lLLL+lL+ll+LL+ll+Lffl+Ll+LL+l+l+ll+Ll+Ll+LLLLLl+Ll+lL+L+l+lL+lLLLLl+L:f:fl+Ll+LL+lL+ll+Ll+Ll+Ll+Ll+ll+l+lLLl+l+Ll+Lf::l+Ll+l++lLLl+l+Ll+LLLL+ll+Lfl+Ll+LLl+l+Ll+LL+ll+Lfl+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+L:l+Ll+Ll+Ll+Lfl+Ll+Ll+L+l+lLLLLL+lLL+l+lLll+lL+ll+L::l+L+l+lL+lL+ll+Ll+Ll+LLLLLLLLLLLl+LL+ll+Ll++lLLLLl+Lf:::l+LLLLl++lLLl++lLl+Ll+Ll+LLLl+LL+ll+LL+ll+LL+lL+lL+ll+LL+ll+Lffff:l+LLLl+LL+ll+LL+ll+Ll+LLLl++l+lLLl+LLLLLL+l+ll+l+L+lLLLLl+L:::::::l+LLLl+LL+ll+LL+ll+Ll+LL+Lll+l+LL+ll+Ll+L+lLl++ll+Ll+Ll+LLLLLl+l++lLl+l++lLl+Ll+LL+lL+ll+Ll+Ll+Ll+Ll+Ll+Ll+LL+ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+LL+ll+L+l+ll+Ll++lLLl+Ll+LLLL+ll+l+L+l+lLl+Ll+LL+ll+l+l++lLLl+Ll+LL+lLLl+LL+ll+Ll++lLLl+Ll+Ll+LLLLLl+LLLLL+lLl+Ll+LL+ll+L+lLLLl+Ll+LL+ll+Ll+LL+ll+Ll++lLLLLl+Ll++ll+LL+ll+Ll+LL+l+lLl+Ll+Ll+Ll+Ll+Ll++ll+Ll+Ll+LLLll+ll+L+l+ll+Ll+lL+Ll+LL+ll+Ll++lLLLLl+LLLLL+l+ll++ll+LL+LLllLLLl+Ll+LLl+LLl+Ll+LLLl+L+lLl+ll+ll++ll+Ll+Ll+Ll+Ll+L+lllLl+LLllLlLLllLLLLLl+Ll+Ll+LL+ll+Ll+Ll+Ll+Ll+LLLL+ll+Ll+Ll+LLLl+LLl+l+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Lterminaltables-3.1.0/tests/test_all_tables_e2e/sub_single_win10b.bmp000066400000000000000000001040461300101162000255560ustar00rootroot00000000000000BM&6(Kl+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+L:l+Ll+Ll+Ll+Lfl+Ll+Ll+LLLl+LLl+Ll+L+lLLl+Ll+L::l+LL+ll+LL+lLl+L+ll+Ll+Ll+LL+ll+LLLLLLl+l++lLLl+Ll+Lf:::l+LL+lLLLLLLLl+L+ll+Ll+Ll+LL+lLLL+lL+lLl+L+ll+LLLl+Lffff:l+LL+ll+Ll+Ll+LL+lLl+L+lLLl+Ll+LL+ll+LL+lL+lL+lLl+L+lLLl+LL+ll+L:::::::l+LLLll+ll+LLLLLL+l+l+ll+LL+ll+Ll+LL+ll++lLl+Ll+Ll+L+ll+Ll++ll+Ll+LLLl+Ll+LLl+l++ll+Ll+LL+lL+lL+ll+Ll+LL+ll+Ll+Ll+LL+lL+ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+LL+ll+Ll++lLL+l+lLl+L+ll+Lf:ffl+LL+lLLl++ll+Ll+Ll+LLLLLl+Ll++lLLl+LL+lLLLLLLl+Lfl+LLl++lLLLLLl+Ll+Ll+Ll+LL+ll+Ll++lLLL+lL+ll+LL+ll+Lffl+Ll+LL+l+l+ll+Ll+Ll+LLLLLl+Ll+lL+L+l+lL+lLLLLl+L:f:fl+Ll+LL+lL+ll+Ll+Ll+Ll+Ll+ll+l+lLLl+l+Ll+Lf::l+Ll+l++lLLl+l+Ll+LLLL+ll+Lfl+Ll+LLl+l+Ll+LL+ll+Lfl+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+L:l+Ll+Ll+Ll+Lfl+Ll+Ll+L+l+lLLLLL+lLL+l+lLll+lL+ll+L::l+L+l+lL+lL+ll+Ll+Ll+LLLLLLLLLLLl+LL+ll+Ll++lLLLLl+Lf:::l+LLLLl++lLLl++lLl+Ll+Ll+LLLl+LL+ll+LL+ll+LL+lL+lL+ll+LL+ll+Lffff:l+LLLl+LL+ll+LL+ll+Ll+LLLl++l+lLLl+LLLLLL+l+ll+l+L+lLLLLl+L:::::::l+LLLl+LL+ll+LL+ll+Ll+LL+Lll+l+LL+ll+Ll+L+lLl++ll+Ll+Ll+LLLLLl+l++lLl+l++lLl+Ll+LL+lL+ll+Ll+Ll+Ll+Ll+Ll+Ll+LL+ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+LL+ll+L+l+ll+Ll++lLLl+Ll+LLLL+ll+l+L+l+lLl+Ll+LL+ll+l+l++lLLl+Ll+LL+lLLl+LL+ll+Ll++lLLl+Ll+Ll+LLLLLl+LLLLL+lLl+Ll+LL+ll+L+lLLLl+Ll+LL+ll+Ll+LL+ll+Ll++lLLLLl+Ll++ll+LL+ll+Ll+LL+l+lLl+Ll+Ll+Ll+Ll+Ll++ll+Ll+Ll+LLLll+ll+L+l+ll+Ll+lL+Ll+LL+ll+Ll++lLLLLl+LLLLL+l+ll++ll+LL+LLllLLLl+Ll+LLl+LLl+Ll+LLLl+L+lLl+ll+ll++ll+Ll+Ll+Ll+Ll+L+lllLl+LLllLlLLllLLLLLl+Ll+Ll+LL+ll+Ll+Ll+Ll+Ll+LLLL+ll+Ll+Ll+LLLl+LLl+l+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Ll+Lterminaltables-3.1.0/tests/test_all_tables_e2e/sub_single_winxp.bmp000066400000000000000000001316021300101162000256210ustar00rootroot00000000000000BM6(KLterminaltables-3.1.0/tests/test_all_tables_e2e/test_ascii_table.py000066400000000000000000000142131300101162000254100ustar00rootroot00000000000000"""AsciiTable end to end testing.""" import sys from textwrap import dedent import py import pytest from terminaltables import AsciiTable from terminaltables.terminal_io import IS_WINDOWS from tests import PROJECT_ROOT from tests.screenshot import RunNewConsole, screenshot_until_match HERE = py.path.local(__file__).dirpath() def test_single_line(): """Test single-lined cells.""" table_data = [ ['Name', 'Color', 'Type'], ['Avocado', 'green', 'nut'], ['Tomato', 'red', 'fruit'], ['Lettuce', 'green', 'vegetable'], ['Watermelon', 'green'], [], ] table = AsciiTable(table_data, 'Example') table.inner_footing_row_border = True table.justify_columns[0] = 'left' table.justify_columns[1] = 'center' table.justify_columns[2] = 'right' actual = table.table expected = ( '+Example-----+-------+-----------+\n' '| Name | Color | Type |\n' '+------------+-------+-----------+\n' '| Avocado | green | nut |\n' '| Tomato | red | fruit |\n' '| Lettuce | green | vegetable |\n' '| Watermelon | green | |\n' '+------------+-------+-----------+\n' '| | | |\n' '+------------+-------+-----------+' ) assert actual == expected def test_multi_line(): """Test multi-lined cells.""" table_data = [ ['Show', 'Characters'], ['Rugrats', 'Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles,\nDil Pickles'], ['South Park', 'Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick'] ] table = AsciiTable(table_data) # Test defaults. actual = table.table expected = ( '+------------+-------------------------------------------------------------------------------------+\n' '| Show | Characters |\n' '+------------+-------------------------------------------------------------------------------------+\n' '| Rugrats | Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, |\n' '| | Dil Pickles |\n' '| South Park | Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick |\n' '+------------+-------------------------------------------------------------------------------------+' ) assert actual == expected # Test inner row border. table.inner_row_border = True actual = table.table expected = ( '+------------+-------------------------------------------------------------------------------------+\n' '| Show | Characters |\n' '+------------+-------------------------------------------------------------------------------------+\n' '| Rugrats | Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, |\n' '| | Dil Pickles |\n' '+------------+-------------------------------------------------------------------------------------+\n' '| South Park | Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick |\n' '+------------+-------------------------------------------------------------------------------------+' ) assert actual == expected # Justify right. table.justify_columns = {1: 'right'} actual = table.table expected = ( '+------------+-------------------------------------------------------------------------------------+\n' '| Show | Characters |\n' '+------------+-------------------------------------------------------------------------------------+\n' '| Rugrats | Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, |\n' '| | Dil Pickles |\n' '+------------+-------------------------------------------------------------------------------------+\n' '| South Park | Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick |\n' '+------------+-------------------------------------------------------------------------------------+' ) assert actual == expected @pytest.mark.skipif(str(not IS_WINDOWS)) @pytest.mark.skipif('True') # https://github.com/Robpol86/terminaltables/issues/30 def test_windows_screenshot(tmpdir): """Test on Windows in a new console window. Take a screenshot to verify it works. :param tmpdir: pytest fixture. """ script = tmpdir.join('script.py') command = [sys.executable, str(script)] screenshot = PROJECT_ROOT.join('test_ascii_table.png') if screenshot.check(): screenshot.remove() # Generate script. script_template = dedent(u"""\ from __future__ import print_function import os, time from colorclass import Color, Windows from terminaltables import AsciiTable Windows.enable(auto_colors=True) stop_after = time.time() + 20 table_data = [ [Color('{b}Name{/b}'), Color('{b}Color{/b}'), Color('{b}Misc{/b}')], ['Avocado', Color('{autogreen}green{/fg}'), 100], ['Tomato', Color('{autored}red{/fg}'), 0.5], ['Lettuce', Color('{autogreen}green{/fg}'), None], ] print(AsciiTable(table_data).table) print('Waiting for screenshot_until_match()...') while not os.path.exists(r'%s') and time.time() < stop_after: time.sleep(0.5) """) script_contents = script_template % str(screenshot) script.write(script_contents.encode('utf-8'), mode='wb') # Setup expected. sub_images = [str(p) for p in HERE.listdir('sub_ascii_*.bmp')] assert sub_images # Run. with RunNewConsole(command) as gen: screenshot_until_match(str(screenshot), 15, sub_images, 1, gen) terminaltables-3.1.0/tests/test_all_tables_e2e/test_double_table.py000066400000000000000000000325221300101162000255750ustar00rootroot00000000000000"""DoubleTable end to end testing.""" import sys from textwrap import dedent import py import pytest from terminaltables import DoubleTable from terminaltables.terminal_io import IS_WINDOWS from tests import PROJECT_ROOT from tests.screenshot import RunNewConsole, screenshot_until_match HERE = py.path.local(__file__).dirpath() def test_single_line(): """Test single-lined cells.""" table_data = [ ['Name', 'Color', 'Type'], ['Avocado', 'green', 'nut'], ['Tomato', 'red', 'fruit'], ['Lettuce', 'green', 'vegetable'], ['Watermelon', 'green'], [], ] table = DoubleTable(table_data, 'Example') table.inner_footing_row_border = True table.justify_columns[0] = 'left' table.justify_columns[1] = 'center' table.justify_columns[2] = 'right' actual = table.table expected = ( u'\u2554Example\u2550\u2550\u2550\u2550\u2550\u2566\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2566\u2550\u2550' u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557\n' u'\u2551 Name \u2551 Color \u2551 Type \u2551\n' u'\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256c\u2550\u2550\u2550\u2550' u'\u2550\u2550\u2550\u256c\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563\n' u'\u2551 Avocado \u2551 green \u2551 nut \u2551\n' u'\u2551 Tomato \u2551 red \u2551 fruit \u2551\n' u'\u2551 Lettuce \u2551 green \u2551 vegetable \u2551\n' u'\u2551 Watermelon \u2551 green \u2551 \u2551\n' u'\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256c\u2550\u2550\u2550\u2550' u'\u2550\u2550\u2550\u256c\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563\n' u'\u2551 \u2551 \u2551 \u2551\n' u'\u255a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2569\u2550\u2550\u2550\u2550' u'\u2550\u2550\u2550\u2569\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255d' ) assert actual == expected def test_multi_line(): """Test multi-lined cells.""" table_data = [ ['Show', 'Characters'], ['Rugrats', 'Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles,\nDil Pickles'], ['South Park', 'Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick'] ] table = DoubleTable(table_data) # Test defaults. actual = table.table expected = ( u'\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2566\u2550\u2550\u2550\u2550' u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557\n' u'\u2551 Show \u2551 Characters ' u'\u2551\n' u'\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256c\u2550\u2550\u2550\u2550' u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563\n' u'\u2551 Rugrats \u2551 Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, ' u'\u2551\n' u'\u2551 \u2551 Dil Pickles ' u'\u2551\n' u'\u2551 South Park \u2551 Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick ' u'\u2551\n' u'\u255a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2569\u2550\u2550\u2550\u2550' u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255d' ) assert actual == expected # Test inner row border. table.inner_row_border = True actual = table.table expected = ( u'\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2566\u2550\u2550\u2550\u2550' u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557\n' u'\u2551 Show \u2551 Characters ' u'\u2551\n' u'\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256c\u2550\u2550\u2550\u2550' u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563\n' u'\u2551 Rugrats \u2551 Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, ' u'\u2551\n' u'\u2551 \u2551 Dil Pickles ' u'\u2551\n' u'\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256c\u2550\u2550\u2550\u2550' u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563\n' u'\u2551 South Park \u2551 Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick ' u'\u2551\n' u'\u255a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2569\u2550\u2550\u2550\u2550' u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255d' ) assert actual == expected # Justify right. table.justify_columns = {1: 'right'} actual = table.table expected = ( u'\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2566\u2550\u2550\u2550\u2550' u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557\n' u'\u2551 Show \u2551 Characters ' u'\u2551\n' u'\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256c\u2550\u2550\u2550\u2550' u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563\n' u'\u2551 Rugrats \u2551 Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, ' u'\u2551\n' u'\u2551 \u2551 Dil Pickles ' u'\u2551\n' u'\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256c\u2550\u2550\u2550\u2550' u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563\n' u'\u2551 South Park \u2551 Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick ' u'\u2551\n' u'\u255a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2569\u2550\u2550\u2550\u2550' u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550' u'\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255d' ) assert actual == expected @pytest.mark.skipif(str(not IS_WINDOWS)) @pytest.mark.skipif('True') # https://github.com/Robpol86/terminaltables/issues/30 def test_windows_screenshot(tmpdir): """Test on Windows in a new console window. Take a screenshot to verify it works. :param tmpdir: pytest fixture. """ script = tmpdir.join('script.py') command = [sys.executable, str(script)] screenshot = PROJECT_ROOT.join('test_double_table.png') if screenshot.check(): screenshot.remove() # Generate script. script_template = dedent(u"""\ from __future__ import print_function import os, time from colorclass import Color, Windows from terminaltables import DoubleTable Windows.enable(auto_colors=True) stop_after = time.time() + 20 table_data = [ [Color('{b}Name{/b}'), Color('{b}Color{/b}'), Color('{b}Misc{/b}')], ['Avocado', Color('{autogreen}green{/fg}'), 100], ['Tomato', Color('{autored}red{/fg}'), 0.5], ['Lettuce', Color('{autogreen}green{/fg}'), None], ] print(DoubleTable(table_data).table) print('Waiting for screenshot_until_match()...') while not os.path.exists(r'%s') and time.time() < stop_after: time.sleep(0.5) """) script_contents = script_template % str(screenshot) script.write(script_contents.encode('utf-8'), mode='wb') # Setup expected. sub_images = [str(p) for p in HERE.listdir('sub_double_*.bmp')] assert sub_images # Run. with RunNewConsole(command) as gen: screenshot_until_match(str(screenshot), 15, sub_images, 1, gen) terminaltables-3.1.0/tests/test_all_tables_e2e/test_github_table.py000066400000000000000000000065771300101162000256200ustar00rootroot00000000000000"""GithubFlavoredMarkdownTable end to end testing.""" from terminaltables import GithubFlavoredMarkdownTable def test_single_line(): """Test single-lined cells.""" table_data = [ ['Name', 'Color', 'Type'], ['Avocado', 'green', 'nut'], ['Tomato', 'red', 'fruit'], ['Lettuce', 'green', 'vegetable'], ['Watermelon', 'green'], [], ] table = GithubFlavoredMarkdownTable(table_data) table.inner_footing_row_border = True table.justify_columns[0] = 'left' table.justify_columns[1] = 'center' table.justify_columns[2] = 'right' actual = table.table expected = ( '| Name | Color | Type |\n' '|:-----------|:-----:|----------:|\n' '| Avocado | green | nut |\n' '| Tomato | red | fruit |\n' '| Lettuce | green | vegetable |\n' '| Watermelon | green | |\n' '| | | |' ) assert actual == expected def test_multi_line(): """Test multi-lined cells.""" table_data = [ ['Show', 'Characters'], ['Rugrats', 'Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles,\nDil Pickles'], ['South Park', 'Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick'] ] table = GithubFlavoredMarkdownTable(table_data) # Test defaults. actual = table.table expected = ( '| Show | Characters |\n' '|------------|-------------------------------------------------------------------------------------|\n' '| Rugrats | Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, |\n' '| | Dil Pickles |\n' '| South Park | Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick |' ) assert actual == expected # Test inner row border. table.inner_row_border = True actual = table.table expected = ( '| Show | Characters |\n' '|------------|-------------------------------------------------------------------------------------|\n' '| Rugrats | Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, |\n' '| | Dil Pickles |\n' '| South Park | Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick |' ) assert actual == expected # Justify right. table.justify_columns = {1: 'right'} actual = table.table expected = ( '| Show | Characters |\n' '|------------|------------------------------------------------------------------------------------:|\n' '| Rugrats | Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, |\n' '| | Dil Pickles |\n' '| South Park | Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick |' ) assert actual == expected terminaltables-3.1.0/tests/test_all_tables_e2e/test_porcelain_table.py000066400000000000000000000043711300101162000263000ustar00rootroot00000000000000"""PorcelainTable end to end testing.""" from terminaltables import PorcelainTable def test_single_line(): """Test single-lined cells.""" table_data = [ ['Name', 'Color', 'Type'], ['Avocado', 'green', 'nut'], ['Tomato', 'red', 'fruit'], ['Lettuce', 'green', 'vegetable'], ['Watermelon', 'green'] ] table = PorcelainTable(table_data) table.justify_columns[0] = 'left' table.justify_columns[1] = 'center' table.justify_columns[2] = 'right' actual = table.table expected = ( ' Name | Color | Type \n' ' Avocado | green | nut \n' ' Tomato | red | fruit \n' ' Lettuce | green | vegetable \n' ' Watermelon | green | ' ) assert actual == expected def test_multi_line(): """Test multi-lined cells.""" table_data = [ ['Show', 'Characters'], ['Rugrats', 'Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles,\nDil Pickles'], ['South Park', 'Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick'] ] table = PorcelainTable(table_data) # Test defaults. actual = table.table expected = ( ' Show | Characters \n' ' Rugrats | Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, \n' ' | Dil Pickles \n' ' South Park | Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick ' ) assert actual == expected # Justify right. table.justify_columns = {1: 'right'} actual = table.table expected = ( ' Show | Characters \n' ' Rugrats | Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, \n' ' | Dil Pickles \n' ' South Park | Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick ' ) assert actual == expected terminaltables-3.1.0/tests/test_all_tables_e2e/test_single_table.py000066400000000000000000000233021300101162000256000ustar00rootroot00000000000000"""SingleTable end to end testing on Linux/OSX.""" import pytest from terminaltables import SingleTable from terminaltables.terminal_io import IS_WINDOWS pytestmark = pytest.mark.skipif(str(IS_WINDOWS)) def test_single_line(): """Test single-lined cells.""" table_data = [ ['Name', 'Color', 'Type'], ['Avocado', 'green', 'nut'], ['Tomato', 'red', 'fruit'], ['Lettuce', 'green', 'vegetable'], ['Watermelon', 'green'], [], ] table = SingleTable(table_data, 'Example') table.inner_footing_row_border = True table.justify_columns[0] = 'left' table.justify_columns[1] = 'center' table.justify_columns[2] = 'right' actual = table.table expected = ( '\033(0\x6c\033(BExample\033(0\x71\x71\x71\x71\x71\x77\x71\x71\x71\x71\x71\x71\x71\x77\x71\x71\x71\x71\x71\x71' '\x71\x71\x71\x71\x71\x6b\033(B\n' '\033(0\x78\033(B Name \033(0\x78\033(B Color \033(0\x78\033(B Type \033(0\x78\033(B\n' '\033(0\x74\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x6e\x71\x71\x71\x71\x71\x71\x71\x6e\x71\x71\x71\x71' '\x71\x71\x71\x71\x71\x71\x71\x75\033(B\n' '\033(0\x78\033(B Avocado \033(0\x78\033(B green \033(0\x78\033(B nut \033(0\x78\033(B\n' '\033(0\x78\033(B Tomato \033(0\x78\033(B red \033(0\x78\033(B fruit \033(0\x78\033(B\n' '\033(0\x78\033(B Lettuce \033(0\x78\033(B green \033(0\x78\033(B vegetable \033(0\x78\033(B\n' '\033(0\x78\033(B Watermelon \033(0\x78\033(B green \033(0\x78\033(B \033(0\x78\033(B\n' '\033(0\x74\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x6e\x71\x71\x71\x71\x71\x71\x71\x6e\x71\x71\x71\x71' '\x71\x71\x71\x71\x71\x71\x71\x75\033(B\n' '\033(0\x78\033(B \033(0\x78\033(B \033(0\x78\033(B \033(0\x78\033(B\n' '\033(0\x6d\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x76\x71\x71\x71\x71\x71\x71\x71\x76\x71\x71\x71\x71' '\x71\x71\x71\x71\x71\x71\x71\x6a\033(B' ) assert actual == expected def test_multi_line(): """Test multi-lined cells.""" table_data = [ ['Show', 'Characters'], ['Rugrats', 'Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles,\nDil Pickles'], ['South Park', 'Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick'] ] table = SingleTable(table_data) # Test defaults. actual = table.table expected = ( '\033(0\x6c\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x77\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x6b\033(B\n' '\033(0\x78\033(B Show \033(0\x78\033(B Characters ' ' \033(0\x78\033(B\n' '\033(0\x74\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x6e\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x75\033(B\n' '\033(0\x78\033(B Rugrats \033(0\x78\033(B Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille,' ' Angelica Pickles, \033(0\x78\033(B\n' '\033(0\x78\033(B \033(0\x78\033(B Dil Pickles ' ' \033(0\x78\033(B\n' '\033(0\x78\033(B South Park \033(0\x78\033(B Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick ' ' \033(0\x78\033(B\n' '\033(0\x6d\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x76\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x6a\033(B' ) assert actual == expected # Test inner row border. table.inner_row_border = True actual = table.table expected = ( '\033(0\x6c\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x77\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x6b\033(B\n' '\033(0\x78\033(B Show \033(0\x78\033(B Characters ' ' \033(0\x78\033(B\n' '\033(0\x74\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x6e\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x75\033(B\n' '\033(0\x78\033(B Rugrats \033(0\x78\033(B Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille,' ' Angelica Pickles, \033(0\x78\033(B\n' '\033(0\x78\033(B \033(0\x78\033(B Dil Pickles ' ' \033(0\x78\033(B\n' '\033(0\x74\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x6e\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x75\033(B\n' '\033(0\x78\033(B South Park \033(0\x78\033(B Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick ' ' \033(0\x78\033(B\n' '\033(0\x6d\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x76\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x6a\033(B' ) assert actual == expected # Justify right. table.justify_columns = {1: 'right'} actual = table.table expected = ( '\033(0\x6c\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x77\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x6b\033(B\n' '\033(0\x78\033(B Show \033(0\x78\033(B ' ' Characters \033(0\x78\033(B\n' '\033(0\x74\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x6e\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x75\033(B\n' '\033(0\x78\033(B Rugrats \033(0\x78\033(B Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille,' ' Angelica Pickles, \033(0\x78\033(B\n' '\033(0\x78\033(B \033(0\x78\033(B ' ' Dil Pickles \033(0\x78\033(B\n' '\033(0\x74\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x6e\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x75\033(B\n' '\033(0\x78\033(B South Park \033(0\x78\033(B Stan Marsh, Kyle Broflovski, ' 'Eric Cartman, Kenny McCormick \033(0\x78\033(B\n' '\033(0\x6d\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x76\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71' '\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x71\x6a\033(B' ) assert actual == expected terminaltables-3.1.0/tests/test_all_tables_e2e/test_single_table_windows.py000066400000000000000000000326221300101162000273570ustar00rootroot00000000000000"""SingleTable end to end testing on Windows.""" import sys from textwrap import dedent import py import pytest from terminaltables import SingleTable from terminaltables.terminal_io import IS_WINDOWS from tests import PROJECT_ROOT from tests.screenshot import RunNewConsole, screenshot_until_match HERE = py.path.local(__file__).dirpath() pytestmark = pytest.mark.skipif(str(not IS_WINDOWS)) def test_single_line(): """Test single-lined cells.""" table_data = [ ['Name', 'Color', 'Type'], ['Avocado', 'green', 'nut'], ['Tomato', 'red', 'fruit'], ['Lettuce', 'green', 'vegetable'], ['Watermelon', 'green'], [], ] table = SingleTable(table_data, 'Example') table.inner_footing_row_border = True table.justify_columns[0] = 'left' table.justify_columns[1] = 'center' table.justify_columns[2] = 'right' actual = table.table expected = ( u'\u250cExample\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500' u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n' u'\u2502 Name \u2502 Color \u2502 Type \u2502\n' u'\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500' u'\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n' u'\u2502 Avocado \u2502 green \u2502 nut \u2502\n' u'\u2502 Tomato \u2502 red \u2502 fruit \u2502\n' u'\u2502 Lettuce \u2502 green \u2502 vegetable \u2502\n' u'\u2502 Watermelon \u2502 green \u2502 \u2502\n' u'\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500' u'\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n' u'\u2502 \u2502 \u2502 \u2502\n' u'\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500' u'\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518' ) assert actual == expected def test_multi_line(): """Test multi-lined cells.""" table_data = [ ['Show', 'Characters'], ['Rugrats', 'Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles,\nDil Pickles'], ['South Park', 'Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick'] ] table = SingleTable(table_data) # Test defaults. actual = table.table expected = ( u'\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500' u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n' u'\u2502 Show \u2502 Characters ' u'\u2502\n' u'\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500' u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n' u'\u2502 Rugrats \u2502 Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, ' u'\u2502\n' u'\u2502 \u2502 Dil Pickles ' u'\u2502\n' u'\u2502 South Park \u2502 Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick ' u'\u2502\n' u'\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500' u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518' ) assert actual == expected # Test inner row border. table.inner_row_border = True actual = table.table expected = ( u'\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500' u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n' u'\u2502 Show \u2502 Characters ' u'\u2502\n' u'\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500' u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n' u'\u2502 Rugrats \u2502 Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, ' u'\u2502\n' u'\u2502 \u2502 Dil Pickles ' u'\u2502\n' u'\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500' u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n' u'\u2502 South Park \u2502 Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick ' u'\u2502\n' u'\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500' u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518' ) assert actual == expected # Justify right. table.justify_columns = {1: 'right'} actual = table.table expected = ( u'\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500' u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n' u'\u2502 Show \u2502 Characters ' u'\u2502\n' u'\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500' u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n' u'\u2502 Rugrats \u2502 Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles, ' u'\u2502\n' u'\u2502 \u2502 Dil Pickles ' u'\u2502\n' u'\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500' u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n' u'\u2502 South Park \u2502 Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick ' u'\u2502\n' u'\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500' u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500' u'\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518' ) assert actual == expected @pytest.mark.skipif(str(not IS_WINDOWS)) @pytest.mark.skipif('True') # https://github.com/Robpol86/terminaltables/issues/30 def test_windows_screenshot(tmpdir): """Test on Windows in a new console window. Take a screenshot to verify it works. :param tmpdir: pytest fixture. """ script = tmpdir.join('script.py') command = [sys.executable, str(script)] screenshot = PROJECT_ROOT.join('test_single_table.png') if screenshot.check(): screenshot.remove() # Generate script. script_template = dedent(u"""\ from __future__ import print_function import os, time from colorclass import Color, Windows from terminaltables import SingleTable Windows.enable(auto_colors=True) stop_after = time.time() + 20 table_data = [ [Color('{b}Name{/b}'), Color('{b}Color{/b}'), Color('{b}Misc{/b}')], ['Avocado', Color('{autogreen}green{/fg}'), 100], ['Tomato', Color('{autored}red{/fg}'), 0.5], ['Lettuce', Color('{autogreen}green{/fg}'), None], ] print(SingleTable(table_data).table) print('Waiting for screenshot_until_match()...') while not os.path.exists(r'%s') and time.time() < stop_after: time.sleep(0.5) """) script_contents = script_template % str(screenshot) script.write(script_contents.encode('utf-8'), mode='wb') # Setup expected. sub_images = [str(p) for p in HERE.listdir('sub_single_*.bmp')] assert sub_images # Run. with RunNewConsole(command) as gen: screenshot_until_match(str(screenshot), 15, sub_images, 1, gen) terminaltables-3.1.0/tests/test_ascii_table.py000066400000000000000000000061441300101162000215200ustar00rootroot00000000000000"""Test AsciiTable class.""" import pytest from terminaltables.other_tables import AsciiTable SINGLE_LINE = ( ('Name', 'Color', 'Type'), ('Avocado', 'green', 'nut'), ('Tomato', 'red', 'fruit'), ('Lettuce', 'green', 'vegetable'), ) MULTI_LINE = ( ('Show', 'Characters'), ('Rugrats', 'Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles,\nDil Pickles'), ('South Park', 'Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick'), ) @pytest.fixture(autouse=True) def patch(monkeypatch): """Monkeypatch before every test function in this module. :param monkeypatch: pytest fixture. """ monkeypatch.setattr('terminaltables.ascii_table.terminal_size', lambda: (79, 24)) monkeypatch.setattr('terminaltables.width_and_alignment.terminal_size', lambda: (79, 24)) @pytest.mark.parametrize('table_data,column_number,expected', [ ([], 0, IndexError), ([[]], 0, IndexError), ([['']], 1, IndexError), (SINGLE_LINE, 0, 55), (SINGLE_LINE, 1, 53), (SINGLE_LINE, 2, 57), (MULTI_LINE, 0, -11), (MULTI_LINE, 1, 62), ]) def test_column_max_width(table_data, column_number, expected): """Test method in class. :param iter table_data: Passed to AsciiTable.__init__(). :param int column_number: Passed to AsciiTable.column_max_width(). :param int expected: Expected return value of AsciiTable.column_max_width(). """ table = AsciiTable(table_data) if expected == IndexError: with pytest.raises(IndexError): table.column_max_width(column_number) return actual = table.column_max_width(column_number) assert actual == expected def test_column_widths(): """Test method in class.""" assert AsciiTable([]).column_widths == list() table = AsciiTable(SINGLE_LINE) actual = table.column_widths assert actual == [7, 5, 9] @pytest.mark.parametrize('table_data,terminal_width,expected', [ ([], None, True), ([[]], None, True), ([['']], None, True), (SINGLE_LINE, None, True), (SINGLE_LINE, 30, False), (MULTI_LINE, None, False), (MULTI_LINE, 100, True), ]) def test_ok(monkeypatch, table_data, terminal_width, expected): """Test method in class. :param monkeypatch: pytest fixture. :param iter table_data: Passed to AsciiTable.__init__(). :param int terminal_width: Monkeypatch width of terminal_size() if not None. :param bool expected: Expected return value. """ if terminal_width is not None: monkeypatch.setattr('terminaltables.ascii_table.terminal_size', lambda: (terminal_width, 24)) table = AsciiTable(table_data) actual = table.ok assert actual is expected @pytest.mark.parametrize('table_data,expected', [ ([], 2), ([[]], 2), ([['']], 4), ([[' ']], 5), (SINGLE_LINE, 31), (MULTI_LINE, 100), ]) def test_table_width(table_data, expected): """Test method in class. :param iter table_data: Passed to AsciiTable.__init__(). :param int expected: Expected return value. """ table = AsciiTable(table_data) actual = table.table_width assert actual == expected terminaltables-3.1.0/tests/test_base_table/000077500000000000000000000000001300101162000207635ustar00rootroot00000000000000terminaltables-3.1.0/tests/test_base_table/test_gen_row_lines.py000066400000000000000000000047041300101162000252330ustar00rootroot00000000000000"""Test method in BaseTable class.""" import pytest from terminaltables.base_table import BaseTable @pytest.mark.parametrize('style', ['heading', 'footing', 'row']) def test_single_line(style): """Test with single-line row. :param str style: Passed to method. """ row = ['Row One Column One', 'Two', 'Three'] table = BaseTable([row]) actual = [tuple(i) for i in table.gen_row_lines(row, style, [18, 3, 5], 1)] expected = [ ('|', ' Row One Column One ', '|', ' Two ', '|', ' Three ', '|'), ] assert actual == expected @pytest.mark.parametrize('style', ['heading', 'footing', 'row']) def test_multi_line(style): """Test with multi-line row. :param str style: Passed to method. """ row = ['Row One\nColumn One', 'Two', 'Three'] table = BaseTable([row]) actual = [tuple(i) for i in table.gen_row_lines(row, style, [10, 3, 5], 2)] expected = [ ('|', ' Row One ', '|', ' Two ', '|', ' Three ', '|'), ('|', ' Column One ', '|', ' ', '|', ' ', '|'), ] assert actual == expected @pytest.mark.parametrize('style', ['heading', 'footing', 'row']) def test_no_padding_no_borders(style): """Test without padding or borders. :param str style: Passed to method. """ row = ['Row One\nColumn One', 'Two', 'Three'] table = BaseTable([row]) table.inner_column_border = False table.outer_border = False table.padding_left = 0 table.padding_right = 0 actual = [tuple(i) for i in table.gen_row_lines(row, style, [10, 3, 5], 2)] expected = [ ('Row One ', 'Two', 'Three'), ('Column One', ' ', ' '), ] assert actual == expected @pytest.mark.parametrize('style', ['heading', 'footing', 'row']) def test_uneven(style): """Test with row missing cells. :param str style: Passed to method. """ row = ['Row One Column One'] table = BaseTable([row]) actual = [tuple(i) for i in table.gen_row_lines(row, style, [18, 3, 5], 1)] expected = [ ('|', ' Row One Column One ', '|', ' ', '|', ' ', '|'), ] assert actual == expected @pytest.mark.parametrize('style', ['heading', 'footing', 'row']) def test_empty_table(style): """Test empty table. :param str style: Passed to method. """ row = [] table = BaseTable([row]) actual = [tuple(i) for i in table.gen_row_lines(row, style, [], 0)] expected = [ ('|', '|'), ] assert actual == expected terminaltables-3.1.0/tests/test_base_table/test_gen_table.py000066400000000000000000000162051300101162000243200ustar00rootroot00000000000000"""Test method in BaseTable class.""" import pytest from terminaltables.base_table import BaseTable from terminaltables.build import flatten from terminaltables.width_and_alignment import max_dimensions @pytest.mark.parametrize('inner_heading_row_border', [True, False]) @pytest.mark.parametrize('inner_footing_row_border', [True, False]) @pytest.mark.parametrize('inner_row_border', [True, False]) def test_inner_row_borders(inner_heading_row_border, inner_footing_row_border, inner_row_border): """Test heading/footing/row borders. :param bool inner_heading_row_border: Passed to table. :param bool inner_footing_row_border: Passed to table. :param bool inner_row_border: Passed to table. """ table_data = [ ['Name', 'Color', 'Type'], ['Avocado', 'green', 'nut'], ['Tomato', 'red', 'fruit'], ['Lettuce', 'green', 'vegetable'], ] table = BaseTable(table_data) table.inner_heading_row_border = inner_heading_row_border table.inner_footing_row_border = inner_footing_row_border table.inner_row_border = inner_row_border inner_widths, inner_heights, outer_widths = max_dimensions(table_data, table.padding_left, table.padding_right)[:3] actual = flatten(table.gen_table(inner_widths, inner_heights, outer_widths)) # Determine expected. if inner_row_border: expected = ( '+---------+-------+-----------+\n' '| Name | Color | Type |\n' '+---------+-------+-----------+\n' '| Avocado | green | nut |\n' '+---------+-------+-----------+\n' '| Tomato | red | fruit |\n' '+---------+-------+-----------+\n' '| Lettuce | green | vegetable |\n' '+---------+-------+-----------+' ) elif inner_heading_row_border and inner_footing_row_border: expected = ( '+---------+-------+-----------+\n' '| Name | Color | Type |\n' '+---------+-------+-----------+\n' '| Avocado | green | nut |\n' '| Tomato | red | fruit |\n' '+---------+-------+-----------+\n' '| Lettuce | green | vegetable |\n' '+---------+-------+-----------+' ) elif inner_heading_row_border: expected = ( '+---------+-------+-----------+\n' '| Name | Color | Type |\n' '+---------+-------+-----------+\n' '| Avocado | green | nut |\n' '| Tomato | red | fruit |\n' '| Lettuce | green | vegetable |\n' '+---------+-------+-----------+' ) elif inner_footing_row_border: expected = ( '+---------+-------+-----------+\n' '| Name | Color | Type |\n' '| Avocado | green | nut |\n' '| Tomato | red | fruit |\n' '+---------+-------+-----------+\n' '| Lettuce | green | vegetable |\n' '+---------+-------+-----------+' ) else: expected = ( '+---------+-------+-----------+\n' '| Name | Color | Type |\n' '| Avocado | green | nut |\n' '| Tomato | red | fruit |\n' '| Lettuce | green | vegetable |\n' '+---------+-------+-----------+' ) assert actual == expected @pytest.mark.parametrize('outer_border', [True, False]) def test_outer_borders(outer_border): """Test left/right/top/bottom table borders. :param bool outer_border: Passed to table. """ table_data = [ ['Name', 'Color', 'Type'], ['Avocado', 'green', 'nut'], ['Tomato', 'red', 'fruit'], ['Lettuce', 'green', 'vegetable'], ] table = BaseTable(table_data, 'Example Table') table.outer_border = outer_border inner_widths, inner_heights, outer_widths = max_dimensions(table_data, table.padding_left, table.padding_right)[:3] actual = flatten(table.gen_table(inner_widths, inner_heights, outer_widths)) # Determine expected. if outer_border: expected = ( '+Example Table----+-----------+\n' '| Name | Color | Type |\n' '+---------+-------+-----------+\n' '| Avocado | green | nut |\n' '| Tomato | red | fruit |\n' '| Lettuce | green | vegetable |\n' '+---------+-------+-----------+' ) else: expected = ( ' Name | Color | Type \n' '---------+-------+-----------\n' ' Avocado | green | nut \n' ' Tomato | red | fruit \n' ' Lettuce | green | vegetable ' ) assert actual == expected @pytest.mark.parametrize('mode', ['row', 'one', 'blank', 'empty', 'none']) @pytest.mark.parametrize('bare', [False, True]) def test_one_no_rows(mode, bare): """Test with one or no rows. :param str mode: Type of table contents to test. :param bool bare: Disable padding/borders. """ if mode == 'row': table_data = [ ['Avocado', 'green', 'nut'], ] elif mode == 'one': table_data = [ ['Avocado'], ] elif mode == 'blank': table_data = [ [''], ] elif mode == 'empty': table_data = [ [], ] else: table_data = [ ] table = BaseTable(table_data) if bare: table.inner_column_border = False table.inner_footing_row_border = False table.inner_heading_row_border = False table.inner_row_border = False table.outer_border = False table.padding_left = 0 table.padding_right = 0 inner_widths, inner_heights, outer_widths = max_dimensions(table_data, table.padding_left, table.padding_right)[:3] actual = flatten(table.gen_table(inner_widths, inner_heights, outer_widths)) # Determine expected. if mode == 'row': if bare: expected = ( 'Avocadogreennut' ) else: expected = ( '+---------+-------+-----+\n' '| Avocado | green | nut |\n' '+---------+-------+-----+' ) elif mode == 'one': if bare: expected = ( 'Avocado' ) else: expected = ( '+---------+\n' '| Avocado |\n' '+---------+' ) elif mode == 'blank': # Remember there's still padding. if bare: expected = ( '' ) else: expected = ( '+--+\n' '| |\n' '+--+' ) elif mode == 'empty': if bare: expected = ( '' ) else: expected = ( '++\n' '||\n' '++' ) else: if bare: expected = ( '' ) else: expected = ( '++\n' '++' ) assert actual == expected terminaltables-3.1.0/tests/test_base_table/test_horizontal_border.py000066400000000000000000000071311300101162000261240ustar00rootroot00000000000000"""Test method in BaseTable class.""" import pytest from terminaltables.base_table import BaseTable from terminaltables.width_and_alignment import max_dimensions SINGLE_LINE = ( ('Name', 'Color', 'Type'), ('Avocado', 'green', 'nut'), ('Tomato', 'red', 'fruit'), ('Lettuce', 'green', 'vegetable'), ) @pytest.mark.parametrize('inner_column_border', [True, False]) @pytest.mark.parametrize('style', ['top', 'bottom']) def test_top_bottom(inner_column_border, style): """Test top and bottom borders. :param bool inner_column_border: Passed to table class. :param str style: Passed to method. """ table = BaseTable(SINGLE_LINE, 'Example') table.inner_column_border = inner_column_border outer_widths = max_dimensions(table.table_data, table.padding_left, table.padding_right)[2] # Determine expected. if style == 'top' and inner_column_border: expected = '+Example--+-------+-----------+' elif style == 'top': expected = '+Example--------------------+' elif style == 'bottom' and inner_column_border: expected = '+---------+-------+-----------+' else: expected = '+---------------------------+' # Test. actual = ''.join(table.horizontal_border(style, outer_widths)) assert actual == expected @pytest.mark.parametrize('inner_column_border', [True, False]) @pytest.mark.parametrize('outer_border', [True, False]) @pytest.mark.parametrize('style', ['heading', 'footing']) def test_heading_footing(inner_column_border, outer_border, style): """Test heading and footing borders. :param bool inner_column_border: Passed to table class. :param bool outer_border: Passed to table class. :param str style: Passed to method. """ table = BaseTable(SINGLE_LINE) table.inner_column_border = inner_column_border table.outer_border = outer_border outer_widths = max_dimensions(table.table_data, table.padding_left, table.padding_right)[2] # Determine expected. if style == 'heading' and outer_border: expected = '+---------+-------+-----------+' if inner_column_border else '+---------------------------+' elif style == 'heading': expected = '---------+-------+-----------' if inner_column_border else '---------------------------' elif style == 'footing' and outer_border: expected = '+---------+-------+-----------+' if inner_column_border else '+---------------------------+' else: expected = '---------+-------+-----------' if inner_column_border else '---------------------------' # Test. actual = ''.join(table.horizontal_border(style, outer_widths)) assert actual == expected @pytest.mark.parametrize('inner_column_border', [True, False]) @pytest.mark.parametrize('outer_border', [True, False]) def test_row(inner_column_border, outer_border): """Test inner borders. :param bool inner_column_border: Passed to table class. :param bool outer_border: Passed to table class. """ table = BaseTable(SINGLE_LINE) table.inner_column_border = inner_column_border table.outer_border = outer_border outer_widths = max_dimensions(table.table_data, table.padding_left, table.padding_right)[2] # Determine expected. if inner_column_border and outer_border: expected = '+---------+-------+-----------+' elif inner_column_border: expected = '---------+-------+-----------' elif outer_border: expected = '+---------------------------+' else: expected = '---------------------------' # Test. actual = ''.join(table.horizontal_border('row', outer_widths)) assert actual == expected terminaltables-3.1.0/tests/test_base_table/test_table.py000066400000000000000000000122121300101162000234610ustar00rootroot00000000000000# coding: utf-8 """Test property in BaseTable class.""" from colorama import Fore from colorclass import Color from termcolor import colored from terminaltables.base_table import BaseTable def test_ascii(): """Test with ASCII characters.""" table_data = [ ['Name', 'Color', 'Type'], ['Avocado', 'green', 'nut'], ['Tomato', 'red', 'fruit'], ['Lettuce', 'green', 'vegetable'], ] table = BaseTable(table_data) actual = table.table expected = ( '+---------+-------+-----------+\n' '| Name | Color | Type |\n' '+---------+-------+-----------+\n' '| Avocado | green | nut |\n' '| Tomato | red | fruit |\n' '| Lettuce | green | vegetable |\n' '+---------+-------+-----------+' ) assert actual == expected def test_int(): """Test with integers instead of strings.""" table_data = [ [100, 10, 1], [0, 3, 6], [1, 4, 7], [2, 5, 8], ] table = BaseTable(table_data, 1234567890) actual = table.table expected = ( '+1234567890+---+\n' '| 100 | 10 | 1 |\n' '+-----+----+---+\n' '| 0 | 3 | 6 |\n' '| 1 | 4 | 7 |\n' '| 2 | 5 | 8 |\n' '+-----+----+---+' ) assert actual == expected def test_float(): """Test with floats instead of strings.""" table_data = [ [1.0, 22.0, 333.0], [0.1, 3.1, 6.1], [1.1, 4.1, 7.1], [2.1, 5.1, 8.1], ] table = BaseTable(table_data, 0.12345678) actual = table.table expected = ( '+0.12345678--+-------+\n' '| 1.0 | 22.0 | 333.0 |\n' '+-----+------+-------+\n' '| 0.1 | 3.1 | 6.1 |\n' '| 1.1 | 4.1 | 7.1 |\n' '| 2.1 | 5.1 | 8.1 |\n' '+-----+------+-------+' ) assert actual == expected def test_bool_none(): """Test with NoneType/boolean instead of strings.""" table_data = [ [True, False, None], [True, False, None], [False, None, True], [None, True, False], ] table = BaseTable(table_data, True) actual = table.table expected = ( '+True---+-------+-------+\n' '| True | False | None |\n' '+-------+-------+-------+\n' '| True | False | None |\n' '| False | None | True |\n' '| None | True | False |\n' '+-------+-------+-------+' ) assert actual == expected def test_cjk(): """Test with CJK characters.""" table_data = [ ['CJK'], ['蓝色'], ['世界你好'], ] table = BaseTable(table_data) actual = table.table expected = ( '+----------+\n' '| CJK |\n' '+----------+\n' '| 蓝色 |\n' '| 世界你好 |\n' '+----------+' ) assert actual == expected def test_rtl(): """Test with RTL characters.""" table_data = [ ['RTL'], ['שלום'], ['معرب'], ] table = BaseTable(table_data) actual = table.table expected = ( '+------+\n' '| RTL |\n' '+------+\n' '| שלום |\n' '| معرب |\n' '+------+' ) assert actual == expected def test_rtl_large(): """Test large table of RTL characters.""" table_data = [ ['اكتب', 'اللون', 'اسم'], ['البندق', 'أخضر', 'أفوكادو'], ['ثمرة', 'أحمر', 'بندورة'], ['الخضروات', 'أخضر', 'الخس'], ] table = BaseTable(table_data, 'جوجل المترجم') actual = table.table expected = ( '+جوجل المترجم------+---------+\n' '| اكتب | اللون | اسم |\n' '+----------+-------+---------+\n' '| البندق | أخضر | أفوكادو |\n' '| ثمرة | أحمر | بندورة |\n' '| الخضروات | أخضر | الخس |\n' '+----------+-------+---------+' ) assert actual == expected def test_color(): """Test with color characters.""" table_data = [ ['ansi', '\033[31mRed\033[39m', '\033[32mGreen\033[39m', '\033[34mBlue\033[39m'], ['colorclass', Color('{red}Red{/red}'), Color('{green}Green{/green}'), Color('{blue}Blue{/blue}')], ['colorama', Fore.RED + 'Red' + Fore.RESET, Fore.GREEN + 'Green' + Fore.RESET, Fore.BLUE + 'Blue' + Fore.RESET], ['termcolor', colored('Red', 'red'), colored('Green', 'green'), colored('Blue', 'blue')], ] table = BaseTable(table_data) table.inner_heading_row_border = False actual = table.table expected = ( u'+------------+-----+-------+------+\n' u'| ansi | \033[31mRed\033[39m | \033[32mGreen\033[39m | \033[34mBlue\033[39m |\n' u'| colorclass | \033[31mRed\033[39m | \033[32mGreen\033[39m | \033[34mBlue\033[39m |\n' u'| colorama | \033[31mRed\033[39m | \033[32mGreen\033[39m | \033[34mBlue\033[39m |\n' u'| termcolor | \033[31mRed\033[0m | \033[32mGreen\033[0m | \033[34mBlue\033[0m |\n' u'+------------+-----+-------+------+' ) assert actual == expected terminaltables-3.1.0/tests/test_build/000077500000000000000000000000001300101162000200015ustar00rootroot00000000000000terminaltables-3.1.0/tests/test_build/test_build_border.py000066400000000000000000000267121300101162000240560ustar00rootroot00000000000000# coding: utf-8 """Test function in module.""" import pytest from colorama import Fore, Style from colorclass import Color from termcolor import colored from terminaltables.build import build_border @pytest.mark.parametrize('outer_widths,horizontal,left,intersect,right,expected', [ ([5, 6, 7], '-', '<', '+', '>', '<-----+------+------->'), ([1, 1, 1], '-', '', '', '', '---'), ([1, 1, 1], '', '', '', '', ''), ([1], '-', '<', '+', '>', '<->'), ([], '-', '<', '+', '>', '<>'), ]) def test_no_title(outer_widths, horizontal, left, intersect, right, expected): """Test without title. :param iter outer_widths: List of integers representing column widths with padding. :param str horizontal: Character to stretch across each column. :param str left: Left border. :param str intersect: Column separator. :param str right: Right border. :param str expected: Expected output. """ actual = build_border(outer_widths, horizontal, left, intersect, right) assert ''.join(actual) == expected @pytest.mark.parametrize('outer_widths,intersect,expected', [ ([20], '+', 'Applications--------'), ([20], '', 'Applications--------'), ([15, 5], '+', 'Applications---+-----'), ([15, 5], '', 'Applications--------'), ([12], '+', 'Applications'), ([12], '', 'Applications'), ([12, 1], '+', 'Applications+-'), ([12, 1], '', 'Applications-'), ([12, 0], '+', 'Applications+'), ([12, 0], '', 'Applications'), ]) @pytest.mark.parametrize('left,right', [('', ''), ('<', '>')]) def test_first_column_fit(outer_widths, left, intersect, right, expected): """Test with title that fits in the first column. :param iter outer_widths: List of integers representing column widths with padding. :param str left: Left border. :param str intersect: Column separator. :param str right: Right border. :param str expected: Expected output. """ if left and right: expected = left + expected + right actual = build_border(outer_widths, '-', left, intersect, right, title='Applications') assert ''.join(actual) == expected @pytest.mark.parametrize('outer_widths,expected', [ ([20], 'Applications--------'), ([10, 10], 'Applications--------'), ([5, 5, 5, 5], 'Applications--------'), ([3, 2, 3, 2, 3, 2, 3, 2], 'Applications--------'), ([1] * 20, 'Applications--------'), ([10, 5], 'Applications---'), ([9, 5], 'Applications--'), ([8, 5], 'Applications-'), ([7, 5], 'Applications'), ([6, 5], '-----------'), ]) @pytest.mark.parametrize('left,right', [('', ''), ('<', '>')]) def test_no_intersect(outer_widths, left, right, expected): """Test with no column dividers. :param iter outer_widths: List of integers representing column widths. :param str left: Left border. :param str right: Right border. :param str expected: Expected output. """ if left and right: expected = left + expected + right actual = build_border(outer_widths, '-', left, '', right, title='Applications') assert ''.join(actual) == expected @pytest.mark.parametrize('outer_widths,expected', [ ([20], 'Applications--------'), ([0, 20], 'Applications---------'), ([20, 0], 'Applications--------+'), ([0, 0, 20], 'Applications----------'), ([20, 0, 0], 'Applications--------++'), ([10, 10], 'Applications---------'), ([11, 9], 'Applications---------'), ([12, 8], 'Applications+--------'), ([13, 7], 'Applications-+-------'), ([5, 5, 5, 5], 'Applications-----+-----'), ([4, 4, 6, 6], 'Applications----+------'), ([3, 3, 7, 7], 'Applications---+-------'), ([2, 2, 7, 9], 'Applications-+---------'), ([1, 1, 9, 9], 'Applications-+---------'), ([2, 2, 2, 2, 2, 2, 2], 'Applications--+--+--'), ([1, 1, 1, 1, 1, 1, 1, 1, 1, 1], 'Applications-+-+-+-'), ([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'Applications++++++++'), ([2, 2, 2, 2], '--+--+--+--'), ([1, 1, 1, 1, 1], '-+-+-+-+-'), ([0, 0, 0, 0, 0, 0, 0, 0, 0, 0], '+++++++++'), ]) @pytest.mark.parametrize('left,right', [('', ''), ('<', '>')]) def test_intersect(outer_widths, left, right, expected): """Test with column dividers. :param iter outer_widths: List of integers representing column widths. :param str left: Left border. :param str right: Right border. :param str expected: Expected output. """ if left and right: expected = left + expected + right actual = build_border(outer_widths, '-', left, '+', right, title='Applications') assert ''.join(actual) == expected @pytest.mark.parametrize('outer_widths,intersect,expected', [ ([12], '+', u'蓝色--------'), ([12], '', u'蓝色--------'), ([7, 5], '+', u'蓝色---+-----'), ([7, 5], '', u'蓝色--------'), ([4], '+', u'蓝色'), ([4], '', u'蓝色'), ([4, 1], '+', u'蓝色+-'), ([4, 1], '', u'蓝色-'), ([4, 0], '+', u'蓝色+'), ([4, 0], '', u'蓝色'), ([12], '', u'蓝色--------'), ([6, 6], '', u'蓝色--------'), ([3, 3, 3, 3], '', u'蓝色--------'), ([2, 1, 2, 1, 2, 1, 2, 1], '', u'蓝色--------'), ([1] * 12, '', u'蓝色--------'), ([2, 4], '', u'蓝色--'), ([1, 4], '', u'蓝色-'), ([1, 3], '', u'蓝色'), ([1, 2], '', u'---'), ([2], '', u'--'), ([12], '+', u'蓝色--------'), ([0, 12], '+', u'蓝色---------'), ([12, 0], '+', u'蓝色--------+'), ([0, 0, 12], '+', u'蓝色----------'), ([12, 0, 0], '+', u'蓝色--------++'), ([3, 3], '+', u'蓝色---'), ([4, 2], '+', u'蓝色+--'), ([5, 1], '+', u'蓝色-+-'), ([3, 3, 3, 3], '+', u'蓝色---+---+---'), ([2, 2, 4, 4], '+', u'蓝色-+----+----'), ([1, 1, 5, 5], '+', u'蓝色-----+-----'), ([2, 2, 2, 2], '+', u'蓝色-+--+--'), ([1, 1, 1, 1, 1], '+', u'蓝色-+-+-'), ([0, 0, 0, 0, 0, 0, 0], '+', u'蓝色++'), ([1, 1], '+', u'-+-'), ]) @pytest.mark.parametrize('left,right', [('', ''), ('<', '>')]) def test_cjk(outer_widths, left, intersect, right, expected): """Test with CJK characters in title. :param iter outer_widths: List of integers representing column widths. :param str left: Left border. :param str intersect: Column separator. :param str right: Right border. :param str expected: Expected output. """ if left and right: expected = left + expected + right actual = build_border(outer_widths, '-', left, intersect, right, title=u'蓝色') assert ''.join(actual) == expected @pytest.mark.parametrize('outer_widths,intersect,expected', [ ([12], '+', u'معرب--------'), ([12], '', u'معرب--------'), ([7, 5], '+', u'معرب---+-----'), ([7, 5], '', u'معرب--------'), ([4], '+', u'معرب'), ([4], '', u'معرب'), ([4, 1], '+', u'معرب+-'), ([4, 1], '', u'معرب-'), ([4, 0], '+', u'معرب+'), ([4, 0], '', u'معرب'), ([12], '', u'معرب--------'), ([6, 6], '', u'معرب--------'), ([3, 3, 3, 3], '', u'معرب--------'), ([2, 1, 2, 1, 2, 1, 2, 1], '', u'معرب--------'), ([1] * 12, '', u'معرب--------'), ([2, 4], '', u'معرب--'), ([1, 4], '', u'معرب-'), ([1, 3], '', u'معرب'), ([1, 2], '', u'---'), ([2], '', u'--'), ([12], '+', u'معرب--------'), ([0, 12], '+', u'معرب---------'), ([12, 0], '+', u'معرب--------+'), ([0, 0, 12], '+', u'معرب----------'), ([12, 0, 0], '+', u'معرب--------++'), ([3, 3], '+', u'معرب---'), ([4, 2], '+', u'معرب+--'), ([5, 1], '+', u'معرب-+-'), ([3, 3, 3, 3], '+', u'معرب---+---+---'), ([2, 2, 4, 4], '+', u'معرب-+----+----'), ([1, 1, 5, 5], '+', u'معرب-----+-----'), ([2, 2, 2, 2], '+', u'معرب-+--+--'), ([1, 1, 1, 1, 1], '+', u'معرب-+-+-'), ([0, 0, 0, 0, 0, 0, 0], '+', u'معرب++'), ([1, 1], '+', u'-+-'), ]) @pytest.mark.parametrize('left,right', [('', ''), ('<', '>')]) def test_rtl(outer_widths, left, intersect, right, expected): """Test with RTL characters in title. :param iter outer_widths: List of integers representing column widths. :param str left: Left border. :param str intersect: Column separator. :param str right: Right border. :param str expected: Expected output. """ if left and right: expected = left + expected + right actual = build_border(outer_widths, '-', left, intersect, right, title=u'معرب') assert ''.join(actual) == expected @pytest.mark.parametrize('outer_widths,intersect,expected', [ ([12], '+', '\x1b[34mTEST\x1b[0m--------'), ([12], '', '\x1b[34mTEST\x1b[0m--------'), ([7, 5], '+', '\x1b[34mTEST\x1b[0m---+-----'), ([7, 5], '', '\x1b[34mTEST\x1b[0m--------'), ([4], '+', '\x1b[34mTEST\x1b[0m'), ([4], '', '\x1b[34mTEST\x1b[0m'), ([4, 1], '+', '\x1b[34mTEST\x1b[0m+-'), ([4, 1], '', '\x1b[34mTEST\x1b[0m-'), ([4, 0], '+', '\x1b[34mTEST\x1b[0m+'), ([4, 0], '', '\x1b[34mTEST\x1b[0m'), ([12], '', '\x1b[34mTEST\x1b[0m--------'), ([6, 6], '', '\x1b[34mTEST\x1b[0m--------'), ([3, 3, 3, 3], '', '\x1b[34mTEST\x1b[0m--------'), ([2, 1, 2, 1, 2, 1, 2, 1], '', '\x1b[34mTEST\x1b[0m--------'), ([1] * 12, '', '\x1b[34mTEST\x1b[0m--------'), ([2, 4], '', '\x1b[34mTEST\x1b[0m--'), ([1, 4], '', '\x1b[34mTEST\x1b[0m-'), ([1, 3], '', '\x1b[34mTEST\x1b[0m'), ([1, 2], '', '---'), ([12], '+', '\x1b[34mTEST\x1b[0m--------'), ([0, 12], '+', '\x1b[34mTEST\x1b[0m---------'), ([12, 0], '+', '\x1b[34mTEST\x1b[0m--------+'), ([0, 0, 12], '+', '\x1b[34mTEST\x1b[0m----------'), ([12, 0, 0], '+', '\x1b[34mTEST\x1b[0m--------++'), ([3, 3], '+', '\x1b[34mTEST\x1b[0m---'), ([4, 2], '+', '\x1b[34mTEST\x1b[0m+--'), ([5, 1], '+', '\x1b[34mTEST\x1b[0m-+-'), ([3, 3, 3, 3], '+', '\x1b[34mTEST\x1b[0m---+---+---'), ([2, 2, 4, 4], '+', '\x1b[34mTEST\x1b[0m-+----+----'), ([1, 1, 5, 5], '+', '\x1b[34mTEST\x1b[0m-----+-----'), ([2, 2, 2, 2], '+', '\x1b[34mTEST\x1b[0m-+--+--'), ([1, 1, 1, 1, 1], '+', '\x1b[34mTEST\x1b[0m-+-+-'), ([0, 0, 0, 0, 0, 0, 0], '+', '\x1b[34mTEST\x1b[0m++'), ([1, 1], '+', '-+-'), ]) @pytest.mark.parametrize('left,right', [('', ''), ('<', '>')]) @pytest.mark.parametrize('title', [ '\x1b[34mTEST\x1b[0m', Color('{blue}TEST{/all}'), Fore.BLUE + 'TEST' + Style.RESET_ALL, colored('TEST', 'blue'), ]) def test_colors(outer_widths, left, intersect, right, title, expected): """Test with color title characters. :param iter outer_widths: List of integers representing column widths with padding. :param str left: Left border. :param str intersect: Column separator. :param str right: Right border. :param title: Title in border with color codes. :param str expected: Expected output. """ if left and right: expected = left + expected + right actual = build_border(outer_widths, '-', left, intersect, right, title=title) assert ''.join(actual) == expected @pytest.mark.parametrize('outer_widths,title,expected', [ ([3, 3, 3], 123, '<123+---+--->'), ([3, 3, 3], 0.9, '<0.9+---+--->'), ([3, 3, 3], True, ''), ([3, 3, 3], False, ''), ]) def test_non_string(outer_widths, title, expected): """Test with non-string values. :param iter outer_widths: List of integers representing column widths with padding. :param title: Title in border. :param str expected: Expected output. """ actual = build_border(outer_widths, '-', '<', '+', '>', title=title) assert ''.join(actual) == expected terminaltables-3.1.0/tests/test_build/test_build_row.py000066400000000000000000000043141300101162000234020ustar00rootroot00000000000000"""Test function in module.""" from terminaltables.build import build_row def test_one_line(): """Test with one line cells.""" row = [ ['Left Cell'], ['Center Cell'], ['Right Cell'], ] actual = [tuple(i) for i in build_row(row, '>', '|', '<')] expected = [ ('>', 'Left Cell', '|', 'Center Cell', '|', 'Right Cell', '<'), ] assert actual == expected def test_two_line(): """Test with two line cells.""" row = [ [ 'Left ', 'Cell1', ], [ 'Center', 'Cell2 ', ], [ 'Right', 'Cell3', ], ] actual = [tuple(i) for i in build_row(row, '>', '|', '<')] expected = [ ('>', 'Left ', '|', 'Center', '|', 'Right', '<'), ('>', 'Cell1', '|', 'Cell2 ', '|', 'Cell3', '<'), ] assert actual == expected def test_three_line(): """Test with three line cells.""" row = [ [ 'Left ', 'Cell1', ' ', ], [ 'Center', 'Cell2 ', ' ', ], [ 'Right', 'Cell3', ' ', ], ] actual = [tuple(i) for i in build_row(row, '>', '|', '<')] expected = [ ('>', 'Left ', '|', 'Center', '|', 'Right', '<'), ('>', 'Cell1', '|', 'Cell2 ', '|', 'Cell3', '<'), ('>', ' ', '|', ' ', '|', ' ', '<'), ] assert actual == expected def test_single(): """Test with single cell.""" actual = [tuple(i) for i in build_row([['Cell']], '>', '|', '<')] expected = [ ('>', 'Cell', '<'), ] assert actual == expected def test_empty(): """Test with empty cell.""" actual = [tuple(i) for i in build_row([['']], '>', '|', '<')] expected = [ ('>', '', '<'), ] assert actual == expected def test_no_cells(): """Test with no cells.""" actual = [tuple(i) for i in build_row([[]], '>', '|', '<')] expected = [ ('>', '<'), ] assert actual == expected actual = [tuple(i) for i in build_row([], '>', '|', '<')] expected = [ ('>', '<'), ] assert actual == expected terminaltables-3.1.0/tests/test_build/test_combine.py000066400000000000000000000020721300101162000230270ustar00rootroot00000000000000"""Test function in module.""" import pytest from terminaltables.build import combine @pytest.mark.parametrize('generator', [False, True]) def test_borders(generator): """Test with borders. :param bool generator: Test with generator instead of list. """ line = ['One', 'Two', 'Three'] actual = list(combine(iter(line) if generator else line, '>', '|', '<')) assert actual == ['>', 'One', '|', 'Two', '|', 'Three', '<'] @pytest.mark.parametrize('generator', [False, True]) def test_no_border(generator): """Test without borders. :param bool generator: Test with generator instead of list. """ line = ['One', 'Two', 'Three'] actual = list(combine(iter(line) if generator else line, '', '', '')) assert actual == ['One', 'Two', 'Three'] @pytest.mark.parametrize('generator', [False, True]) def test_no_items(generator): """Test with empty list. :param bool generator: Test with generator instead of list. """ actual = list(combine(iter([]) if generator else [], '>', '|', '<')) assert actual == ['>', '<'] terminaltables-3.1.0/tests/test_build/test_flatten.py000066400000000000000000000012341300101162000230470ustar00rootroot00000000000000"""Test function in module.""" from terminaltables.build import flatten def test_one_line(): """Test with one line cells.""" table = [ ['>', 'Left Cell', '|', 'Center Cell', '|', 'Right Cell', '<'], ] actual = flatten(table) expected = '>Left Cell|Center Cell|Right Cell<' assert actual == expected def test_two_line(): """Test with two line cells.""" table = [ ['>', 'Left ', '|', 'Center', '|', 'Right', '<'], ['>', 'Cell1', '|', 'Cell2 ', '|', 'Cell3', '<'], ] actual = flatten(table) expected = ('>Left |Center|Right<\n' '>Cell1|Cell2 |Cell3<') assert actual == expected terminaltables-3.1.0/tests/test_examples.py000066400000000000000000000013301300101162000210670ustar00rootroot00000000000000"""Test example scripts.""" from __future__ import print_function import os import subprocess import sys import pytest from tests import PROJECT_ROOT @pytest.mark.parametrize('filename', map('example{0}.py'.format, (1, 2, 3))) def test(filename): """Test with subprocess. :param str filename: Example script filename to run. """ command = [sys.executable, str(PROJECT_ROOT.join(filename))] env = dict(os.environ, PYTHONIOENCODING='utf-8') # Run. proc = subprocess.Popen(command, env=env, stderr=subprocess.STDOUT, stdout=subprocess.PIPE) output = proc.communicate()[0] # Verify. try: assert proc.poll() == 0 except AssertionError: print(output) raise terminaltables-3.1.0/tests/test_terminal_io/000077500000000000000000000000001300101162000212045ustar00rootroot00000000000000terminaltables-3.1.0/tests/test_terminal_io/__init__.py000066400000000000000000000027771300101162000233320ustar00rootroot00000000000000"""Common objects used by tests in directory.""" from terminaltables import terminal_io class MockKernel32(object): """Mock kernel32.""" def __init__(self, stderr=terminal_io.INVALID_HANDLE_VALUE, stdout=terminal_io.INVALID_HANDLE_VALUE): """Constructor.""" self.stderr = stderr self.stdout = stdout self.csbi_err = b'x\x00)#\x00\x00\x87\x05\x07\x00\x00\x00j\x05w\x00\x87\x05x\x00J\x00' # 119 x 29 self.csbi_out = b'L\x00,\x01\x00\x00*\x01\x07\x00\x00\x00\x0e\x01K\x00*\x01L\x00L\x00' # 75 x 28 self.setConsoleTitleA_called = False self.setConsoleTitleW_called = False def GetConsoleScreenBufferInfo(self, handle, lpcsbi): # noqa """Mock GetConsoleScreenBufferInfo. :param handle: Unused handle. :param lpcsbi: ctypes.create_string_buffer() return value. """ if handle == self.stderr: lpcsbi.raw = self.csbi_err else: lpcsbi.raw = self.csbi_out return 1 def GetStdHandle(self, handle): # noqa """Mock GetStdHandle. :param int handle: STD_ERROR_HANDLE or STD_OUTPUT_HANDLE. """ return self.stderr if handle == terminal_io.STD_ERROR_HANDLE else self.stdout def SetConsoleTitleA(self, _): # noqa """Mock SetConsoleTitleA.""" self.setConsoleTitleA_called = True return 1 def SetConsoleTitleW(self, _): # noqa """Mock SetConsoleTitleW.""" self.setConsoleTitleW_called = True return 1 terminaltables-3.1.0/tests/test_terminal_io/sub_title_ascii_win10.bmp000066400000000000000000000037161300101162000260730ustar00rootroot00000000000000BM6(H ff:ffffffې::۶fff::۶ffffff:fffff:fېfې::ې::fېf::fې:ې:ffffff:fېfې::ې::fې:fېf::f:fېff:۶f:fffff:fېf::f:fې:fې:f:ې::fې:ې::۶f:ff:ffff:fې:f:ې::fې:fې:fېfې:ff:fې::::ې:f::ffff:fې:fېfې:ff:fېff:f:fff::ې::fې:f:ffffff:f:fff:fې:fې:ې::ې:f::fffff:fې:fې:fې:fېf::::fېf::fffff:fې:fېffff::۶ffffterminaltables-3.1.0/tests/test_terminal_io/sub_title_ascii_win2012.bmp000066400000000000000000000057501300101162000262370ustar00rootroot00000000000000BM 6(Z    ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffn>fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffftEffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffvtffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffYffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffmfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffterminaltables-3.1.0/tests/test_terminal_io/sub_title_ascii_winxp.bmp000066400000000000000000000040261300101162000262750ustar00rootroot00000000000000BM6(Sj$ k% k% l' n)n)n)o*o+o+p+p,q,q-q-s/s/s0u1v3w4w4w5x5x5y6y7z8|:|:|:j% k% k% l& l& l' l' l' m' m( m(n)o*o*o*o+o+p+p,q,q-q-r.r.r.r/s/t0t0t1t1u1u2v2v3v3w4w4w4w5x5x6y6y6y7z8z8z8z8{9{9{9|:|; }; j% k% k% l& l& l' l' l' m' n)o*o*o*o+o+p+q-r.r.r.r/s/t0t0t1t1u1u2v2v3v3w4w4w4w5x5x6y6y6y7z8z8z8z8{9|:|; }; j% k% k% l' n)n)o*o*o*o+o+p+p+q,q-q-r.s/t0t0t1t1u1u2v2v3v3w4w4w4w5x5x6y6y6z8|:|:|; }; j% k% k% l& l& l' m(m(n)n)o*o*o*o+o+p+p+q,q-q-s/s/t0t0t1t1u1u2v2v3v3w4w4w4w5x5x6y6y6y7z8z8{9{:|:|:|; }; k% k% l' l' m' n)o*o+o+p+p+q,q-q-r.r/s/s/s/t0t0t1t1u1u2v2v3v3w4w4w4w5x5y6y7z8z8{9|:j% k% k% k% k& l& l& l' l' l' m' m( m(m(n)n)o*o*o*o+o+p+p+p,q-q-q-r.r/s/s/s/t0t0t1t1u1u2v2v3v3w4w4w4w5x5x6y6y6y7y7y7z8z8z8z8{9{9{9{:|:|:|; }; j% k% k% k% k& l& l& l' l' l' m' m( m(m(n)n)o*o*o*o+o+p+p+p,q-q-q-r.s/s0u1v3w4w4w5x5x6y6y6y7y7y7z8z8z8z8{9{9{9{:|:|:|; }; terminaltables-3.1.0/tests/test_terminal_io/sub_title_cjk_win10.bmp000066400000000000000000000102661300101162000255500ustar00rootroot00000000000000BM6(u :fff:fffffffffffffff:fffff:fېfې::ې::fېffffffff:fېfې::ې::fې:fېf::f:fېff:fېf::f:fې:fې:f:ې::fېېf:fې:f:ې::fې:fې:fېfې:ff:fېېffffff:fې:fېfې:ff:fېff:f:fffېfېffې:ffff:f:fff:fې:fېېfېf:f:fې:fې:fې:fېېfېf:ې::fې:fېfffې:ffffېterminaltables-3.1.0/tests/test_terminal_io/sub_title_cjk_win2012.bmp000066400000000000000000000153601300101162000257140ustar00rootroot00000000000000BM6(  ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffSffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeo~fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffDterminaltables-3.1.0/tests/test_terminal_io/sub_title_cjk_winxp.bmp000066400000000000000000000103421300101162000257520ustar00rootroot00000000000000BM6( j$ j$ j% k% k% k% k& l& l& l' l' l' m' m( m(m(n)n)n)n)o*o*o*o+o+p+p+p,r/s/s/t0t0t0t1u2u2u2v2v3v3w4w4x5x6x6y6y7y7z8z8{9{9{:|:~=">"A%A&B&B&B&C'C'C'C'D(D(D(E)E)E)E)F*F*F*G*G+G+G+H+H,H,j$ k% k% l' n)n)n)o*o+o+p+p+p,q-q-q-q-r.r.r.r/s/s/s/t0t0t1u2u2u2v2v3v3w4x5x6x6y6y6z8z8{9{9{:|:|:|:~=!~="~=">"?#?#?#?$@$@$@%A%A&B&B&B&C'D(E)G+G+G+j% k% k% l& l& l' l' l' m' m( m(n)o*o*o*o+o+p+p+p,q-q-q-q-r.r.r.r/s/s/s/s0t0t1u2u2u2v2v3v3w4x5y6y6y7z8z8z8{9{9{:|:|:|:~=!~="~=">"?#?#?#?$@$@$@%A%A%A%A&B&B&C'C'C'D(E)E)E)E)F*F*F*G+H,H,j% k% k% l& l& l' l' l' m' n)o*o*o*o+o+p+p+p,q-r.r/s/t0t1v2v3v3w4x5y6z8z8{9{9{:|:|:|:~=!~="~=">"?#?#?#?$@$@$@%A%A%A%A&B&B&C'C'C'D(E)E)E)E)F*G+H,H,j% k% k% l' n)n)o*o*o*o+o+p+p+p,q-q-r.r/s/s/s/t0t1u2u2v2v3w4x6y6y7|:|:}; }< }"A%A%A&B&B&C'C'C'E)G+G+H,H,j% k% k% l& l& l' m(m(n)n)o*o*o*o+o+p+p+p,q-q-r.r/s/s/s/s0t0u1u2u2u2v2v3w4x6y6y7z8z8{9{9{:|:|:~=!~="~=">"?#?#@$@$A%A%A&B&B&C'C'C'D(E)E)F*G*G+G+H,H,k% k% l' l' m' n)o*o+o+p+p+p,q-q-r.r/s/s/u2v2v3w4x6y6y6z8z8{9{9{:|:|:}; ~"?#?#@$@$A%A%A&B&B&C'D(E)E)F*G+j% k% k% k% k& l& l& l' l' l' m' m( m(m(n)n)o*o*o*o+o+p+s/s/t0t0t1u1u2v2v3v3w4x5y6y6z8z8z8{9{:|:|:}; ~="~="A%A%A&B&B&C'C'C'D(D(D(E)E)E)E)F*F*F*G*G+G+H,H,j% k% k% k% k& l& l& l' l' l' m' m( m(m(n)n)o*o*o*o+o+p+p+p,q-q-r.r/s/s/u2v2v3v3w4y6z8z8z8{9{:|:|:|:|;}; }< ~=!~=!~="~=">"?#?#?#@$@%A%A%A%A&B&B&C'C'C'D(D(D(E)E)E)E)F*F*F*G*G+G+H,H,j$ j$ j% k% k% k% k& l& l& l' l' l' m' m( m(m(n)n)n)n)o*o*o*o+o+p+p+p,q-q-r.r/s/s/t0t0t1u1u2v2v3v3v3x5x5x6x6y6y6y7z8{:|:~=">">"@$@%A%A%A%A&B&B&B&C'C'C'C'D(D(D(E)E)E)E)F*F*F*G*G+G+G+H+H,H,j$ j$ j% k% k% k% k& l& l& l' l' l' m' m( m(m(n)n)n)n)o*o*o*o+o+p+p+p,q-q-r.r/s/s/u2v2v3v3w4w4w4w5x5x5x6x6y6y6y7z8z8z8z8{9{9{9{:|:|:|:|;}; }< ~=!~=!~="~=">">">#?#?$@$@$@%A%A%A%A&B&B&B&C'C'C'C'D(D(D(E)E)E)E)F*F*F*G*G+G+G+H+H,H,terminaltables-3.1.0/tests/test_terminal_io/test_get_console_info.py000066400000000000000000000012661300101162000261360ustar00rootroot00000000000000# coding: utf-8 """Test function in module.""" import ctypes import pytest from terminaltables.terminal_io import get_console_info, INVALID_HANDLE_VALUE, IS_WINDOWS from tests.test_terminal_io import MockKernel32 def test(): """Test function.""" # Test live WinError. if IS_WINDOWS: with pytest.raises(OSError): get_console_info(ctypes.windll.kernel32, 0) # Test INVALID_HANDLE_VALUE. kernel32 = MockKernel32(stderr=1) with pytest.raises(OSError): get_console_info(kernel32, INVALID_HANDLE_VALUE) # Test no error with mock methods. width, height = get_console_info(kernel32, 1) assert width == 119 assert height == 29 terminaltables-3.1.0/tests/test_terminal_io/test_set_terminal_title.py000066400000000000000000000070421300101162000265070ustar00rootroot00000000000000# coding: utf-8 """Test function in module.""" import sys from textwrap import dedent import py import pytest from terminaltables.terminal_io import IS_WINDOWS, set_terminal_title from tests import PROJECT_ROOT from tests.screenshot import RunNewConsole, screenshot_until_match from tests.test_terminal_io import MockKernel32 HERE = py.path.local(__file__).dirpath() @pytest.mark.parametrize('is_windows', [False, True]) @pytest.mark.parametrize('mode', ['ascii', 'unicode', 'bytes']) def test(monkeypatch, is_windows, mode): """Test function. :param monkeypatch: pytest fixture. :param bool is_windows: Monkeypatch terminal_io.IS_WINDOWS :param str mode: Scenario to test for. """ monkeypatch.setattr('terminaltables.terminal_io.IS_WINDOWS', is_windows) kernel32 = MockKernel32() # Title. if mode == 'ascii': title = 'Testing terminaltables.' elif mode == 'unicode': title = u'Testing terminaltables with unicode: 世界你好蓝色' else: title = b'Testing terminaltables with bytes.' # Run. assert set_terminal_title(title, kernel32) if not is_windows: return # Verify. if mode == 'ascii': assert kernel32.setConsoleTitleA_called assert not kernel32.setConsoleTitleW_called elif mode == 'unicode': assert not kernel32.setConsoleTitleA_called assert kernel32.setConsoleTitleW_called else: assert kernel32.setConsoleTitleA_called assert not kernel32.setConsoleTitleW_called @pytest.mark.skipif(str(not IS_WINDOWS)) @pytest.mark.skipif('True') # https://github.com/Robpol86/terminaltables/issues/30 @pytest.mark.parametrize('mode', ['ascii', 'unicode', 'bytes']) def test_windows_screenshot(tmpdir, mode): """Test function on Windows in a new console window. Take a screenshot to verify it works. :param tmpdir: pytest fixture. :param str mode: Scenario to test for. """ script = tmpdir.join('script.py') command = [sys.executable, str(script)] change_title = tmpdir.join('change_title') screenshot = PROJECT_ROOT.join('test_terminal_io.png') if screenshot.check(): screenshot.remove() # Determine title. if mode == 'ascii': title = "'test ASCII test'" elif mode == 'unicode': title = u"u'test 世界你好蓝色 test'" else: title = "b'test ASCII test'" # Generate script. script_template = dedent(u"""\ # coding: utf-8 from __future__ import print_function import os, time from terminaltables.terminal_io import set_terminal_title stop_after = time.time() + 20 print('Waiting for FindWindowA() in RunNewConsole.__enter__()...') while not os.path.exists(r'{change_title}') and time.time() < stop_after: time.sleep(0.5) assert set_terminal_title({title}) is True print('Waiting for screenshot_until_match()...') while not os.path.exists(r'{screenshot}') and time.time() < stop_after: time.sleep(0.5) """) script_contents = script_template.format(change_title=str(change_title), title=title, screenshot=str(screenshot)) script.write(script_contents.encode('utf-8'), mode='wb') # Setup expected. if mode == 'unicode': sub_images = [str(p) for p in HERE.listdir('sub_title_cjk_*.bmp')] else: sub_images = [str(p) for p in HERE.listdir('sub_title_ascii_*.bmp')] assert sub_images # Run. with RunNewConsole(command) as gen: change_title.ensure(file=True) # Touch file. screenshot_until_match(str(screenshot), 15, sub_images, 1, gen) terminaltables-3.1.0/tests/test_terminal_io/test_terminal_size.py000066400000000000000000000032041300101162000254610ustar00rootroot00000000000000# coding: utf-8 """Test function in module.""" import pytest from terminaltables.terminal_io import DEFAULT_HEIGHT, DEFAULT_WIDTH, INVALID_HANDLE_VALUE, IS_WINDOWS, terminal_size from tests.test_terminal_io import MockKernel32 @pytest.mark.parametrize('stderr', [1, INVALID_HANDLE_VALUE]) @pytest.mark.parametrize('stdout', [2, INVALID_HANDLE_VALUE]) def test_windows(monkeypatch, stderr, stdout): """Test function with IS_WINDOWS=True. :param monkeypatch: pytest fixture. :param int stderr: Mock handle value. :param int stdout: Mock handle value. """ monkeypatch.setattr('terminaltables.terminal_io.IS_WINDOWS', True) kernel32 = MockKernel32(stderr=stderr, stdout=stdout) width, height = terminal_size(kernel32) if stderr == INVALID_HANDLE_VALUE and stdout == INVALID_HANDLE_VALUE: assert width == DEFAULT_WIDTH assert height == DEFAULT_HEIGHT elif stdout == INVALID_HANDLE_VALUE: assert width == 119 assert height == 29 elif stderr == INVALID_HANDLE_VALUE: assert width == 75 assert height == 28 else: assert width == 119 assert height == 29 @pytest.mark.skipif(str(IS_WINDOWS)) def test_nix(monkeypatch): """Test function with IS_WINDOWS=False. :param monkeypatch: pytest fixture. """ # Test exception (no terminal within pytest). width, height = terminal_size() assert width == DEFAULT_WIDTH assert height == DEFAULT_HEIGHT # Test mocked. monkeypatch.setattr('fcntl.ioctl', lambda *_: b'\x1d\x00w\x00\xca\x02\x96\x01') width, height = terminal_size() assert width == 119 assert height == 29 terminaltables-3.1.0/tests/test_width_and_alignment/000077500000000000000000000000001300101162000227015ustar00rootroot00000000000000terminaltables-3.1.0/tests/test_width_and_alignment/test_align_and_pad_cell.py000066400000000000000000000225001300101162000300500ustar00rootroot00000000000000# coding: utf-8 """Test function in module.""" import pytest from colorama import Fore from colorclass import Color from termcolor import colored from terminaltables.width_and_alignment import align_and_pad_cell @pytest.mark.parametrize('string,align,width,expected', [ ('test', '', 4, ['test']), (123, '', 3, ['123']), (0.9, '', 3, ['0.9']), (None, '', 4, ['None']), (True, '', 4, ['True']), (False, '', 5, ['False']), (Color('{blue}Test{/blue}'), '', 4, ['\x1b[34mTest\x1b[39m']), (Fore.BLUE + 'Test' + Fore.RESET, '', 4, ['\x1b[34mTest\x1b[39m']), (colored('Test', 'blue'), '', 4, ['\x1b[34mTest\x1b[0m']), ('蓝色', '', 4, ['蓝色']), (u'שלום', '', 4, [u'\u05e9\u05dc\u05d5\u05dd']), (u'معرب', '', 4, [u'\u0645\u0639\u0631\u0628']), ('test', '', 5, ['test ']), (123, '', 4, ['123 ']), (0.9, '', 4, ['0.9 ']), (None, '', 5, ['None ']), (True, '', 5, ['True ']), (False, '', 6, ['False ']), (Color('{blue}Test{/blue}'), '', 5, ['\x1b[34mTest\x1b[39m ']), (Fore.BLUE + 'Test' + Fore.RESET, '', 5, ['\x1b[34mTest\x1b[39m ']), (colored('Test', 'blue'), '', 5, ['\x1b[34mTest\x1b[0m ']), ('蓝色', '', 5, ['蓝色 ']), (u'שלום', '', 5, [u'\u05e9\u05dc\u05d5\u05dd ']), (u'معرب', '', 5, [u'\u0645\u0639\u0631\u0628 ']), ('test', 'left', 5, ['test ']), (123, 'left', 4, ['123 ']), (0.9, 'left', 4, ['0.9 ']), (None, 'left', 5, ['None ']), (True, 'left', 5, ['True ']), (False, 'left', 6, ['False ']), (Color('{blue}Test{/blue}'), 'left', 5, ['\x1b[34mTest\x1b[39m ']), (Fore.BLUE + 'Test' + Fore.RESET, 'left', 5, ['\x1b[34mTest\x1b[39m ']), (colored('Test', 'blue'), 'left', 5, ['\x1b[34mTest\x1b[0m ']), ('蓝色', 'left', 5, ['蓝色 ']), (u'שלום', 'left', 5, [u'\u05e9\u05dc\u05d5\u05dd ']), (u'معرب', 'left', 5, [u'\u0645\u0639\u0631\u0628 ']), ('test', 'right', 5, [' test']), (123, 'right', 4, [' 123']), (0.9, 'right', 4, [' 0.9']), (None, 'right', 5, [' None']), (True, 'right', 5, [' True']), (False, 'right', 6, [' False']), (Color('{blue}Test{/blue}'), 'right', 5, [' \x1b[34mTest\x1b[39m']), (Fore.BLUE + 'Test' + Fore.RESET, 'right', 5, [' \x1b[34mTest\x1b[39m']), (colored('Test', 'blue'), 'right', 5, [' \x1b[34mTest\x1b[0m']), ('蓝色', 'right', 5, [' 蓝色']), (u'שלום', 'right', 5, [u' \u05e9\u05dc\u05d5\u05dd']), (u'معرب', 'right', 5, [u' \u0645\u0639\u0631\u0628']), ('test', 'center', 6, [' test ']), (123, 'center', 5, [' 123 ']), (0.9, 'center', 5, [' 0.9 ']), (None, 'center', 6, [' None ']), (True, 'center', 6, [' True ']), (False, 'center', 7, [' False ']), (Color('{blue}Test{/blue}'), 'center', 6, [' \x1b[34mTest\x1b[39m ']), (Fore.BLUE + 'Test' + Fore.RESET, 'center', 6, [' \x1b[34mTest\x1b[39m ']), (colored('Test', 'blue'), 'center', 6, [' \x1b[34mTest\x1b[0m ']), ('蓝色', 'center', 6, [' 蓝色 ']), (u'שלום', 'center', 6, [u' \u05e9\u05dc\u05d5\u05dd ']), (u'معرب', 'center', 6, [u' \u0645\u0639\u0631\u0628 ']), ]) def test_width(string, align, width, expected): """Test width and horizontal alignment. :param str string: String to test. :param str align: Horizontal alignment. :param int width: Expand string to this width without padding. :param list expected: Expected output string. """ actual = align_and_pad_cell(string, (align,), (width, 1), (0, 0, 0, 0)) assert actual == expected @pytest.mark.parametrize('string,align,height,expected', [ ('test', '', 1, ['test']), (Color('{blue}Test{/blue}'), '', 1, ['\x1b[34mTest\x1b[39m']), (Fore.BLUE + 'Test' + Fore.RESET, '', 1, ['\x1b[34mTest\x1b[39m']), (colored('Test', 'blue'), '', 1, ['\x1b[34mTest\x1b[0m']), ('蓝色', '', 1, ['蓝色']), (u'שלום', '', 1, [u'\u05e9\u05dc\u05d5\u05dd']), (u'معرب', '', 1, [u'\u0645\u0639\u0631\u0628']), ('test', '', 2, ['test', ' ']), (Color('{blue}Test{/blue}'), '', 2, ['\x1b[34mTest\x1b[39m', ' ']), (Fore.BLUE + 'Test' + Fore.RESET, '', 2, ['\x1b[34mTest\x1b[39m', ' ']), (colored('Test', 'blue'), '', 2, ['\x1b[34mTest\x1b[0m', ' ']), ('蓝色', '', 2, ['蓝色', ' ']), (u'שלום', '', 2, [u'\u05e9\u05dc\u05d5\u05dd', ' ']), (u'معرب', '', 2, [u'\u0645\u0639\u0631\u0628', ' ']), ('test', 'top', 2, ['test', ' ']), (Color('{blue}Test{/blue}'), 'top', 2, ['\x1b[34mTest\x1b[39m', ' ']), (Fore.BLUE + 'Test' + Fore.RESET, 'top', 2, ['\x1b[34mTest\x1b[39m', ' ']), (colored('Test', 'blue'), 'top', 2, ['\x1b[34mTest\x1b[0m', ' ']), ('蓝色', 'top', 2, ['蓝色', ' ']), (u'שלום', 'top', 2, [u'\u05e9\u05dc\u05d5\u05dd', ' ']), (u'معرب', 'top', 2, [u'\u0645\u0639\u0631\u0628', ' ']), ('test', 'bottom', 2, [' ', 'test']), (Color('{blue}Test{/blue}'), 'bottom', 2, [' ', '\x1b[34mTest\x1b[39m']), (Fore.BLUE + 'Test' + Fore.RESET, 'bottom', 2, [' ', '\x1b[34mTest\x1b[39m']), (colored('Test', 'blue'), 'bottom', 2, [' ', '\x1b[34mTest\x1b[0m']), ('蓝色', 'bottom', 2, [' ', '蓝色']), (u'שלום', 'bottom', 2, [' ', u'\u05e9\u05dc\u05d5\u05dd']), (u'معرب', 'bottom', 2, [' ', u'\u0645\u0639\u0631\u0628']), ('test', 'middle', 3, [' ', 'test', ' ']), (Color('{blue}Test{/blue}'), 'middle', 3, [' ', '\x1b[34mTest\x1b[39m', ' ']), (Fore.BLUE + 'Test' + Fore.RESET, 'middle', 3, [' ', '\x1b[34mTest\x1b[39m', ' ']), (colored('Test', 'blue'), 'middle', 3, [' ', '\x1b[34mTest\x1b[0m', ' ']), ('蓝色', 'middle', 3, [' ', '蓝色', ' ']), (u'שלום', 'middle', 3, [' ', u'\u05e9\u05dc\u05d5\u05dd', ' ']), (u'معرب', 'middle', 3, [' ', u'\u0645\u0639\u0631\u0628', ' ']), ]) def test_height(string, align, height, expected): """Test height and vertical alignment. :param str string: String to test. :param str align: Horizontal alignment. :param int height: Expand string to this height without padding. :param list expected: Expected output string. """ actual = align_and_pad_cell(string, (align,), (4, height), (0, 0, 0, 0)) assert actual == expected @pytest.mark.parametrize('string,align,expected', [ ('', '', ['.......', '.......', '.......', '.......', '.......']), ('\n', '', ['.......', '.......', '.......', '.......', '.......']), ('a\nb\nc', '', ['.......', '.a.....', '.b.....', '.c.....', '.......']), ('test', '', ['.......', '.test..', '.......', '.......', '.......']), ('', 'left', ['.......', '.......', '.......', '.......', '.......']), ('\n', 'left', ['.......', '.......', '.......', '.......', '.......']), ('a\nb\nc', 'left', ['.......', '.a.....', '.b.....', '.c.....', '.......']), ('test', 'left', ['.......', '.test..', '.......', '.......', '.......']), ('', 'right', ['.......', '.......', '.......', '.......', '.......']), ('\n', 'right', ['.......', '.......', '.......', '.......', '.......']), ('a\nb\nc', 'right', ['.......', '.....a.', '.....b.', '.....c.', '.......']), ('test', 'right', ['.......', '..test.', '.......', '.......', '.......']), ('', 'center', ['.......', '.......', '.......', '.......', '.......']), ('\n', 'center', ['.......', '.......', '.......', '.......', '.......']), ('a\nb\nc', 'center', ['.......', '...a...', '...b...', '...c...', '.......']), ('test', 'center', ['.......', '..test.', '.......', '.......', '.......']), ('', 'top', ['.......', '.......', '.......', '.......', '.......']), ('\n', 'top', ['.......', '.......', '.......', '.......', '.......']), ('a\nb\nc', 'top', ['.......', '.a.....', '.b.....', '.c.....', '.......']), ('test', 'top', ['.......', '.test..', '.......', '.......', '.......']), ('', 'bottom', ['.......', '.......', '.......', '.......', '.......']), ('\n', 'bottom', ['.......', '.......', '.......', '.......', '.......']), ('a\nb\nc', 'bottom', ['.......', '.a.....', '.b.....', '.c.....', '.......']), ('test', 'bottom', ['.......', '.......', '.......', '.test..', '.......']), ('', 'middle', ['.......', '.......', '.......', '.......', '.......']), ('\n', 'middle', ['.......', '.......', '.......', '.......', '.......']), ('a\nb\nc', 'middle', ['.......', '.a.....', '.b.....', '.c.....', '.......']), ('test', 'middle', ['.......', '.......', '.test..', '.......', '.......']), ( u'蓝色\nשלום\nمعرب', '', ['.......', u'.蓝色..', u'.\u05e9\u05dc\u05d5\u05dd..', u'.\u0645\u0639\u0631\u0628..', '.......'] ), ( '\n'.join((Color('{blue}Test{/blue}'), Fore.BLUE + 'Test' + Fore.RESET, colored('Test', 'blue'))), '', ['.......', '.\x1b[34mTest\x1b[39m..', '.\x1b[34mTest\x1b[39m..', '.\x1b[34mTest\x1b[0m..', '.......'] ), # (Color('{blue}A\nB{/blue}'), '', '.......\n.\x1b[34mA\x1b[39m.....\n.\x1b[34mB\x1b[39m.....\n.......\n.......'), ]) def test_odd_width_height_pad_space(string, align, expected): """Test odd number width, height, padding, and dots for whitespaces. :param str string: String to test. :param str align: Alignment in any dimension but one at a time. :param list expected: Expected output string. """ actual = align_and_pad_cell(string, (align,), (5, 3), (1, 1, 1, 1), '.') assert actual == expected terminaltables-3.1.0/tests/test_width_and_alignment/test_column_max_width.py000066400000000000000000000104651300101162000276610ustar00rootroot00000000000000"""Test function in module.""" import pytest from terminaltables.width_and_alignment import column_max_width, max_dimensions @pytest.fixture(autouse=True) def patch(monkeypatch): """Monkeypatch before every test function in this module. :param monkeypatch: pytest fixture. """ monkeypatch.setattr('terminaltables.width_and_alignment.terminal_size', lambda: (79, 24)) def test_empty(): """Test with zero-length cells.""" assert column_max_width(max_dimensions([['']])[0], 0, 0, 0, 0) == 79 assert column_max_width(max_dimensions([['', '', '']])[0], 0, 0, 0, 0) == 79 assert column_max_width(max_dimensions([['', '', ''], ['', '', '']])[0], 0, 0, 0, 0) == 79 assert column_max_width(max_dimensions([['']])[0], 0, 2, 1, 2) == 75 assert column_max_width(max_dimensions([['', '', '']])[0], 0, 2, 1, 2) == 69 assert column_max_width(max_dimensions([['', '', ''], ['', '', '']])[0], 0, 2, 1, 2) == 69 def test_single_line(): """Test with single-line cells.""" table_data = [ ['Name', 'Color', 'Type'], ['Avocado', 'green', 'nut'], ['Tomato', 'red', 'fruit'], ['Lettuce', 'green', 'vegetable'], ] inner_widths = max_dimensions(table_data)[0] # '| Lettuce | green | vegetable |' outer, inner, padding = 2, 1, 2 assert column_max_width(inner_widths, 0, outer, inner, padding) == 55 assert column_max_width(inner_widths, 1, outer, inner, padding) == 53 assert column_max_width(inner_widths, 2, outer, inner, padding) == 57 # ' Lettuce | green | vegetable ' outer = 0 assert column_max_width(inner_widths, 0, outer, inner, padding) == 57 assert column_max_width(inner_widths, 1, outer, inner, padding) == 55 assert column_max_width(inner_widths, 2, outer, inner, padding) == 59 # '| Lettuce green vegetable |' outer, inner = 2, 0 assert column_max_width(inner_widths, 0, outer, inner, padding) == 57 assert column_max_width(inner_widths, 1, outer, inner, padding) == 55 assert column_max_width(inner_widths, 2, outer, inner, padding) == 59 # ' Lettuce green vegetable ' outer = 0 assert column_max_width(inner_widths, 0, outer, inner, padding) == 59 assert column_max_width(inner_widths, 1, outer, inner, padding) == 57 assert column_max_width(inner_widths, 2, outer, inner, padding) == 61 # '|Lettuce |green |vegetable |' outer, inner, padding = 2, 1, 1 assert column_max_width(inner_widths, 0, outer, inner, padding) == 58 assert column_max_width(inner_widths, 1, outer, inner, padding) == 56 assert column_max_width(inner_widths, 2, outer, inner, padding) == 60 # '|Lettuce |green |vegetable |' padding = 5 assert column_max_width(inner_widths, 0, outer, inner, padding) == 46 assert column_max_width(inner_widths, 1, outer, inner, padding) == 44 assert column_max_width(inner_widths, 2, outer, inner, padding) == 48 table_data = [ ['Name', 'Color', 'Type'], ['Avocado', 'green', 'nut'], ['Tomato', 'red', 'fruit'], ['Lettuce', 'green', 'vegetable'], ['Watermelon', 'green', 'fruit'], ] inner_widths = max_dimensions(table_data)[0] outer, inner, padding = 2, 1, 2 assert column_max_width(inner_widths, 0, outer, inner, padding) == 55 assert column_max_width(inner_widths, 1, outer, inner, padding) == 50 assert column_max_width(inner_widths, 2, outer, inner, padding) == 54 def test_multi_line(monkeypatch): """Test with multi-line cells. :param monkeypatch: pytest fixture. """ table_data = [ ['Show', 'Characters'], ['Rugrats', ('Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles,\n' 'Susie Carmichael, Dil Pickles, Kimi Finster, Spike')], ['South Park', 'Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick'] ] inner_widths = max_dimensions(table_data)[0] outer, inner, padding = 2, 1, 2 assert column_max_width(inner_widths, 0, outer, inner, padding) == -11 assert column_max_width(inner_widths, 1, outer, inner, padding) == 62 monkeypatch.setattr('terminaltables.width_and_alignment.terminal_size', lambda: (100, 24)) assert column_max_width(inner_widths, 0, outer, inner, padding) == 10 assert column_max_width(inner_widths, 1, outer, inner, padding) == 83 terminaltables-3.1.0/tests/test_width_and_alignment/test_max_dimensions.py000066400000000000000000000057021300101162000273330ustar00rootroot00000000000000# coding: utf-8 """Test function in module.""" import pytest from colorama import Fore from colorclass import Color from termcolor import colored from terminaltables.width_and_alignment import max_dimensions @pytest.mark.parametrize('table_data,expected_w,expected_h', [ ([[]], [], [0]), ([['']], [0], [0]), ([['', '']], [0, 0], [0]), ([[], []], [], [0, 0]), ([[''], ['']], [0], [0, 0]), ([['', ''], ['', '']], [0, 0], [0, 0]), ]) def test_zero_length(table_data, expected_w, expected_h): """Test zero-length or empty tables. :param list table_data: Input table data to test. :param list expected_w: Expected widths. :param list expected_h: Expected heights. """ actual = max_dimensions(table_data) assert actual == (expected_w, expected_h, expected_w, expected_h) def test_single_line(): """Test widths.""" table_data = [ ['Name', 'Color', 'Type'], ['Avocado', 'green', 'nut'], ['Tomato', 'red', 'fruit'], ['Lettuce', 'green', 'vegetable'], ] assert max_dimensions(table_data, 1, 1) == ([7, 5, 9], [1, 1, 1, 1], [9, 7, 11], [1, 1, 1, 1]) table_data.append(['Watermelon', 'green', 'fruit']) assert max_dimensions(table_data, 2, 2) == ([10, 5, 9], [1, 1, 1, 1, 1], [14, 9, 13], [1, 1, 1, 1, 1]) def test_multi_line(): """Test heights.""" table_data = [ ['One\nTwo', 'Buckle\nMy\nShoe'], ] assert max_dimensions(table_data, 0, 0, 1, 1) == ([3, 6], [3], [3, 6], [5]) table_data = [ ['Show', 'Characters'], ['Rugrats', ('Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles,\n' 'Susie Carmichael, Dil Pickles, Kimi Finster, Spike')], ['South Park', 'Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick'] ] assert max_dimensions(table_data, 0, 0, 2, 2) == ([10, 83], [1, 2, 1], [10, 83], [5, 6, 5]) def test_trailing_newline(): r"""Test with trailing \n.""" table_data = [ ['Row One\n'], ['\nRow Two'], ['Row Three\n'], ['\nRow Four'], ] assert max_dimensions(table_data) == ([9], [2, 2, 2, 2], [9], [2, 2, 2, 2]) def test_colors_cjk_rtl(): """Test color text, CJK characters, and RTL characters.""" table_data = [ [Color('{blue}Test{/blue}')], [Fore.BLUE + 'Test' + Fore.RESET], [colored('Test', 'blue')], ] assert max_dimensions(table_data) == ([4], [1, 1, 1], [4], [1, 1, 1]) table_data = [ ['蓝色'], ['世界你好'], ] assert max_dimensions(table_data) == ([8], [1, 1], [8], [1, 1]) table_data = [ ['שלום'], ['معرب'], ] assert max_dimensions(table_data) == ([4], [1, 1], [4], [1, 1]) def test_non_string(): """Test with non-string values.""" table_data = [ [123, 0.9, None, True, False], ] assert max_dimensions(table_data) == ([3, 3, 4, 4, 5], [1], [3, 3, 4, 4, 5], [1]) terminaltables-3.1.0/tests/test_width_and_alignment/test_table_width.py000066400000000000000000000050111300101162000265750ustar00rootroot00000000000000"""Test function in module.""" from terminaltables.width_and_alignment import max_dimensions, table_width def test_empty(): """Test with zero-length cells.""" assert table_width(max_dimensions([['']])[2], 0, 0) == 0 assert table_width(max_dimensions([['', '', '']])[2], 0, 0) == 0 assert table_width(max_dimensions([['', '', ''], ['', '', '']])[2], 0, 0) == 0 assert table_width(max_dimensions([['']], 1, 1)[2], 2, 1) == 4 assert table_width(max_dimensions([['', '', '']], 1, 1)[2], 2, 1) == 10 assert table_width(max_dimensions([['', '', ''], ['', '', '']], 1, 1)[2], 2, 1) == 10 def test_single_line(): """Test with single-line cells.""" table_data = [ ['Name', 'Color', 'Type'], ['Avocado', 'green', 'nut'], ['Tomato', 'red', 'fruit'], ['Lettuce', 'green', 'vegetable'], ] # '| Lettuce | green | vegetable |' outer, inner, outer_widths = 2, 1, max_dimensions(table_data, 1, 1)[2] assert table_width(outer_widths, outer, inner) == 31 # ' Lettuce | green | vegetable ' outer = 0 assert table_width(outer_widths, outer, inner) == 29 # '| Lettuce green vegetable |' outer, inner = 2, 0 assert table_width(outer_widths, outer, inner) == 29 # ' Lettuce green vegetable ' outer = 0 assert table_width(outer_widths, outer, inner) == 27 # '|Lettuce |green |vegetable |' outer, inner, outer_widths = 2, 1, max_dimensions(table_data, 1)[2] assert table_width(outer_widths, outer, inner) == 28 # '|Lettuce |green |vegetable |' outer_widths = max_dimensions(table_data, 3, 2)[2] assert table_width(outer_widths, outer, inner) == 40 table_data = [ ['Name', 'Color', 'Type'], ['Avocado', 'green', 'nut'], ['Tomato', 'red', 'fruit'], ['Lettuce', 'green', 'vegetable'], ['Watermelon', 'green', 'fruit'], ] outer, inner, outer_widths = 2, 1, max_dimensions(table_data, 1, 1)[2] assert table_width(outer_widths, outer, inner) == 34 def test_multi_line(): """Test with multi-line cells.""" table_data = [ ['Show', 'Characters'], ['Rugrats', ('Tommy Pickles, Chuckie Finster, Phillip DeVille, Lillian DeVille, Angelica Pickles,\n' 'Susie Carmichael, Dil Pickles, Kimi Finster, Spike')], ['South Park', 'Stan Marsh, Kyle Broflovski, Eric Cartman, Kenny McCormick'] ] outer, inner, outer_widths = 2, 1, max_dimensions(table_data, 1, 1)[2] assert table_width(outer_widths, outer, inner) == 100 terminaltables-3.1.0/tests/test_width_and_alignment/test_visible_width.py000066400000000000000000000034051300101162000271500ustar00rootroot00000000000000# coding: utf-8 """Test function in module.""" import pytest from colorama import Fore from colorclass import Color from termcolor import colored from terminaltables.width_and_alignment import visible_width @pytest.mark.parametrize('string,expected', [ # str ('hello, world', 12), ('世界你好', 8), ('蓝色', 4), ('שלום', 4), ('معرب', 4), ('hello 世界', 10), # str+ansi ('\x1b[34mhello, world\x1b[39m', 12), ('\x1b[34m世界你好\x1b[39m', 8), ('\x1b[34m蓝色\x1b[39m', 4), ('\x1b[34mשלום\x1b[39m', 4), ('\x1b[34mمعرب\x1b[39m', 4), ('\x1b[34mhello 世界\x1b[39m', 10), # colorclass (Color(u'{blue}hello, world{/blue}'), 12), (Color(u'{blue}世界你好{/blue}'), 8), (Color(u'{blue}蓝色{/blue}'), 4), (Color(u'{blue}שלום{/blue}'), 4), (Color(u'{blue}معرب{/blue}'), 4), (Color(u'{blue}hello 世界{/blue}'), 10), # colorama (Fore.BLUE + 'hello, world' + Fore.RESET, 12), (Fore.BLUE + '世界你好' + Fore.RESET, 8), (Fore.BLUE + '蓝色' + Fore.RESET, 4), (Fore.BLUE + 'שלום' + Fore.RESET, 4), (Fore.BLUE + 'معرب' + Fore.RESET, 4), (Fore.BLUE + 'hello 世界' + Fore.RESET, 10), # termcolor (colored('hello, world', 'blue'), 12), (colored('世界你好', 'blue'), 8), (colored('蓝色', 'blue'), 4), (colored('שלום', 'blue'), 4), (colored('معرب', 'blue'), 4), (colored('hello 世界', 'blue'), 10), ]) def test(string, expected): """Test function with different color libraries. :param str string: Input string to measure. :param int expected: Expected visible width of string (some characters are len() == 1 but take up 2 spaces). """ assert visible_width(string) == expected terminaltables-3.1.0/tox.ini000066400000000000000000000031761300101162000160230ustar00rootroot00000000000000[general] name = terminaltables [tox] envlist = lint,py{34,27,26} [testenv] commands = python -c "import os, sys; sys.platform == 'win32' and os.system('easy_install pillow')" py.test --cov-report term-missing --cov-report xml --cov {[general]name} --cov-config tox.ini {posargs:tests} deps = colorama==0.3.7 colorclass==2.2.0 pytest-cov==2.4.0 termcolor==1.1.0 passenv = WINDIR setenv = PYTHON_EGG_CACHE = {envtmpdir} usedevelop = True [testenv:lint] commands = python setup.py check --strict python setup.py check --strict -m python setup.py check --strict -s python setup.py check_version flake8 --application-import-names={[general]name},tests pylint --rcfile=tox.ini setup.py {[general]name} deps = flake8-docstrings==1.0.2 flake8-import-order==0.9.2 flake8==3.0.4 pep8-naming==0.4.1 pylint==1.6.4 [testenv:docs] changedir = {toxinidir}/docs commands = sphinx-build . _build/html {posargs} deps = robpol86-sphinxcontrib-googleanalytics==0.1 sphinx-rtd-theme==0.1.10a0 sphinx==1.4.8 usedevelop = False [testenv:docsV] commands = sphinx-versioning push docs gh-pages . deps = {[testenv:docs]deps} sphinxcontrib-versioning==2.2.0 passenv = HOME HOSTNAME SSH_AUTH_SOCK TRAVIS* USER [flake8] exclude = .tox/*,build/*,docs/*,env/*,get-pip.py import-order-style = smarkets max-line-length = 120 statistics = True [pylint] disable = locally-disabled, too-few-public-methods, too-many-instance-attributes, ignore = .tox/*,build/*,docs/*,env/*,get-pip.py max-args = 6 max-line-length = 120 reports = no [run] branch = True