├── .btd.yml ├── .editorconfig ├── .github ├── dependabot.yml ├── pull_request_template.md └── workflows │ └── Pipeline.yml ├── .gitignore ├── .idea ├── .name ├── inspectionProfiles │ └── profiles_settings.xml ├── markdown.xml ├── misc.xml ├── modules.xml ├── pyTooling.TerminalUI.iml └── vcs.xml ├── .vscode └── settings.json ├── LICENSE.md ├── README.md ├── dist └── requirements.txt ├── doc ├── Dependencies.rst ├── Doc-License.rst ├── ILineTerminal.rst ├── Installation.rst ├── License.rst ├── Line.rst ├── LineTerminal.rst ├── Makefile ├── Severity.rst ├── Terminal.rst ├── _extensions │ ├── DocumentMember.py │ └── autoapi │ │ ├── __init__.py │ │ ├── apinode.py │ │ └── sphinx.py ├── _static │ └── .gitempty ├── _templates │ └── autoapi │ │ └── module.rst ├── conf.py ├── genindex.rst ├── index.rst ├── make.bat ├── prolog.inc ├── py-modindex.rst ├── requirements.txt └── shields.inc ├── pyTooling └── TerminalUI │ ├── __init__.py │ └── py.typed ├── pyproject.toml ├── requirements.txt ├── setup.py └── tests ├── __init__.py ├── requirements.txt └── unit ├── TerminalUI.py └── __init__.py /.btd.yml: -------------------------------------------------------------------------------- 1 | input: doc 2 | output: _build 3 | requirements: requirements.txt 4 | target: gh-pages 5 | formats: [ html ] 6 | images: 7 | base: btdi/sphinx:pytooling 8 | latex: btdi/latex 9 | theme: https://codeload.GitHub.com/buildthedocs/sphinx.theme/tar.gz/v1 10 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | # end_of_line = lf 6 | insert_final_newline = true 7 | trim_trailing_whitespace = true 8 | indent_style = tab 9 | indent_size = 2 10 | tab_width = 2 11 | 12 | 13 | [*.py] 14 | indent_style = tab 15 | indent_size = 2 16 | 17 | [*.{yml,yaml}] 18 | indent_style = space 19 | indent_size = 2 20 | 21 | [*.{json,ini}] 22 | indent_style = tab 23 | indent_size = 2 24 | 25 | [*.md] 26 | trim_trailing_whitespace = false 27 | 28 | [*.rst] 29 | indent_style = space 30 | indent_size = 3 31 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | # Maintain Python packages 4 | - package-ecosystem: "pip" 5 | directory: "/" 6 | target-branch: dev 7 | commit-message: 8 | prefix: "[Dependabot]" 9 | labels: 10 | - Dependencies 11 | assignees: 12 | - Paebbels 13 | reviewers: 14 | - Paebbels 15 | schedule: 16 | interval: "daily" # Checks on Monday trough Friday. 17 | 18 | # Maintain GitHub Action runners 19 | - package-ecosystem: "github-actions" 20 | directory: "/" 21 | target-branch: dev 22 | commit-message: 23 | prefix: "[Dependabot]" 24 | labels: 25 | - Dependencies 26 | assignees: 27 | - Paebbels 28 | reviewers: 29 | - Paebbels 30 | schedule: 31 | interval: "weekly" 32 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | # New Features 2 | * tbd 3 | 4 | # Changes 5 | * tbd 6 | 7 | # Bug Fixes 8 | * tbd 9 | -------------------------------------------------------------------------------- /.github/workflows/Pipeline.yml: -------------------------------------------------------------------------------- 1 | name: Pipeline 2 | 3 | on: 4 | push: 5 | workflow_dispatch: 6 | 7 | jobs: 8 | 9 | Params: 10 | uses: pyTooling/Actions/.github/workflows/Parameters.yml@r0 11 | with: 12 | name: pyTooling.TerminalUI 13 | 14 | 15 | UnitTesting: 16 | uses: pyTooling/Actions/.github/workflows/UnitTesting.yml@r0 17 | needs: 18 | - Params 19 | with: 20 | jobs: ${{ needs.Params.outputs.python_jobs }} 21 | artifact: ${{ fromJson(needs.Params.outputs.params).artifacts.unittesting }} 22 | 23 | Coverage: 24 | uses: pyTooling/Actions/.github/workflows/CoverageCollection.yml@r0 25 | needs: 26 | - Params 27 | with: 28 | python_version: ${{ fromJson(needs.Params.outputs.params).python_version }} 29 | artifact: ${{ fromJson(needs.Params.outputs.params).artifacts.coverage }} 30 | secrets: 31 | codacy_token: ${{ secrets.CODACY_PROJECT_TOKEN }} 32 | 33 | StaticTypeCheck: 34 | uses: pyTooling/Actions/.github/workflows/StaticTypeCheck.yml@r0 35 | needs: 36 | - Params 37 | with: 38 | python_version: ${{ fromJson(needs.Params.outputs.params).python_version }} 39 | commands: | 40 | cd pyTooling 41 | mypy --html-report ../htmlmypy -p TerminalUI 42 | report: 'htmlmypy' 43 | artifact: ${{ fromJson(needs.Params.outputs.params).artifacts.typing }} 44 | 45 | PublishTestResults: 46 | uses: pyTooling/Actions/.github/workflows/PublishTestResults.yml@r0 47 | needs: 48 | - UnitTesting 49 | 50 | Package: 51 | uses: pyTooling/Actions/.github/workflows/Package.yml@r0 52 | needs: 53 | - Params 54 | - Coverage 55 | with: 56 | python_version: ${{ fromJson(needs.Params.outputs.params).python_version }} 57 | artifact: ${{ fromJson(needs.Params.outputs.params).artifacts.package }} 58 | 59 | Release: 60 | uses: pyTooling/Actions/.github/workflows/Release.yml@r0 61 | if: startsWith(github.ref, 'refs/tags') 62 | needs: 63 | - UnitTesting 64 | - Coverage 65 | - StaticTypeCheck 66 | - Package 67 | 68 | PublishOnPyPI: 69 | uses: pyTooling/Actions/.github/workflows/PublishOnPyPI.yml@r0 70 | if: startsWith(github.ref, 'refs/tags') 71 | needs: 72 | - Params 73 | - Release 74 | - Package 75 | with: 76 | python_version: ${{ fromJson(needs.Params.outputs.params).python_version }} 77 | requirements: -r dist/requirements.txt 78 | artifact: ${{ fromJson(needs.Params.outputs.params).artifacts.package }} 79 | secrets: 80 | PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }} 81 | 82 | # VerifyDocs: 83 | # uses: pyTooling/Actions/.github/workflows/VerifyDocs.yml@r0 84 | # needs: 85 | # - Params 86 | # with: 87 | # python_version: ${{ fromJson(needs.Params.outputs.params).python_version }} 88 | 89 | BuildTheDocs: 90 | uses: pyTooling/Actions/.github/workflows/BuildTheDocs.yml@r0 91 | needs: 92 | - Params 93 | # - VerifyDocs 94 | with: 95 | artifact: ${{ fromJson(needs.Params.outputs.params).artifacts.doc }} 96 | 97 | PublishToGitHubPages: 98 | uses: pyTooling/Actions/.github/workflows/PublishToGitHubPages.yml@r0 99 | needs: 100 | - Params 101 | - BuildTheDocs 102 | - Coverage 103 | - StaticTypeCheck 104 | with: 105 | doc: ${{ fromJson(needs.Params.outputs.params).artifacts.doc }} 106 | coverage: ${{ fromJson(needs.Params.outputs.params).artifacts.coverage }} 107 | typing: ${{ fromJson(needs.Params.outputs.params).artifacts.typing }} 108 | 109 | ArtifactCleanUp: 110 | uses: pyTooling/Actions/.github/workflows/ArtifactCleanUp.yml@r0 111 | needs: 112 | - Params 113 | - UnitTesting 114 | - Coverage 115 | - StaticTypeCheck 116 | - BuildTheDocs 117 | - PublishToGitHubPages 118 | - PublishTestResults 119 | with: 120 | package: ${{ fromJson(needs.Params.outputs.params).artifacts.package }} 121 | remaining: | 122 | ${{ fromJson(needs.Params.outputs.params).artifacts.unittesting }}-ubuntu-3.7 123 | ${{ fromJson(needs.Params.outputs.params).artifacts.unittesting }}-ubuntu-3.8 124 | ${{ fromJson(needs.Params.outputs.params).artifacts.unittesting }}-ubuntu-3.9 125 | ${{ fromJson(needs.Params.outputs.params).artifacts.unittesting }}-ubuntu-3.10 126 | ${{ fromJson(needs.Params.outputs.params).artifacts.unittesting }}-windows-3.7 127 | ${{ fromJson(needs.Params.outputs.params).artifacts.unittesting }}-windows-3.8 128 | ${{ fromJson(needs.Params.outputs.params).artifacts.unittesting }}-windows-3.9 129 | ${{ fromJson(needs.Params.outputs.params).artifacts.unittesting }}-windows-3.10 130 | ${{ fromJson(needs.Params.outputs.params).artifacts.unittesting }}-msys2-3.9 131 | ${{ fromJson(needs.Params.outputs.params).artifacts.unittesting }}-macos-3.7 132 | ${{ fromJson(needs.Params.outputs.params).artifacts.unittesting }}-macos-3.8 133 | ${{ fromJson(needs.Params.outputs.params).artifacts.unittesting }}-macos-3.9 134 | ${{ fromJson(needs.Params.outputs.params).artifacts.unittesting }}-macos-3.10 135 | ${{ fromJson(needs.Params.outputs.params).artifacts.coverage }} 136 | ${{ fromJson(needs.Params.outputs.params).artifacts.typing }} 137 | ${{ fromJson(needs.Params.outputs.params).artifacts.doc }} 138 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Python cache and object files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # Coverage.py 6 | .coverage 7 | .cov 8 | coverage.xml 9 | 10 | # setuptools 11 | /build/**/*.* 12 | /dist/**/*.* 13 | /*.egg-info 14 | 15 | # Dependencies 16 | !requirements.txt 17 | 18 | # Sphinx 19 | doc/_build/ 20 | doc/pyTooling.TerminalUI/**/*.* 21 | !doc/pyTooling.TerminalUI/index.rst 22 | 23 | # BuildTheDocs 24 | doc/_theme/**/*.* 25 | 26 | # IntelliJ project files 27 | /.idea/workspace.xml 28 | 29 | # Git files 30 | !.git* 31 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | pyTooling.TerminalUI -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/markdown.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.idea/pyTooling.TerminalUI.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.trimTrailingWhitespace": false, 3 | } 4 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | This is a local copy of the Apache License Version 2.0. 2 | The original can be obtained here: [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0) 3 | 4 | -------------------------------------------------------------------------------- 5 | 6 | # Apache License 7 | 8 | Version 2.0, January 2004 9 | 10 | ## TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 11 | 12 | ### 1. Definitions. 13 | 14 | *"License"* shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. 15 | 16 | *"Licensor"* shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. 17 | 18 | *"Legal Entity"* shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. 19 | 20 | *"You"* (or *"Your"*) shall mean an individual or Legal Entity exercising permissions granted by this License. 21 | 22 | *"Source"* form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. 23 | 24 | *"Object"* form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. 25 | 26 | *"Work"* shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). 27 | 28 | *"Derivative Works"* shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. 29 | 30 | *"Contribution"* shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, *"submitted"* means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as *"Not a Contribution."* 31 | 32 | *"Contributor"* shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 33 | 34 | ### 2. Grant of Copyright License. 35 | Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 36 | 37 | ### 3. Grant of Patent License. 38 | Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 39 | 40 | ### 4. Redistribution. 41 | You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: 42 | 43 | - You must give any other recipients of the Work or Derivative Works a copy of this License; and 44 | - You must cause any modified files to carry prominent notices stating that You changed the files; and 45 | - You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and 46 | - If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. 47 | 48 | You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 49 | 50 | ### 5. Submission of Contributions. 51 | Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 52 | 53 | ### 6. Trademarks. 54 | This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 55 | 56 | ### 7. Disclaimer of Warranty. 57 | Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 58 | 59 | ### 8. Limitation of Liability. 60 | In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 61 | 62 | ### 9. Accepting Warranty or Additional Liability. 63 | While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. 64 | 65 | 66 | ## Appendix: How to apply the Apache License to your work 67 | 68 | To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. 69 | 70 | Copyright [yyyy] [name of copyright owner] 71 | 72 | Licensed under the Apache License, Version 2.0 (the "License"); 73 | you may not use this file except in compliance with the License. 74 | You may obtain a copy of the License at 75 | 76 | http://www.apache.org/licenses/LICENSE-2.0 77 | 78 | Unless required by applicable law or agreed to in writing, software 79 | distributed under the License is distributed on an "AS IS" BASIS, 80 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 81 | See the License for the specific language governing permissions and 82 | limitations under the License. 83 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Sourcecode on GitHub](https://img.shields.io/badge/pyTooling-pyTooling.TerminalUI-323131.svg?logo=github&longCache=true)](https://GitHub.com/pyTooling/pyTooling.TerminalUI) 2 | [![Sourcecode License](https://img.shields.io/pypi/l/pyTooling.TerminalUI?logo=GitHub&label=code%20license)](LICENSE.md) 3 | [![GitHub tag (latest SemVer incl. pre-release)](https://img.shields.io/github/v/tag/pyTooling/pyTooling.TerminalUI?logo=GitHub&include_prereleases)](https://GitHub.com/pyTooling/pyTooling.TerminalUI/tags) 4 | [![GitHub release (latest SemVer incl. including pre-releases)](https://img.shields.io/github/v/release/pyTooling/pyTooling.TerminalUI?logo=GitHub&include_prereleases)](https://GitHub.com/pyTooling/pyTooling.TerminalUI/releases/latest) 5 | [![GitHub release date](https://img.shields.io/github/release-date/pyTooling/pyTooling.TerminalUI?logo=GitHub)](https://GitHub.com/pyTooling/pyTooling.TerminalUI/releases) 6 | [![Dependents (via libraries.io)](https://img.shields.io/librariesio/dependents/pypi/pyTooling.TerminalUI?logo=librariesdotio)](https://GitHub.com/pyTooling/pyTooling.TerminalUI/network/dependents) 7 | [![GitHub Workflow - Build and Test Status](https://img.shields.io/github/workflow/status/pyTooling/pyTooling.TerminalUI/Unit%20Testing,%20Coverage%20Collection,%20Package,%20Release,%20Documentation%20and%20Publish?label=Pipeline&logo=GitHub%20Actions&logoColor=FFFFFF)](https://GitHub.com/pyTooling/pyTooling.TerminalUI/actions/workflows/Pipeline.yml) 8 | [![Codacy - Quality](https://img.shields.io/codacy/grade/e8a1b6e33d564f82927235e17fb26e93?logo=Codacy)](https://www.codacy.com/gh/pyTooling/pyTooling.TerminalUI) 9 | [![Codacy - Coverage](https://img.shields.io/codacy/coverage/e8a1b6e33d564f82927235e17fb26e93?logo=Codacy)](https://www.codacy.com/gh/pyTooling/pyTooling.TerminalUI) 10 | [![Codecov - Branch Coverage](https://img.shields.io/codecov/c/github/pyTooling/pyTooling.TerminalUI?logo=Codecov)](https://codecov.io/gh/pyTooling/pyTooling.TerminalUI) 11 | [![Libraries.io SourceRank](https://img.shields.io/librariesio/sourcerank/pypi/pyTooling.TerminalUI?logo=librariesdotio)](https://libraries.io/github/pyTooling/pyTooling.TerminalUI/sourcerank) 12 | [![PyPI](https://img.shields.io/pypi/v/pyTooling.TerminalUI?logo=PyPI&logoColor=FBE072)](https://pypi.org/project/pyTooling.TerminalUI/) 13 | ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/pyTooling.TerminalUI?logo=PyPI&logoColor=FBE072) 14 | ![PyPI - Status](https://img.shields.io/pypi/status/pyTooling.TerminalUI?logo=PyPI&logoColor=FBE072) 15 | [![Libraries.io status for latest release](https://img.shields.io/librariesio/release/pypi/pyTooling.TerminalUI?logo=librariesdotio)](https://libraries.io/github/pyTooling/pyTooling.TerminalUI) 16 | [![Requires.io](https://img.shields.io/requires/github/pyTooling/pyTooling.TerminalUI)](https://requires.io/github/pyTooling/pyTooling.TerminalUI/requirements/?branch=main) 17 | [![Documentation License](https://img.shields.io/badge/doc%20license-CC--BY%204.0-green?logo=readthedocs)](doc/Doc-License.rst) 18 | [![Documentation - Read Now!](https://img.shields.io/badge/doc-read%20now%20%E2%9E%9A-blueviolet?logo=readthedocs)](https://pyTooling.GitHub.io/pyTooling.TerminalUI) 19 | 20 | # pyTooling.TerminalUI 21 | 22 | A set of helpers to implement a text user interface (TUI) in a terminal. 23 | 24 | ## Features 25 | * Colored command line outputs based on colorama 26 | * Message classification in fatal, error, warning, normal, quiet, ... 27 | * Get information like terminal dimensions from underlying terminal window 28 | 29 | 30 | ## Simple Terminal Application 31 | 32 | This is a minimal terminal application example which inherits from `LineTerminal`. 33 | 34 | ```python 35 | from pyTooling.TerminalUI import LineTerminal 36 | 37 | class Application(LineTerminal): 38 | def __init__(self): 39 | super().__init__(verbose=True, debug=True, quiet=False) 40 | 41 | def run(self): 42 | self.WriteNormal("This is a simple application.") 43 | self.WriteWarning("This is a warning message.") 44 | self.WriteError("This is an error message.") 45 | 46 | # entry point 47 | if __name__ == "__main__": 48 | Application.versionCheck((3, 6, 0)) 49 | app = Application() 50 | app.run() 51 | app.exit() 52 | ``` 53 | 54 | ## Complex Terminal Application 55 | 56 | This example hands over the terminal instance to a submodule, which implements 57 | `ILineTerminal`, so the submodule can also use the terminal's writing methods. 58 | 59 | ```python 60 | from pathlib import Path 61 | from pyTooling.TerminalUI import LineTerminal, ILineTerminal 62 | 63 | class SubModule(ILineTerminal): 64 | def __init__(self, configFile: Path, terminal): 65 | super().__init__(terminal) 66 | 67 | if not configFile.exists(): 68 | self.WriteError(f"Config file '{configFile}' not found.") 69 | 70 | 71 | class Application(LineTerminal): 72 | def __init__(self): 73 | super().__init__(verbose=True, debug=True, quiet=False) 74 | 75 | mod = SubModule(Path("config.yml"), self) 76 | 77 | def run(self): 78 | pass 79 | 80 | # entry point 81 | if __name__ == "__main__": 82 | app = Application() 83 | app.run() 84 | ``` 85 | 86 | 87 | ## Contributors 88 | 89 | * [Patrick Lehmann](https://GitHub.com/Paebbels) (Maintainer) 90 | * [and more...](https://GitHub.com/pyTooling/pyTooling.TerminalUI/graphs/contributors) 91 | 92 | 93 | ## License 94 | 95 | This Python package (source code) licensed under [Apache License 2.0](LICENSE.md). 96 | The accompanying documentation is licensed under [Creative Commons - Attribution 4.0 (CC-BY 4.0)](doc/Doc-License.rst). 97 | 98 | 99 | ------------------------- 100 | 101 | SPDX-License-Identifier: Apache-2.0 102 | -------------------------------------------------------------------------------- /dist/requirements.txt: -------------------------------------------------------------------------------- 1 | wheel 2 | twine 3 | -------------------------------------------------------------------------------- /doc/Dependencies.rst: -------------------------------------------------------------------------------- 1 | .. _dependency: 2 | 3 | Dependencies 4 | ############ 5 | 6 | .. |img-TerminalUI-lib-status| image:: https://img.shields.io/librariesio/release/pypi/pyTooling.TerminalUI 7 | :alt: Libraries.io status for latest release 8 | :height: 22 9 | :target: https://libraries.io/github/pyTooling/pyTooling.TerminalUI 10 | .. |img-TerminalUI-req-status| image:: https://img.shields.io/requires/github/pyTooling/pyTooling.TerminalUI 11 | :alt: Requires.io 12 | :height: 22 13 | :target: https://requires.io/github/pyTooling/pyTooling.TerminalUI/requirements/?branch=main 14 | 15 | +------------------------------------------+------------------------------------------+ 16 | | `Libraries.io `_ | `Requires.io `_ | 17 | +==========================================+==========================================+ 18 | | |img-TerminalUI-lib-status| | |img-TerminalUI-req-status| | 19 | +------------------------------------------+------------------------------------------+ 20 | 21 | .. _dependency-package: 22 | 23 | pyTooling.TerminalUI Package (Mandatory) 24 | **************************************** 25 | 26 | .. rubric:: Manually Installing Package Requirements 27 | 28 | Use the :file:`requirements.txt` file to install all dependencies via ``pip3`` or install the package directly from 29 | PyPI (see :ref:`installation`). 30 | 31 | .. code-block:: shell 32 | 33 | pip3 install -U -r requirements.txt 34 | 35 | 36 | .. rubric:: Dependency List 37 | 38 | +----------------------------------------------------------+-------------+-------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------+ 39 | | **Package** | **Version** | **License** | **Dependencies** | 40 | +==========================================================+=============+===========================================================================================+=================================================================================================================================+ 41 | | `colorama `__ | ≥0.4.5 | `BSD-3-Clause `__ | None | 42 | +----------------------------------------------------------+-------------+-------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------+ 43 | | `pyTooling `__ | ≥2.1.0 | `Apache License, 2.0 `__ | *None* | 44 | +----------------------------------------------------------+-------------+-------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------+ 45 | 46 | 47 | 48 | .. _dependency-testing: 49 | 50 | Unit Testing / Coverage / Type Checking (Optional) 51 | ************************************************** 52 | 53 | Additional Python packages needed for testing, code coverage collection and static type checking. These packages are 54 | only needed for developers or on a CI server, thus sub-dependencies are not evaluated further. 55 | 56 | 57 | .. rubric:: Manually Installing Test Requirements 58 | 59 | Use the :file:`tests/requirements.txt` file to install all dependencies via ``pip3``. The file will recursively install 60 | the mandatory dependencies too. 61 | 62 | .. code-block:: shell 63 | 64 | pip3 install -U -r tests/requirements.txt 65 | 66 | 67 | .. rubric:: Dependency List 68 | 69 | +-----------------------------------------------------------+-------------+----------------------------------------------------------------------------------------+----------------------+ 70 | | **Package** | **Version** | **License** | **Dependencies** | 71 | +===========================================================+=============+========================================================================================+======================+ 72 | | `pytest `__ | ≥7.1.1 | `MIT `__ | *Not yet evaluated.* | 73 | +-----------------------------------------------------------+-------------+----------------------------------------------------------------------------------------+----------------------+ 74 | | `pytest-cov `__ | ≥3.0.0 | `MIT `__ | *Not yet evaluated.* | 75 | +-----------------------------------------------------------+-------------+----------------------------------------------------------------------------------------+----------------------+ 76 | | `Coverage `__ | ≥6.3 | `Apache License, 2.0 `__ | *Not yet evaluated.* | 77 | +-----------------------------------------------------------+-------------+----------------------------------------------------------------------------------------+----------------------+ 78 | | `mypy `__ | ≥0.931 | `MIT `__ | *Not yet evaluated.* | 79 | +-----------------------------------------------------------+-------------+----------------------------------------------------------------------------------------+----------------------+ 80 | | `lxml `__ | ≥4.8 | `BSD 3-Clause `__ | *Not yet evaluated.* | 81 | +-----------------------------------------------------------+-------------+----------------------------------------------------------------------------------------+----------------------+ 82 | 83 | 84 | .. _dependency-documentation: 85 | 86 | Sphinx Documentation (Optional) 87 | ******************************* 88 | 89 | Additional Python packages needed for documentation generation. These packages are only needed for developers or on a 90 | CI server, thus sub-dependencies are not evaluated further. 91 | 92 | 93 | .. rubric:: Manually Installing Documentation Requirements 94 | 95 | Use the :file:`doc/requirements.txt` file to install all dependencies via ``pip3``. The file will recursively install 96 | the mandatory dependencies too. 97 | 98 | .. code-block:: shell 99 | 100 | pip3 install -U -r doc/requirements.txt 101 | 102 | 103 | .. rubric:: Dependency List 104 | 105 | +-------------------------------------------------------------------------------------------------+--------------+----------------------------------------------------------------------------------------------------------+----------------------+ 106 | | **Package** | **Version** | **License** | **Dependencies** | 107 | +=================================================================================================+==============+==========================================================================================================+======================+ 108 | | `Sphinx `__ | ≥4.5.0 | `BSD 3-Clause `__ | *Not yet evaluated.* | 109 | +-------------------------------------------------------------------------------------------------+--------------+----------------------------------------------------------------------------------------------------------+----------------------+ 110 | | `sphinx_btd_theme `__ | ≥0.5.2 | `MIT `__ | *Not yet evaluated.* | 111 | +-------------------------------------------------------------------------------------------------+--------------+----------------------------------------------------------------------------------------------------------+----------------------+ 112 | | !! `sphinx_fontawesome `__ | ≥0.0.6 | `GPL 2.0 `__ | *Not yet evaluated.* | 113 | +-------------------------------------------------------------------------------------------------+--------------+----------------------------------------------------------------------------------------------------------+----------------------+ 114 | | `sphinx_autodoc_typehints `__ | ≥1.18.1 | `MIT `__ | *Not yet evaluated.* | 115 | +-------------------------------------------------------------------------------------------------+--------------+----------------------------------------------------------------------------------------------------------+----------------------+ 116 | 117 | 118 | .. _dependency-packaging: 119 | 120 | Packaging (Optional) 121 | ******************** 122 | 123 | Additional Python packages needed for installation package generation. These packages are only needed for developers or 124 | on a CI server, thus sub-dependencies are not evaluated further. 125 | 126 | 127 | .. rubric:: Manually Installing Packaging Requirements 128 | 129 | Use the :file:`build/requirements.txt` file to install all dependencies via ``pip3``. The file will recursively 130 | install the mandatory dependencies too. 131 | 132 | .. code-block:: shell 133 | 134 | pip3 install -U -r build/requirements.txt 135 | 136 | 137 | .. rubric:: Dependency List 138 | 139 | +----------------------------------------------------------------------------+--------------+----------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------+ 140 | | **Package** | **Version** | **License** | **Dependencies** | 141 | +============================================================================+==============+==========================================================================================================+======================================================================================================================================================+ 142 | | `pyTooling `__ | ≥2.1.0 | `Apache License, 2.0 `__ | *None* | 143 | +----------------------------------------------------------------------------+--------------+----------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------+ 144 | | `wheel `__ | any | `MIT `__ | *Not yet evaluated.* | 145 | +----------------------------------------------------------------------------+--------------+----------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------+ 146 | 147 | 148 | .. _dependency-publishing: 149 | 150 | Publishing (CI-Server only) 151 | *************************** 152 | 153 | Additional Python packages needed for publishing the generated installation package to e.g, PyPI or any equivalent 154 | services. These packages are only needed for maintainers or on a CI server, thus sub-dependencies are not evaluated 155 | further. 156 | 157 | 158 | .. rubric:: Manually Installing Publishing Requirements 159 | 160 | Use the :file:`dist/requirements.txt` file to install all dependencies via ``pip3``. The file will recursively 161 | install the mandatory dependencies too. 162 | 163 | .. code-block:: shell 164 | 165 | pip3 install -U -r dist/requirements.txt 166 | 167 | 168 | .. rubric:: Dependency List 169 | 170 | +----------------------------------------------------------+--------------+-------------------------------------------------------------------------------------------+----------------------+ 171 | | **Package** | **Version** | **License** | **Dependencies** | 172 | +==========================================================+==============+===========================================================================================+======================+ 173 | | `wheel `__ | any | `MIT `__ | *Not yet evaluated.* | 174 | +----------------------------------------------------------+--------------+-------------------------------------------------------------------------------------------+----------------------+ 175 | | `Twine `__ | any | `Apache License, 2.0 `__ | *Not yet evaluated.* | 176 | +----------------------------------------------------------+--------------+-------------------------------------------------------------------------------------------+----------------------+ 177 | -------------------------------------------------------------------------------- /doc/Doc-License.rst: -------------------------------------------------------------------------------- 1 | .. _DOCLICENSE: 2 | 3 | .. Note:: This is a local copy of the `Creative Commons - Attribution 4.0 International (CC BY 4.0) `__. 4 | 5 | .. Attention:: This **CC BY 4.0** license applies only to the **documentation** of this project. 6 | 7 | 8 | Creative Commons Attribution 4.0 International 9 | ############################################## 10 | 11 | Creative Commons Corporation (“Creative Commons”) is not a law firm and does not 12 | provide legal services or legal advice. Distribution of Creative Commons public 13 | licenses does not create a lawyer-client or other relationship. Creative Commons 14 | makes its licenses and related information available on an “as-is” basis. 15 | Creative Commons gives no warranties regarding its licenses, any material 16 | licensed under their terms and conditions, or any related information. Creative 17 | Commons disclaims all liability for damages resulting from their use to the 18 | fullest extent possible. 19 | 20 | .. topic:: Using Creative Commons Public Licenses 21 | 22 | Creative Commons public licenses provide a standard set of terms and conditions 23 | that creators and other rights holders may use to share original works of 24 | authorship and other material subject to copyright and certain other rights 25 | specified in the public license below. The following considerations are for 26 | informational purposes only, are not exhaustive, and do not form part of our 27 | licenses. 28 | 29 | * **Considerations for licensors:** Our public licenses are intended for use 30 | by those authorized to give the public permission to use material in ways 31 | otherwise restricted by copyright and certain other rights. Our licenses are 32 | irrevocable. Licensors should read and understand the terms and conditions 33 | of the license they choose before applying it. Licensors should also secure 34 | all rights necessary before applying our licenses so that the public can reuse 35 | the material as expected. Licensors should clearly mark any material not 36 | subject to the license. This includes other CC-licensed material, or material 37 | used under an exception or limitation to copyright. 38 | `More considerations for licensors `__. 39 | 40 | * **Considerations for the public:** By using one of our public licenses, a 41 | licensor grants the public permission to use the licensed material under 42 | specified terms and conditions. If the licensor’s permission is not necessary 43 | for any reason–for example, because of any applicable exception or limitation 44 | to copyright–then that use is not regulated by the license. Our licenses grant 45 | only permissions under copyright and certain other rights that a licensor has 46 | authority to grant. Use of the licensed material may still be restricted for 47 | other reasons, including because others have copyright or other rights in the 48 | material. A licensor may make special requests, such as asking that all 49 | changes be marked or described. Although not required by our licenses, you are 50 | encouraged to respect those requests where reasonable. 51 | `More considerations for the public `__. 52 | 53 | :xlarge:`Creative Commons Attribution 4.0 International Public License` 54 | 55 | By exercising the Licensed Rights (defined below), You accept and agree to be 56 | bound by the terms and conditions of this Creative Commons Attribution 4.0 57 | International Public License ("Public License"). To the extent this Public 58 | License may be interpreted as a contract, You are granted the Licensed Rights 59 | in consideration of Your acceptance of these terms and conditions, and the 60 | Licensor grants You such rights in consideration of benefits the Licensor 61 | receives from making the Licensed Material available under these terms and 62 | conditions. 63 | 64 | Section 1 – Definitions. 65 | ======================== 66 | 67 | a. **Adapted Material** means material subject to Copyright and Similar 68 | Rights that is derived from or based upon the Licensed Material and in 69 | which the Licensed Material is translated, altered, arranged, transformed, or 70 | otherwise modified in a manner requiring permission under the Copyright and 71 | Similar Rights held by the Licensor. For purposes of this Public License, 72 | where the Licensed Material is a musical work, performance, or sound 73 | recording, Adapted Material is always produced where the Licensed Material 74 | is synched in timed relation with a moving image. 75 | 76 | b. **Adapter's License** means the license You apply to Your Copyright and 77 | Similar Rights in Your contributions to Adapted Material in accordance with 78 | the terms and conditions of this Public License. 79 | 80 | c. **Copyright and Similar Rights** means copyright and/or similar rights 81 | closely related to copyright including, without limitation, performance, 82 | broadcast, sound recording, and Sui Generis Database Rights, without regard 83 | to how the rights are labeled or categorized. For purposes of this Public 84 | License, the rights specified in Section 2(b)(1)-(2) are not Copyright and 85 | Similar Rights. 86 | 87 | d. **Effective Technological Measures** means those measures that, in the 88 | absence of proper authority, may not be circumvented under laws fulfilling 89 | obligations under Article 11 of the WIPO Copyright Treaty adopted on 90 | December 20, 1996, and/or similar international agreements. 91 | 92 | e. **Exceptions and Limitations** means fair use, fair dealing, and/or any 93 | other exception or limitation to Copyright and Similar Rights that applies to 94 | Your use of the Licensed Material. 95 | 96 | f. **Licensed Material** means the artistic or literary work, database, or 97 | other material to which the Licensor applied this Public License. 98 | 99 | g. **Licensed Rights** means the rights granted to You subject to the terms 100 | and conditions of this Public License, which are limited to all Copyright and 101 | Similar Rights that apply to Your use of the Licensed Material and that the 102 | Licensor has authority to license. 103 | 104 | h. **Licensor** means the individual(s) or entity(ies) granting rights under 105 | this Public License. 106 | 107 | i. **Share** means to provide material to the public by any means or process 108 | that requires permission under the Licensed Rights, such as reproduction, 109 | public display, public performance, distribution, dissemination, 110 | communication, or importation, and to make material available to the public 111 | including in ways that members of the public may access the material from a 112 | place and at a time individually chosen by them. 113 | 114 | j. **Sui Generis Database Rights** means rights other than copyright 115 | resulting from Directive 96/9/EC of the European Parliament and of the 116 | Council of 11 March 1996 on the legal protection of databases, as amended 117 | and/or succeeded, as well as other essentially equivalent rights anywhere 118 | in the world. 119 | 120 | k. **You** means the individual or entity exercising the Licensed Rights 121 | under this Public License. **Your** has a corresponding meaning. 122 | 123 | Section 2 – Scope. 124 | ================== 125 | 126 | a. **License grant.** 127 | 128 | 1. Subject to the terms and conditions of this Public License, the Licensor 129 | hereby grants You a worldwide, royalty-free, non-sublicensable, 130 | non-exclusive, irrevocable license to exercise the Licensed Rights in the 131 | Licensed Material to: 132 | 133 | A. reproduce and Share the Licensed Material, in whole or in part; and 134 | 135 | B. produce, reproduce, and Share Adapted Material. 136 | 137 | 2. :underline:`Exceptions and Limitations.` For the avoidance of doubt, where 138 | Exceptions and Limitations apply to Your use, this Public License does not 139 | apply, and You do not need to comply with its terms and conditions. 140 | 141 | 3. :underline:`Term.` The term of this Public License is specified in Section 6(a). 142 | 143 | 4. :underline:`Media and formats`; :underline:`technical modifications allowed.` The Licensor 144 | authorizes You to exercise the Licensed Rights in all media and formats 145 | whether now known or hereafter created, and to make technical 146 | modifications necessary to do so. The Licensor waives and/or agrees not to 147 | assert any right or authority to forbid You from making technical 148 | modifications necessary to exercise the Licensed Rights, including 149 | technical modifications necessary to circumvent Effective Technological 150 | Measures. For purposes of this Public License, simply making modifications 151 | authorized by this Section 2(a)(4) never produces Adapted Material. 152 | 153 | 5. :underline:`Downstream recipients.` 154 | 155 | A. :underline:`Offer from the Licensor – Licensed Material.` Every recipient of 156 | the Licensed Material automatically receives an offer from the 157 | Licensor to exercise the Licensed Rights under the terms and 158 | conditions of this Public License. 159 | 160 | B. :underline:`No downstream restrictions.` You may not offer or impose any 161 | additional or different terms or conditions on, or apply any Effective 162 | Technological Measures to, the Licensed Material if doing so restricts 163 | exercise of the Licensed Rights by any recipient of the Licensed 164 | Material. 165 | 166 | 6. :underline:`No endorsement.` Nothing in this Public License constitutes or may 167 | be construed as permission to assert or imply that You are, or that Your 168 | use of the Licensed Material is, connected with, or sponsored, endorsed, 169 | or granted official status by, the Licensor or others designated to 170 | receive attribution as provided in Section 3(a)(1)(A)(i). 171 | 172 | b. **Other rights.** 173 | 174 | 1. Moral rights, such as the right of integrity, are not licensed under this 175 | Public License, nor are publicity, privacy, and/or other similar 176 | personality rights; however, to the extent possible, the Licensor waives 177 | and/or agrees not to assert any such rights held by the Licensor to the 178 | limited extent necessary to allow You to exercise the Licensed Rights, but 179 | not otherwise. 180 | 181 | 2. Patent and trademark rights are not licensed under this Public License. 182 | 183 | 3. To the extent possible, the Licensor waives any right to collect royalties 184 | from You for the exercise of the Licensed Rights, whether directly or 185 | through a collecting society under any voluntary or waivable statutory or 186 | compulsory licensing scheme. In all other cases the Licensor expressly 187 | reserves any right to collect such royalties. 188 | 189 | Section 3 – License Conditions. 190 | =============================== 191 | 192 | Your exercise of the Licensed Rights is expressly made subject to the following conditions. 193 | 194 | a. **Attribution.** 195 | 196 | 1. If You Share the Licensed Material (including in modified form), You must: 197 | 198 | A. retain the following if it is supplied by the Licensor with the 199 | Licensed Material: 200 | 201 | i. identification of the creator(s) of the Licensed Material and any 202 | others designated to receive attribution, in any reasonable manner 203 | requested by the Licensor (including by pseudonym if designated); 204 | 205 | ii. a copyright notice; 206 | 207 | iii. a notice that refers to this Public License; 208 | 209 | iv. a notice that refers to the disclaimer of warranties; 210 | 211 | v. a URI or hyperlink to the Licensed Material to the extent reasonably 212 | practicable; 213 | 214 | B. indicate if You modified the Licensed Material and retain an 215 | indication of any previous modifications; and 216 | 217 | C. indicate the Licensed Material is licensed under this Public License, 218 | and include the text of, or the URI or hyperlink to, this Public 219 | License. 220 | 221 | 2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner 222 | based on the medium, means, and context in which You Share the Licensed 223 | Material. For example, it may be reasonable to satisfy the conditions by 224 | providing a URI or hyperlink to a resource that includes the required 225 | information. 226 | 227 | 3. If requested by the Licensor, You must remove any of the information 228 | required by Section 3(a)(1)(A) to the extent reasonably practicable. 229 | 230 | 4. If You Share Adapted Material You produce, the Adapter's License You apply 231 | must not prevent recipients of the Adapted Material from complying with 232 | this Public License. 233 | 234 | Section 4 – Sui Generis Database Rights. 235 | ======================================== 236 | 237 | Where the Licensed Rights include Sui Generis Database Rights that apply to Your 238 | use of the Licensed Material: 239 | 240 | a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, 241 | reuse, reproduce, and Share all or a substantial portion of the contents of 242 | the database; 243 | 244 | b. if You include all or a substantial portion of the database contents in a 245 | database in which You have Sui Generis Database Rights, then the database 246 | in which You have Sui Generis Database Rights (but not its individual 247 | contents) is Adapted Material; and 248 | 249 | c. You must comply with the conditions in Section 3(a) if You Share all or a 250 | substantial portion of the contents of the database. 251 | 252 | For the avoidance of doubt, this Section 4 supplements and does not replace 253 | Your obligations under this Public License where the Licensed Rights include 254 | other Copyright and Similar Rights. 255 | 256 | Section 5 – Disclaimer of Warranties and Limitation of Liability. 257 | ================================================================= 258 | 259 | a. **Unless otherwise separately undertaken by the Licensor, to the extent 260 | possible, the Licensor offers the Licensed Material as-is and as-available, 261 | and makes no representations or warranties of any kind concerning the 262 | Licensed Material, whether express, implied, statutory, or other. This 263 | includes, without limitation, warranties of title, merchantability, 264 | fitness for a particular purpose, non-infringement, absence of latent or 265 | other defects, accuracy, or the presence or absence of errors, whether or 266 | not known or discoverable. Where disclaimers of warranties are not allowed 267 | in full or in part, this disclaimer may not apply to You.** 268 | 269 | b. **To the extent possible, in no event will the Licensor be liable to You 270 | on any legal theory (including, without limitation, negligence) or 271 | otherwise for any direct, special, indirect, incidental, consequential, 272 | punitive, exemplary, or other losses, costs, expenses, or damages arising 273 | out of this Public License or use of the Licensed Material, even if the 274 | Licensor has been advised of the possibility of such losses, costs, expenses, 275 | or damages. Where a limitation of liability is not allowed in full or in 276 | part, this limitation may not apply to You.** 277 | 278 | c. The disclaimer of warranties and limitation of liability provided above 279 | shall be interpreted in a manner that, to the extent possible, most 280 | closely approximates an absolute disclaimer and waiver of all liability. 281 | 282 | Section 6 – Term and Termination. 283 | ================================= 284 | 285 | a. This Public License applies for the term of the Copyright and Similar Rights 286 | licensed here. However, if You fail to comply with this Public License, then 287 | Your rights under this Public License terminate automatically. 288 | 289 | b. Where Your right to use the Licensed Material has terminated under 290 | Section 6(a), it reinstates: 291 | 292 | 1. automatically as of the date the violation is cured, provided it is cured 293 | within 30 days of Your discovery of the violation; or 294 | 295 | 2. upon express reinstatement by the Licensor. 296 | 297 | For the avoidance of doubt, this Section 6(b) does not affect any right the 298 | Licensor may have to seek remedies for Your violations of this Public License. 299 | 300 | c. For the avoidance of doubt, the Licensor may also offer the Licensed Material 301 | under separate terms or conditions or stop distributing the Licensed Material 302 | at any time; however, doing so will not terminate this Public License. 303 | 304 | d. Sections 1, 5, 6, 7, and 8 survive termination of this Public License. 305 | 306 | Section 7 – Other Terms and Conditions. 307 | ======================================= 308 | 309 | a. The Licensor shall not be bound by any additional or different terms or 310 | conditions communicated by You unless expressly agreed. 311 | 312 | b. Any arrangements, understandings, or agreements regarding the Licensed 313 | Material not stated herein are separate from and independent of the terms 314 | and conditions of this Public License. 315 | 316 | Section 8 – Interpretation. 317 | =========================== 318 | 319 | a. For the avoidance of doubt, this Public License does not, and shall not be 320 | interpreted to, reduce, limit, restrict, or impose conditions on any use of 321 | the Licensed Material that could lawfully be made without permission under 322 | this Public License. 323 | 324 | b. To the extent possible, if any provision of this Public License is deemed 325 | unenforceable, it shall be automatically reformed to the minimum extent 326 | necessary to make it enforceable. If the provision cannot be reformed, it 327 | shall be severed from this Public License without affecting the 328 | enforceability of the remaining terms and conditions. 329 | 330 | c. No term or condition of this Public License will be waived and no failure to 331 | comply consented to unless expressly agreed to by the Licensor. 332 | 333 | d. Nothing in this Public License constitutes or may be interpreted as a 334 | limitation upon, or waiver of, any privileges and immunities that apply to 335 | the Licensor or You, including from the legal processes of any jurisdiction 336 | or authority. 337 | 338 | ------------------ 339 | 340 | Creative Commons is not a party to its public licenses. Notwithstanding, 341 | Creative Commons may elect to apply one of its public licenses to material it 342 | publishes and in those instances will be considered the “Licensor.” Except for 343 | the limited purpose of indicating that material is shared under a Creative 344 | Commons public license or as otherwise permitted by the Creative Commons 345 | policies published at `creativecommons.org/policies `__, 346 | Creative Commons does not authorize the use of the trademark “Creative Commons” 347 | or any other trademark or logo of Creative Commons without its prior written 348 | consent including, without limitation, in connection with any unauthorized 349 | modifications to any of its public licenses or any other arrangements, 350 | understandings, or agreements concerning use of licensed material. For the 351 | avoidance of doubt, this paragraph does not form part of the public licenses. 352 | 353 | Creative Commons may be contacted at `creativecommons.org `__ 354 | -------------------------------------------------------------------------------- /doc/ILineTerminal.rst: -------------------------------------------------------------------------------- 1 | ILineTerminal 2 | ############# 3 | 4 | .. autoclass:: pyTooling.TerminalUI.ILineTerminal 5 | :members: 6 | :private-members: 7 | -------------------------------------------------------------------------------- /doc/Installation.rst: -------------------------------------------------------------------------------- 1 | Installation/Updates 2 | #################### 3 | 4 | Using PIP 5 | ********* 6 | 7 | Installation using PIP 8 | ====================== 9 | 10 | .. code-block:: bash 11 | 12 | pip3 install pyTooling.TerminalUI 13 | 14 | 15 | Updating using PIP 16 | ================== 17 | 18 | .. code-block:: bash 19 | 20 | pip3 install -U pyTooling.TerminalUI 21 | -------------------------------------------------------------------------------- /doc/License.rst: -------------------------------------------------------------------------------- 1 | .. Note:: This is a local copy of the `Apache License Version 2.0 `_. 2 | 3 | Apache License 2.0 4 | ################## 5 | 6 | Version 2.0, January 2004 7 | 8 | **TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION** 9 | 10 | 11 | 1. Definitions. 12 | =============== 13 | **"License"** shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. 14 | 15 | **"Licensor"** shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. 16 | 17 | **"Legal Entity"** shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that 18 | entity. For the purposes of this definition, **"control"** means (i) the power, direct or indirect, to cause the direction or management of such entity, whether 19 | by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. 20 | 21 | **"You"** (or **"Your"**) shall mean an individual or Legal Entity exercising permissions granted by this License. 22 | 23 | **"Source"** form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and 24 | configuration files. 25 | 26 | **"Object"** form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object 27 | code, generated documentation, and conversions to other media types. 28 | 29 | **"Work"** shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is 30 | included in or attached to the work (an example is provided in the Appendix below). 31 | 32 | **"Derivative Works"** shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, 33 | annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works 34 | shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. 35 | 36 | **"Contribution"** shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative 37 | Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to 38 | submit on behalf of the copyright owner. For the purposes of this definition, **"submitted"** means any form of electronic, verbal, or written communication 39 | sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue 40 | tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is 41 | conspicuously marked or otherwise designated in writing by the copyright owner as **"Not a Contribution."** 42 | 43 | **"Contributor"** shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently 44 | incorporated within the Work. 45 | 46 | 2. Grant of Copyright License. 47 | ============================== 48 | Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, 49 | irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such 50 | Derivative Works in Source or Object form. 51 | 52 | 3. Grant of Patent License. 53 | =========================== 54 | Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, 55 | irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such 56 | license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of 57 | their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim 58 | or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then 59 | any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 60 | 61 | 4. Redistribution. 62 | ================== 63 | You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, 64 | provided that You meet the following conditions: 65 | 66 | * You must give any other recipients of the Work or Derivative Works a copy of this License; and 67 | * You must cause any modified files to carry prominent notices stating that You changed the files; and 68 | * You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source 69 | form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and 70 | * If the Work includes a **"NOTICE"** text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the 71 | attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the 72 | following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the 73 | Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE 74 | file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, 75 | alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. 76 | 77 | You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or 78 | distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise 79 | complies with the conditions stated in this License. 80 | 81 | 5. Submission of Contributions. 82 | =============================== 83 | Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and 84 | conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any 85 | separate license agreement you may have executed with Licensor regarding such Contributions. 86 | 87 | 6. Trademarks. 88 | ============== 89 | This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable 90 | and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 91 | 92 | 7. Disclaimer of Warranty. 93 | ========================== 94 | Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, 95 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, 96 | MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and 97 | assume any risks associated with Your exercise of permissions under this License. 98 | 99 | 8. Limitation of Liability. 100 | =========================== 101 | In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate 102 | and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or 103 | consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages 104 | for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been 105 | advised of the possibility of such damages. 106 | 107 | 9. Accepting Warranty or Additional Liability. 108 | ============================================== 109 | While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other 110 | liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole 111 | responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability 112 | incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. 113 | 114 | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- 115 | 116 | **Appendix: How to apply the Apache License to your work** 117 | 118 | To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying 119 | information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or 120 | class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. 121 | 122 | .. code-block:: none 123 | 124 | Copyright [yyyy] [name of copyright owner] 125 | 126 | Licensed under the Apache License, Version 2.0 (the "License"); 127 | you may not use this file except in compliance with the License. 128 | You may obtain a copy of the License at 129 | 130 | http://www.apache.org/licenses/LICENSE-2.0 131 | 132 | Unless required by applicable law or agreed to in writing, software 133 | distributed under the License is distributed on an "AS IS" BASIS, 134 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 135 | See the License for the specific language governing permissions and 136 | limitations under the License. 137 | -------------------------------------------------------------------------------- /doc/Line.rst: -------------------------------------------------------------------------------- 1 | Line 2 | #### 3 | 4 | ``Line`` represents a single line in a line-based terminal application. If a 5 | line is visible, depends on the :class:`~pyTooling.TerminalUI.Severity`-level of a 6 | ``Line`` object. 7 | 8 | .. autoclass:: pyTooling.TerminalUI.Line 9 | :show-inheritance: 10 | :members: 11 | :private-members: 12 | -------------------------------------------------------------------------------- /doc/LineTerminal.rst: -------------------------------------------------------------------------------- 1 | LineTerminal 2 | ############ 3 | 4 | .. inheritance-diagram:: pyTooling.TerminalUI.LineTerminal 5 | :parts: 1 6 | 7 | .. autoclass:: pyTooling.TerminalUI.LineTerminal 8 | :show-inheritance: 9 | :members: 10 | :private-members: 11 | -------------------------------------------------------------------------------- /doc/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /doc/Severity.rst: -------------------------------------------------------------------------------- 1 | Severity 2 | ######## 3 | 4 | .. autoclass:: pyTooling.TerminalUI.Severity 5 | :show-inheritance: 6 | :members: 7 | :private-members: 8 | -------------------------------------------------------------------------------- /doc/Terminal.rst: -------------------------------------------------------------------------------- 1 | Terminal 2 | ******** 3 | 4 | .. autoclass:: pyTooling.TerminalUI.Terminal 5 | :show-inheritance: 6 | :members: 7 | :private-members: 8 | -------------------------------------------------------------------------------- /doc/_extensions/DocumentMember.py: -------------------------------------------------------------------------------- 1 | # ============================================================================== 2 | # Authors: Patrick Lehmann 3 | # 4 | # Python Module: 5 | # 6 | # Description: 7 | # ------------------------------------ 8 | # - TODO 9 | # 10 | # License: 11 | # ============================================================================== 12 | # Copyright 2007-2016 Patrick Lehmann - Dresden, Germany 13 | # 14 | # Licensed under the Apache License, Version 2.0 (the "License"); 15 | # you may not use this file except in compliance with the License. 16 | # You may obtain a copy of the License at 17 | # 18 | # http://www.apache.org/licenses/LICENSE-2.0 19 | # 20 | # Unless required by applicable law or agreed to in writing, software 21 | # distributed under the License is distributed on an "AS IS" BASIS, 22 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 23 | # See the License for the specific language governing permissions and 24 | # limitations under the License. 25 | # ============================================================================== 26 | # 27 | from SphinxExtensions import DocumentMemberAttribute 28 | 29 | 30 | def skip_member_handler(app, what, name, obj, skip, options): 31 | # try: 32 | # print("skip_member_handler: ", obj) 33 | # except: 34 | # print("skip_member_handler: ERROR") 35 | 36 | try: 37 | attributes = DocumentMemberAttribute.GetAttributes(obj) 38 | if (len(attributes) > 0): 39 | # print("*#"*20) 40 | # try: 41 | # print("skip_member_handler: ", obj) 42 | # except: 43 | # print("skip_member_handler: ERROR") 44 | 45 | return not attributes[0].value 46 | except: 47 | pass 48 | return None 49 | 50 | def setup(app): 51 | app.connect('autodoc-skip-member', skip_member_handler) 52 | -------------------------------------------------------------------------------- /doc/_extensions/autoapi/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Copyright (C) 2015 Carlos Jenkins 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | from __future__ import unicode_literals, absolute_import 19 | from __future__ import print_function, division 20 | 21 | from .apinode import __doc__, APINode # noqa 22 | 23 | __author__ = 'Carlos Jenkins' 24 | __email__ = 'carlos@jenkins.co.cr' 25 | __version__ = '1.3.1' 26 | 27 | __all__ = ['APINode'] 28 | -------------------------------------------------------------------------------- /doc/_extensions/autoapi/apinode.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Copyright (C) 2015 Carlos Jenkins 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | """ 19 | Module that provides the module tree node :class:`APINode`. 20 | 21 | This class will load the module identified by ``name`` and recursively build a 22 | tree with all it's submodules and subpackages. In the process, each node 23 | analyze and fetch the public API of that module. 24 | 25 | ``name`` can be any node, like the root package, or any subpackage or submodule 26 | and a tree will be built from there. ``name`` must follow the standard 27 | "dot notation" for importing a module. 28 | 29 | This class will not assume any special naming, or perform any complex analysis 30 | to determine what must be in the public interface. This is because it is not 31 | only a difficult problem, but it involves analyzing deeply the namespace of the 32 | module which can be quite expensive. 33 | 34 | In general it is very difficult to determine in a module namespace what 35 | elements are private or public declared locally, private or public but declared 36 | in another module and brought into the module local namespace 37 | (``from x import y``), third party library, Python standard library, etc. At 38 | the end, any algorithm that tries to determine this will eventually fail to 39 | meet the requirements or expectations of the developer, leaving false positives 40 | or removing elements expected to be present in the public API. 41 | 42 | For example, a common scenario is that some modules, specially package entry 43 | points ``__init__.py``, can be setup to expose the public API of their sibling 44 | modules, possible causing several objects to be identified as part of the 45 | public API of both modules. 46 | 47 | Because of this the approach taken by this module follows the rule in PEP20 48 | "Explicit is better than implicit". In consequence, the node will consider 49 | elements as public if they are explicitly listed in the ``__api__`` or 50 | ``__all__`` variables. It is up to the developer to list the elements that must 51 | be published in the public API. 52 | 53 | ``__api__`` is a special variable introduced by this module, and it exists for 54 | situation were for whatever reason the developer don't want to list in the 55 | ``__all__`` variable an element that needs to be published in the public API. 56 | 57 | This class will extract all elements identified in ONE of those listings (not 58 | the union), with ``__api__`` having the precedence. If none of those variables 59 | exists in the module then it will be assumed that no public API exists for that 60 | module and no futher actions will be taken. 61 | 62 | If any of those variables exists this class will iterate all elements listed in 63 | them and will catalog them in four categories: 64 | 65 | - Functions. 66 | - Exceptions. 67 | - Classes. 68 | - Variables. 69 | 70 | Being Variables the default if it cannot be determined that an element belongs 71 | to any of other categories. 72 | """ 73 | 74 | from __future__ import unicode_literals, absolute_import 75 | from __future__ import print_function, division 76 | 77 | from logging import getLogger 78 | from traceback import format_exc 79 | from importlib import import_module 80 | from pkgutil import iter_modules 81 | from inspect import isclass, isfunction 82 | from collections import OrderedDict 83 | 84 | 85 | log = getLogger(__name__) 86 | 87 | 88 | class APINode(object): 89 | """ 90 | Tree node class for module instrospection. 91 | 92 | :param str name: Name of the module to build the tree from. It must follow 93 | the "dot notation" of the import mechanism. 94 | :param dict directory: Directory to store the index of all the modules. 95 | If None, the default, the root node will create one a pass it to the 96 | subnodes. 97 | 98 | **Attributes:** 99 | 100 | :var name: Name of the current module. 101 | :var subname: Last part of the name of this module. For example if name is 102 | ``my.module.another`` the subname will be ``another``. 103 | :var directory: Directory of the tree. This is a :py:class:`OrderedDict` 104 | that will register all modules name with it's associated node 105 | :class:`APINode`. All nodes of a tree share this index and thus 106 | the whole tree can be queried from any node. 107 | :var module: The loaded module. 108 | :var subnodes: A list of :class:`APINode` with all child submodules 109 | and subpackages. 110 | :var subnodes_failed: A list of submodules and subpackages names that 111 | failed to import. 112 | 113 | **Public API categories:** 114 | 115 | :var functions: A :py:class:`OrderedDict` of all functions found in the 116 | public API of the module. 117 | :var classes: A :py:class:`OrderedDict` of all classes found in the 118 | public API of the module. 119 | :var exceptions: A :py:class:`OrderedDict` of all exceptions found in the 120 | public API of the module. 121 | :var variables: A :py:class:`OrderedDict` of all other elements found in 122 | the public API of the module. 123 | 124 | In all categories the order on which the elements are listed is preserved. 125 | """ 126 | 127 | def __init__(self, name, directory=None): 128 | self.module = import_module(name) 129 | self.name = name 130 | self.subname = name.split('.')[-1] 131 | 132 | self.functions = OrderedDict() 133 | self.classes = OrderedDict() 134 | self.exceptions = OrderedDict() 135 | self.variables = OrderedDict() 136 | self.api = OrderedDict(( 137 | ('functions', self.functions), 138 | ('classes', self.classes), 139 | ('exceptions', self.exceptions), 140 | ('variables', self.variables), 141 | )) 142 | 143 | self.subnodes = [] 144 | self.subnodes_failed = [] 145 | 146 | self.directory = OrderedDict() 147 | if directory is not None: 148 | self.directory = directory 149 | 150 | self._relevant = None 151 | 152 | # Now that all node public attributes exists and module was imported 153 | # register itself in the directory 154 | self.directory[self.name] = self 155 | 156 | # Check if package and iterate over subnodes 157 | if hasattr(self.module, '__path__'): 158 | for _, subname, ispkg in iter_modules( 159 | self.module.__path__, self.module.__name__ + '.'): 160 | log.info('Recursing into {}'.format(subname)) 161 | 162 | try: 163 | subnode = APINode(subname, self.directory) 164 | self.subnodes.append(subnode) 165 | except: # Overbroad exception handling on purpose 166 | log.error('Failed to import {}'.format(subname)) 167 | log.debug(format_exc()) 168 | self.subnodes_failed.append(subname) 169 | 170 | # Fetch all public objects 171 | public = OrderedDict() 172 | for public_key in ['__api__', '__all__']: 173 | if not hasattr(self.module, public_key): 174 | continue 175 | 176 | for obj_name in getattr(self.module, public_key): 177 | if not hasattr(self.module, obj_name): 178 | log.warning( 179 | 'Module {} doesn\'t have a element {}'.format( 180 | self.name, obj_name 181 | ) 182 | ) 183 | continue 184 | public[obj_name] = getattr(self.module, obj_name) 185 | break 186 | 187 | # Categorize objects 188 | for obj_name, obj in public.items(): 189 | if isclass(obj): 190 | if issubclass(obj, Exception): 191 | self.exceptions[obj_name] = obj 192 | continue 193 | self.classes[obj_name] = obj 194 | continue 195 | if isfunction(obj): 196 | self.functions[obj_name] = obj 197 | continue 198 | self.variables[obj_name] = obj 199 | 200 | # Flag to mark if this branch is relevant 201 | # For self._relevant, None means undertermined 202 | if self.is_root(): 203 | self.is_relevant() 204 | 205 | def has_public_api(self): 206 | """ 207 | Check if this node has a public API. 208 | 209 | :rtype: bool 210 | :return: True if any category has at least one element. 211 | """ 212 | return any(self.api.values()) 213 | 214 | def is_leaf(self): 215 | """ 216 | Check if the current node is a leaf in the tree. 217 | 218 | A leaf node not necessarily is a module, it can be a package without 219 | modules (just the entry point ``__init__.py``). 220 | 221 | :rtype: bool 222 | :return: True if no other subnodes exists for this node. 223 | """ 224 | return not self.subnodes 225 | 226 | def is_root(self): 227 | """ 228 | Check if the current node is the root node. 229 | 230 | :rtype: bool 231 | :return: True if the current node is the root node. 232 | """ 233 | for key in self.directory.keys(): 234 | return key == self.name 235 | raise Exception('Empty directory!') 236 | 237 | def is_relevant(self): 238 | """ 239 | Check if this branch of the tree is relevant. 240 | 241 | A branch is relevant if the current node has a public API or if any of 242 | its subnodes is relevant (in order to reach relevant nodes). 243 | 244 | Relevancy is determined at initialization by the root node. 245 | 246 | :rtype: bool 247 | :return: True if the current node is relevant. 248 | """ 249 | if self._relevant is not None: 250 | return self._relevant 251 | 252 | relevant = False 253 | if self.has_public_api() or \ 254 | any(s.is_relevant() for s in self.subnodes): 255 | relevant = True 256 | 257 | self._relevant = relevant 258 | 259 | return self._relevant 260 | 261 | def depth(self): 262 | """ 263 | Get the depth of the current node in the tree. 264 | 265 | :rtype: int 266 | :return: The depth of the node. For example, for node ``my.add.foo`` 267 | the depth is 3. 268 | """ 269 | return len(self.name.split('.')) 270 | 271 | def get_module(self, name): 272 | """ 273 | Get a module node by it's name. 274 | 275 | This is just a helper that does lookup on the directory index. 276 | 277 | :rtype: :class:`APINode` or None 278 | :return: The module node identified by ``name`` in the tree. ``None`` 279 | if the name doesn't exists. 280 | """ 281 | return self.directory.get(name, None) 282 | 283 | def walk(self): 284 | """ 285 | Traverse the tree top-down. 286 | 287 | :return: This method will yield tuples ``(node, [leaves])`` for each 288 | node in the tree. 289 | """ 290 | if self.is_leaf(): 291 | raise StopIteration() 292 | 293 | yield (self, [n for n in self.subnodes if n.is_leaf()]) 294 | 295 | for subnode in [n for n in self.subnodes if not n.is_leaf()]: 296 | for step in subnode.walk(): 297 | yield step 298 | 299 | def __iter__(self): 300 | return self.walk 301 | 302 | def tree(self, level=0, fullname=True): 303 | """ 304 | Pretty print the subtree at the current node. 305 | 306 | For example, for the module ``confspec``: 307 | 308 | :: 309 | 310 | confspec 311 | confspec.manager [c] 312 | confspec.options [c] 313 | confspec.providers [c, v] 314 | confspec.providers.dict [c] 315 | confspec.providers.ini [c] 316 | confspec.providers.json [c] 317 | confspec.utils [f] 318 | confspec.validation [f] 319 | 320 | The tags at the right of the name shows what kind of elements are 321 | present in the public interfaces of those modules. 322 | 323 | :param int level: Indentation level. 324 | :param bool fullname: Plot the full name of the module or just it's 325 | subname. 326 | """ 327 | name = [(' ' * level)] 328 | if fullname: 329 | name.append(self.name) 330 | else: 331 | name.append(self.subname) 332 | 333 | tags = [] 334 | for tag, category in zip(['f', 'c', 'e', 'v'], self.api.values()): 335 | if category: 336 | tags.append(tag) 337 | if tags: 338 | name.append(' [{}]'.format(', '.join(tags))) 339 | 340 | output = [''.join(name)] 341 | for subnode in self.subnodes: 342 | output.append(subnode.tree(level=level + 1, fullname=fullname)) 343 | return '\n'.join(output) 344 | 345 | def __str__(self): 346 | return self.tree() 347 | 348 | def __repr__(self): 349 | return self.name 350 | 351 | 352 | __all__ = ['APINode'] 353 | __api__ = [] 354 | -------------------------------------------------------------------------------- /doc/_extensions/autoapi/sphinx.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Copyright (C) 2015 Carlos Jenkins 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | """ 19 | Glue for Sphinx API. 20 | """ 21 | 22 | from __future__ import unicode_literals, absolute_import 23 | from __future__ import print_function, division 24 | 25 | from inspect import getdoc 26 | from logging import getLogger 27 | from traceback import format_exc 28 | from os.path import join, dirname, abspath, exists 29 | from os import environ 30 | 31 | from jinja2.sandbox import SandboxedEnvironment 32 | from sphinx.util.osutil import ensuredir 33 | from sphinx.jinja2glue import BuiltinTemplateLoader 34 | 35 | from . import __version__ 36 | from .apinode import APINode 37 | 38 | 39 | log = getLogger(__name__) 40 | 41 | 42 | def handle_exception(func): 43 | """ 44 | Utility decorator to report all exceptions in module without making Sphinx 45 | to die. 46 | """ 47 | def wrapper(app): 48 | try: 49 | func(app) 50 | except Exception: 51 | app.warn( 52 | 'Unhandled exception in autoapi module: \n{}'.format( 53 | format_exc() 54 | ) 55 | ) 56 | 57 | # Preserve docstring 58 | if hasattr(func, '__doc__'): 59 | wrapper.__doc__ = func.__doc__ 60 | 61 | return wrapper 62 | 63 | 64 | def filter_summary(obj): 65 | """ 66 | Jinja2 filter that allows to extract the documentation summary of an 67 | object. 68 | """ 69 | try: 70 | doc = getdoc(obj) 71 | if doc is None: 72 | return 'Undocumented.' 73 | 74 | summary = doc.split('\n').pop(0) 75 | summary.replace('\\', '\\\\') # Escape backslash in RST 76 | return summary 77 | except: 78 | log.error( 79 | 'AutoApi failed to determine autosummary for obj: {}'.format(obj) 80 | ) 81 | log.error(format_exc()) 82 | 83 | return 'AutoApi: Unable to determine summary.' 84 | 85 | 86 | def get_template_env(app): 87 | """ 88 | Get the template environment. 89 | 90 | .. note:: 91 | 92 | Template should be loaded as a package_data using 93 | :py:function:`pkgutil.get_data`, but because we want the user to 94 | override the default template we need to hook it to the Sphinx loader, 95 | and thus a file system approach is required as it is implemented like 96 | that. 97 | """ 98 | template_dir = [join(dirname(abspath(__file__)), 'templates')] 99 | template_loader = BuiltinTemplateLoader() 100 | template_loader.init(app.builder, dirs=template_dir) 101 | template_env = SandboxedEnvironment(loader=template_loader) 102 | template_env.filters['summary'] = filter_summary 103 | return template_env 104 | 105 | 106 | @handle_exception 107 | def builder_inited(app): 108 | """ 109 | autoapi Sphinx extension hook for the ``builder-inited`` event. 110 | 111 | This hook will read the configuration value ``autoapi_modules`` and render 112 | the modules described in it. 113 | 114 | See http://sphinx-doc.org/extdev/appapi.html#event-builder-inited 115 | """ 116 | # Get modules to build documentation for 117 | modules = app.config.autoapi_modules 118 | if not modules: 119 | return 120 | 121 | # Overwrite all files in an ReadTheDocs environment when the first builder runs (pickle) 122 | overrideDefault = True 123 | if (environ.get('READTHEDOCS') == "True"): 124 | #if (app.buildername != "pickle"): 125 | overrideDefault = False 126 | 127 | # Get template environment 128 | template_env = get_template_env(app) 129 | 130 | print("===============================") 131 | print("overrideDefault set to: {0!s}".format(overrideDefault)) 132 | print("===============================") 133 | 134 | for module, overrides in modules.items(): 135 | 136 | # Get options 137 | options = { 138 | 'prune': False, 139 | 'override': overrideDefault, 140 | 'template': 'module', 141 | 'output': module 142 | } 143 | if overrides: 144 | options.update(overrides) 145 | 146 | # Get template 147 | template = template_env.get_template( 148 | 'autoapi/{}.rst'.format(options['template']) 149 | ) 150 | 151 | # Build API tree 152 | tree = APINode(module) 153 | 154 | # Gather nodes to document 155 | if options['prune']: 156 | nodes = [ 157 | node for node in tree.directory.values() 158 | if node.is_relevant() 159 | ] 160 | else: 161 | nodes = tree.directory.values() 162 | 163 | if not nodes: 164 | continue 165 | 166 | # Define output directory 167 | out_dir = join(app.env.srcdir, options['output']) 168 | ensuredir(out_dir) 169 | 170 | # Iterate nodes and render them 171 | for node in nodes: 172 | out_file = join(out_dir, node.name + app.config.source_suffix[0]) 173 | 174 | # Skip file if it override is off and it exists 175 | if not options['override'] and exists(out_file): 176 | continue 177 | 178 | # Consider only subnodes that are relevant if prune is enabled 179 | subnodes = node.subnodes 180 | if options['prune']: 181 | subnodes = [ 182 | subnode for subnode in node.subnodes 183 | if subnode.is_relevant() 184 | ] 185 | 186 | # Write file 187 | with open(out_file, 'w') as fd: 188 | fd.write( 189 | template.render( 190 | node=node, 191 | subnodes=subnodes 192 | ) 193 | ) 194 | 195 | 196 | def setup(app): 197 | """ 198 | autoapi Sphinx extension setup. 199 | 200 | See http://sphinx-doc.org/extdev/tutorial.html#the-setup-function 201 | """ 202 | # autodoc is required 203 | app.setup_extension('sphinx.ext.autodoc') 204 | app.add_config_value('autoapi_modules', {}, True) 205 | app.connect(str('builder-inited'), builder_inited) 206 | return {'version': __version__} 207 | 208 | 209 | __all__ = ['builder_inited', 'setup'] 210 | -------------------------------------------------------------------------------- /doc/_static/.gitempty: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyTooling/pyTooling.TerminalUI/6a01c78dcbc6ac9a51cf67a79d18b89484a2547b/doc/_static/.gitempty -------------------------------------------------------------------------------- /doc/_templates/autoapi/module.rst: -------------------------------------------------------------------------------- 1 | .. # Template modified by Patrick Lehmann 2 | * removed automodule on top, because private members are activated for autodoc (no doubled documentation). 3 | * Made sections like 'submodules' bold text, but no headlines to reduce number of ToC levels. 4 | 5 | =={{ '=' * node.name|length }}== 6 | ``{{ node.name }}`` 7 | =={{ '=' * node.name|length }}== 8 | 9 | .. py:module:: {{ node.name }} 10 | 11 | {##} 12 | {%- block modules -%} 13 | {%- if subnodes %} 14 | 15 | **Submodules** 16 | 17 | 18 | .. toctree:: 19 | {% for item in subnodes %} 20 | {{ item.name }} 21 | {%- endfor %} 22 | {##} 23 | {%- endif -%} 24 | {%- endblock -%} 25 | {##} 26 | .. currentmodule:: {{ node.name }} 27 | {##} 28 | {%- block functions -%} 29 | {%- if node.functions %} 30 | 31 | **Functions** 32 | 33 | {% for item, obj in node.functions.items() -%} 34 | - :py:func:`{{ item }}`: 35 | {{ obj|summary }} 36 | 37 | {% endfor -%} 38 | 39 | {% for item in node.functions %} 40 | .. autofunction:: {{ item }} 41 | {##} 42 | {%- endfor -%} 43 | {%- endif -%} 44 | {%- endblock -%} 45 | 46 | {%- block classes -%} 47 | {%- if node.classes %} 48 | 49 | **Classes** 50 | 51 | {% for item, obj in node.classes.items() -%} 52 | - :py:class:`{{ item }}`: 53 | {{ obj|summary }} 54 | 55 | {% endfor -%} 56 | 57 | {% for item in node.classes %} 58 | .. autoclass:: {{ item }} 59 | :members: 60 | 61 | .. rubric:: Inheritance 62 | .. inheritance-diagram:: {{ item }} 63 | :parts: 1 64 | {##} 65 | {%- endfor -%} 66 | {%- endif -%} 67 | {%- endblock -%} 68 | 69 | {%- block exceptions -%} 70 | {%- if node.exceptions %} 71 | 72 | **Exceptions** 73 | 74 | {% for item, obj in node.exceptions.items() -%} 75 | - :py:exc:`{{ item }}`: 76 | {{ obj|summary }} 77 | 78 | {% endfor -%} 79 | 80 | {% for item in node.exceptions %} 81 | .. autoexception:: {{ item }} 82 | 83 | .. rubric:: Inheritance 84 | .. inheritance-diagram:: {{ item }} 85 | :parts: 1 86 | {##} 87 | {%- endfor -%} 88 | {%- endif -%} 89 | {%- endblock -%} 90 | 91 | {%- block variables -%} 92 | {%- if node.variables %} 93 | 94 | **Variables** 95 | 96 | {% for item, obj in node.variables.items() -%} 97 | - :py:data:`{{ item }}` 98 | {% endfor -%} 99 | 100 | {% for item, obj in node.variables.items() %} 101 | .. autodata:: {{ item }} 102 | :annotation: 103 | 104 | .. code-block:: text 105 | 106 | {{ obj|pprint|indent(6) }} 107 | {##} 108 | {%- endfor -%} 109 | {%- endif -%} 110 | {%- endblock -%} 111 | -------------------------------------------------------------------------------- /doc/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # This file only contains a selection of the most common options. For a full 4 | # list see the documentation: 5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 6 | 7 | # -- Path setup -------------------------------------------------------------- 8 | 9 | # If extensions (or modules to document with autodoc) are in another directory, 10 | # add these directories to sys.path here. If the directory is relative to the 11 | # documentation root, use os.path.abspath to make it absolute, like shown here. 12 | # 13 | from json import loads 14 | from os.path import abspath 15 | from pathlib import Path 16 | from sys import path as sys_path 17 | 18 | from pyTooling.Packaging import extractVersionInformation 19 | 20 | sys_path.insert(0, abspath('.')) 21 | sys_path.insert(0, abspath('..')) 22 | sys_path.insert(0, abspath('../pyTooling.TerminalUI')) 23 | sys_path.insert(0, abspath('_extensions')) 24 | #sys_path.insert(0, os.path.abspath('_themes/sphinx_rtd_theme')) 25 | 26 | 27 | # ============================================================================== 28 | # Project information and versioning 29 | # ============================================================================== 30 | # The version info for the project you're documenting, acts as replacement for 31 | # |version| and |release|, also used in various other places throughout the 32 | # built documents. 33 | project = "pyTooling.TerminalUI" 34 | 35 | packageInformationFile = Path(f"../{project.replace('.', '/')}/__init__.py") 36 | versionInformation = extractVersionInformation(packageInformationFile) 37 | 38 | author = versionInformation.Author 39 | copyright = versionInformation.Copyright 40 | version = ".".join(versionInformation.Version.split(".")[:2]) # e.g. 2.3 The short X.Y version. 41 | release = versionInformation.Version 42 | 43 | 44 | # ============================================================================== 45 | # Miscellaneous settings 46 | # ============================================================================== 47 | # The master toctree document. 48 | master_doc = 'index' 49 | 50 | # Add any paths that contain templates here, relative to this directory. 51 | templates_path = ['_templates'] 52 | 53 | # List of patterns, relative to source directory, that match files and 54 | # directories to ignore when looking for source files. 55 | # This pattern also affects html_static_path and html_extra_path. 56 | exclude_patterns = [ 57 | "_build", 58 | "_themes", 59 | "Thumbs.db", 60 | ".DS_Store" 61 | ] 62 | 63 | # The name of the Pygments (syntax highlighting) style to use. 64 | pygments_style = 'stata-dark' 65 | 66 | 67 | # ============================================================================== 68 | # Restructured Text settings 69 | # ============================================================================== 70 | prologPath = "prolog.inc" 71 | try: 72 | with open(prologPath, "r") as prologFile: 73 | rst_prolog = prologFile.read() 74 | except Exception as ex: 75 | print(f"[ERROR:] While reading '{prologPath}'.") 76 | print(ex) 77 | rst_prolog = "" 78 | 79 | 80 | # ============================================================================== 81 | # Options for HTML output 82 | # ============================================================================== 83 | html_theme_options = { 84 | 'home_breadcrumbs': True, 85 | 'vcs_pageview_mode': 'blob', 86 | } 87 | 88 | html_context = {} 89 | ctx = Path(__file__).resolve().parent / 'context.json' 90 | if ctx.is_file(): 91 | html_context.update(loads(ctx.open('r').read())) 92 | 93 | html_theme_path = ["."] 94 | html_theme = "_theme" 95 | 96 | # Add any paths that contain custom static files (such as style sheets) here, 97 | # relative to this directory. They are copied after the builtin static files, 98 | # so a file named "default.css" will overwrite the builtin "default.css". 99 | html_static_path = ['_static'] 100 | 101 | # Output file base name for HTML help builder. 102 | htmlhelp_basename = 'pyToolingDoc' 103 | 104 | # If not None, a 'Last updated on:' timestamp is inserted at every page 105 | # bottom, using the given strftime format. 106 | # The empty string is equivalent to '%b %d, %Y'. 107 | html_last_updated_fmt = "%d.%m.%Y" 108 | 109 | 110 | # ============================================================================== 111 | # Options for LaTeX / PDF output 112 | # ============================================================================== 113 | from textwrap import dedent 114 | 115 | latex_elements = { 116 | # The paper size ('letterpaper' or 'a4paper'). 117 | 'papersize': 'a4paper', 118 | 119 | # The font size ('10pt', '11pt' or '12pt'). 120 | #'pointsize': '10pt', 121 | 122 | # Additional stuff for the LaTeX preamble. 123 | 'preamble': dedent(r""" 124 | % ================================================================================ 125 | % User defined additional preamble code 126 | % ================================================================================ 127 | % Add more Unicode characters for pdfLaTeX. 128 | % - Alternatively, compile with XeLaTeX or LuaLaTeX. 129 | % - https://GitHub.com/sphinx-doc/sphinx/issues/3511 130 | % 131 | \ifdefined\DeclareUnicodeCharacter 132 | \DeclareUnicodeCharacter{2265}{$\geq$} 133 | \DeclareUnicodeCharacter{21D2}{$\Rightarrow$} 134 | \fi 135 | 136 | 137 | % ================================================================================ 138 | """), 139 | 140 | # Latex figure (float) alignment 141 | #'figure_align': 'htbp', 142 | } 143 | 144 | # Grouping the document tree into LaTeX files. List of tuples 145 | # (source start file, target name, title, 146 | # author, documentclass [howto, manual, or own class]). 147 | latex_documents = [ 148 | ( master_doc, 149 | 'pyTooling.TerminalUI.tex', 150 | 'The pyTooling.TerminalUI Documentation', 151 | 'Patrick Lehmann', 152 | 'manual' 153 | ), 154 | ] 155 | 156 | 157 | 158 | # ============================================================================== 159 | # Extensions 160 | # ============================================================================== 161 | extensions = [ 162 | # Standard Sphinx extensions 163 | "sphinx.ext.autodoc", 164 | 'sphinx.ext.extlinks', 165 | 'sphinx.ext.intersphinx', 166 | 'sphinx.ext.inheritance_diagram', 167 | 'sphinx.ext.todo', 168 | 'sphinx.ext.graphviz', 169 | 'sphinx.ext.mathjax', 170 | 'sphinx.ext.ifconfig', 171 | 'sphinx.ext.viewcode', 172 | # 'sphinx.ext.duration', 173 | 174 | # SphinxContrib extensions 175 | # 'sphinxcontrib.actdiag', 176 | 'sphinxcontrib.mermaid', 177 | # 'sphinxcontrib.seqdiag', 178 | # 'sphinxcontrib.textstyle', 179 | # 'sphinxcontrib.spelling', 180 | # 'changelog', 181 | 182 | # BuildTheDocs extensions 183 | # 'btd.sphinx.autoprogram', 184 | # 'btd.sphinx.graphviz', 185 | # 'btd.sphinx.inheritance_diagram', 186 | 187 | # Other extensions 188 | # 'DocumentMember', 189 | 'sphinx_fontawesome', 190 | 'sphinx_autodoc_typehints', 191 | 192 | # local extensions (patched) 193 | # 'autoapi.sphinx', 194 | 195 | # local extensions 196 | # 'DocumentMember' 197 | ] 198 | 199 | # ============================================================================== 200 | # Sphinx.Ext.InterSphinx 201 | # ============================================================================== 202 | intersphinx_mapping = { 203 | 'python': ('https://docs.python.org/3', None), 204 | 'pyTooling': ('http://pyTooling.GitHub.io/pyTooling', None), 205 | } 206 | 207 | 208 | # ============================================================================== 209 | # Sphinx.Ext.AutoDoc 210 | # ============================================================================== 211 | # see: https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html#configuration 212 | autodoc_member_order = "bysource" # alphabetical, groupwise, bysource 213 | 214 | 215 | # ============================================================================== 216 | # Sphinx.Ext.ExtLinks 217 | # ============================================================================== 218 | extlinks = { 219 | "gh": ("https://GitHub.com/%s", "gh:"), 220 | "ghissue": ('https://GitHub.com/pyTooling/pyTooling.TerminalUI/issues/%s', 'issue #'), 221 | "ghpull": ('https://GitHub.com/pyTooling/pyTooling.TerminalUI/pull/%s', 'pull request #'), 222 | "ghsrc": ('https://GitHub.com/pyTooling/pyTooling.TerminalUI/blob/main/pyTooling/%s?ts=2', None), 223 | # "ghtest": ('https://GitHub.com/pyTooling/pyTooling.TerminalUI/blob/main/test/%s?ts=2', None) 224 | } 225 | 226 | 227 | # ============================================================================== 228 | # Sphinx.Ext.Graphviz 229 | # ============================================================================== 230 | graphviz_output_format = "svg" 231 | 232 | 233 | 234 | # ============================================================================== 235 | # Sphinx.Ext.ToDo 236 | # ============================================================================== 237 | # If true, `todo` and `todoList` produce output, else they produce nothing. 238 | todo_include_todos = True 239 | todo_link_only = True 240 | 241 | 242 | 243 | # ============================================================================== 244 | # AutoAPI.Sphinx 245 | # ============================================================================== 246 | autoapi_modules = { 247 | 'pyTooling.TerminalUI': {'output': "pyTooling.TerminalUI", "override": True} 248 | } 249 | -------------------------------------------------------------------------------- /doc/genindex.rst: -------------------------------------------------------------------------------- 1 | .. This file is a placeholder and will be replaced 2 | 3 | Index 4 | ##### 5 | -------------------------------------------------------------------------------- /doc/index.rst: -------------------------------------------------------------------------------- 1 | .. |img-TerminalUI-github| image:: https://img.shields.io/badge/pyTooling-pyTooling.TerminalUI-323131.svg?logo=github&longCache=true 2 | :alt: Sourcecode on GitHub 3 | :height: 22 4 | :target: https://GitHub.com/pyTooling/pyTooling.TerminalUI 5 | .. |img-TerminalUI-codelicense| image:: https://img.shields.io/pypi/l/pyTooling.TerminalUI?logo=GitHub&label=code%20license 6 | :alt: Sourcecode License 7 | :height: 22 8 | .. |img-TerminalUI-tag| image:: https://img.shields.io/github/v/tag/pyTooling/pyTooling.TerminalUI?logo=GitHub&include_prereleases 9 | :alt: GitHub tag (latest SemVer incl. pre-release) 10 | :height: 22 11 | :target: https://GitHub.com/pyTooling/pyTooling.TerminalUI/tags 12 | .. |img-TerminalUI-release| image:: https://img.shields.io/github/v/release/pyTooling/pyTooling.TerminalUI?logo=GitHub&include_prereleases 13 | :alt: GitHub release (latest SemVer incl. including pre-releases 14 | :height: 22 15 | :target: https://GitHub.com/pyTooling/pyTooling.TerminalUI/releases/latest 16 | .. |img-TerminalUI-date| image:: https://img.shields.io/github/release-date/pyTooling/pyTooling.TerminalUI?logo=GitHub 17 | :alt: GitHub release date 18 | :height: 22 19 | :target: https://GitHub.com/pyTooling/pyTooling.TerminalUI/releases 20 | .. |img-TerminalUI-lib-dep| image:: https://img.shields.io/librariesio/dependents/pypi/pyTooling.TerminalUI?logo=librariesdotio 21 | :alt: Dependents (via libraries.io) 22 | :height: 22 23 | :target: https://GitHub.com/pyTooling/pyTooling.TerminalUI/network/dependents 24 | .. |img-TerminalUI-gha-pipeline| image:: https://img.shields.io/github/workflow/status/pyTooling/pyTooling.TerminalUI/Unit%20Testing,%20Coverage%20Collection,%20Package,%20Release,%20Documentation%20and%20Publish?label=Pipeline&logo=GitHub%20Actions&logoColor=FFFFFF 25 | :alt: GitHub Workflow - Build and Test Status 26 | :height: 22 27 | :target: https://GitHub.com/pyTooling/pyTooling.TerminalUI/actions/workflows/Pipeline.yml 28 | .. |img-TerminalUI-codacy-quality| image:: https://img.shields.io/codacy/grade/e8a1b6e33d564f82927235e17fb26e93?logo=Codacy 29 | :alt: Codacy - Quality 30 | :height: 22 31 | :target: https://www.codacy.com/gh/pyTooling/pyTooling.TerminalUI 32 | .. |img-TerminalUI-codacy-coverage| image:: https://img.shields.io/codacy/coverage/e8a1b6e33d564f82927235e17fb26e93?logo=Codacy 33 | :alt: Codacy - Line Coverage 34 | :height: 22 35 | :target: https://www.codacy.com/gh/pyTooling/pyTooling.TerminalUI 36 | .. |img-TerminalUI-codecov-coverage| image:: https://img.shields.io/codecov/c/github/pyTooling/pyTooling.TerminalUI?logo=Codecov 37 | :alt: Codecov - Branch Coverage 38 | :height: 22 39 | :target: https://codecov.io/gh/pyTooling/pyTooling.TerminalUI 40 | .. |img-TerminalUI-lib-rank| image:: https://img.shields.io/librariesio/sourcerank/pypi/pyTooling.TerminalUI?logo=librariesdotio 41 | :alt: Libraries.io SourceRank 42 | :height: 22 43 | :target: https://libraries.io/github/pyTooling/pyTooling.TerminalUI/sourcerank 44 | .. |img-TerminalUI-pypi-tag| image:: https://img.shields.io/pypi/v/pyTooling.TerminalUI?logo=PyPI&logoColor=FBE072 45 | :alt: PyPI - Tag 46 | :height: 22 47 | :target: https://pypi.org/project/pyTooling.TerminalUI/ 48 | .. |img-TerminalUI-pypi-python| image:: https://img.shields.io/pypi/pyversions/pyTooling.TerminalUI?logo=PyPI&logoColor=FBE072 49 | :alt: PyPI - Python Version 50 | :height: 22 51 | .. |img-TerminalUI-pypi-status| image:: https://img.shields.io/pypi/status/pyTooling.TerminalUI?logo=PyPI&logoColor=FBE072 52 | :alt: PyPI - Status 53 | :height: 22 54 | .. |img-TerminalUI-lib-status| image:: https://img.shields.io/librariesio/release/pypi/pyTooling.TerminalUI?logo=librariesdotio 55 | :alt: Libraries.io status for latest release 56 | :height: 22 57 | :target: https://libraries.io/github/pyTooling/pyTooling.TerminalUI 58 | .. |img-TerminalUI-req-status| image:: https://img.shields.io/requires/github/pyTooling/pyTooling.TerminalUI 59 | :alt: Requires.io 60 | :height: 22 61 | :target: https://requires.io/github/pyTooling/pyTooling.TerminalUI/requirements/?branch=main 62 | .. |img-TerminalUI-doclicense| image:: https://img.shields.io/badge/doc%20license-CC--BY%204.0-green?logo=readthedocs 63 | :alt: Documentation License 64 | :height: 22 65 | :target: Doc-License.html 66 | .. |img-TerminalUI-doc| image:: https://img.shields.io/badge/doc-read%20now%20%E2%9E%9A-blueviolet?logo=readthedocs 67 | :alt: Documentation - Read Now! 68 | :height: 22 69 | :target: https://pyTooling.GitHub.io/pyTooling.TerminalUI 70 | 71 | |img-TerminalUI-github| |img-TerminalUI-codelicense| |img-TerminalUI-tag| |img-TerminalUI-release| |img-TerminalUI-date| |img-TerminalUI-lib-dep| |br| 72 | |img-TerminalUI-gha-pipeline| |img-TerminalUI-codacy-quality| |img-TerminalUI-codacy-coverage| |img-TerminalUI-codecov-coverage| |img-TerminalUI-lib-rank| |br| 73 | |img-TerminalUI-pypi-tag| |img-TerminalUI-pypi-python| |img-TerminalUI-pypi-status| |img-TerminalUI-lib-status| |img-TerminalUI-req-status| |br| 74 | |img-TerminalUI-doclicense| |img-TerminalUI-doc| 75 | 76 | 77 | pyTooling.TerminalUI Documentation 78 | ################################## 79 | 80 | A set of helpers to implement a text user interface (TUI) in a terminal. 81 | 82 | Introduction 83 | ************ 84 | 85 | This package offers a :py:class:`pyTooling.TerminalUI.LineTerminal` implementation, 86 | derived from a basic :py:class:`pyTooling.TerminalUI.Terminal` class. It eases the 87 | creation of simple terminal/console applications. It includes colored outputs 88 | based on 89 | 90 | List of meta classes 91 | ******************** 92 | 93 | * :py:class:`pyTooling.TerminalUI.Terminal` 94 | * :py:class:`pyTooling.TerminalUI.LineTerminal` 95 | 96 | 97 | Example 98 | ******* 99 | 100 | .. code-block:: Python 101 | 102 | from pyTooling.TerminalUI import LineTerminal 103 | 104 | class Application(LineTerminal): 105 | def __init__(self): 106 | super().__init__(verbose=True, debug=True, quiet=False) 107 | 108 | def run(self): 109 | self.WriteQuiet("This is a quiet message.") 110 | self.WriteNormal("This is a normal message.") 111 | self.WriteInfo("This is a info message.") 112 | self.WriteDebug("This is a debug message.") 113 | self.WriteWarning("This is a warning message.") 114 | self.WriteError("This is an error message.") 115 | self.WriteFatal("This is a fatal message.") 116 | 117 | # entry point 118 | if __name__ == "__main__": 119 | Application.versionCheck((3,6,0)) 120 | app = Application() 121 | app.run() 122 | app.exit() 123 | 124 | 125 | 126 | Contributors 127 | ************ 128 | 129 | * `Patrick Lehmann `__ (Maintainer) 130 | * `and more... `__ 131 | 132 | 133 | 134 | License 135 | ******* 136 | 137 | .. only:: html 138 | 139 | This Python package (source code) is licensed under `Apache License 2.0 `__. |br| 140 | The accompanying documentation is licensed under `Creative Commons - Attribution 4.0 (CC-BY 4.0) `__. 141 | 142 | .. only:: latex 143 | 144 | This Python package (source code) is licensed under **Apache License 2.0**. |br| 145 | The accompanying documentation is licensed under **Creative Commons - Attribution 4.0 (CC-BY 4.0)**. 146 | 147 | 148 | ------------------------------------ 149 | 150 | .. |docdate| date:: %b %d, %Y - %H:%M 151 | 152 | .. only:: html 153 | 154 | This document was generated on |docdate|. 155 | 156 | .. toctree:: 157 | :caption: Overview 158 | :hidden: 159 | 160 | Installation 161 | Dependencies 162 | 163 | 164 | .. toctree:: 165 | :caption: pyTooling.TerminalUI Classes 166 | :hidden: 167 | 168 | Terminal 169 | Severity 170 | Line 171 | ILineTerminal 172 | LineTerminal 173 | 174 | 175 | .. toctree:: 176 | :caption: Appendix 177 | :hidden: 178 | 179 | Coverage Report ➚ 180 | Static Type Check Report ➚ 181 | License 182 | Doc-License 183 | genindex 184 | py-modindex 185 | -------------------------------------------------------------------------------- /doc/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.http://sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /doc/prolog.inc: -------------------------------------------------------------------------------- 1 | .. # Load pre-defined aliases and graphical characters like © from docutils 2 | # is used to denote the special path 3 | # \Lib\site-packages\docutils\parsers\rst\include 4 | .. include:: 5 | .. include:: 6 | 7 | .. # define a hard line break for HTML 8 | .. |br| raw:: html 9 | 10 |
11 | 12 | .. # define horizontal line for HTML 13 | .. |hr| raw:: html 14 | 15 |
16 | 17 | .. # define additional CSS based styles and ReST roles for HTML 18 | .. raw:: html 19 | 20 | 25 | 26 | .. role:: bolditalic 27 | :class: bolditalic 28 | 29 | .. role:: underline 30 | :class: underline 31 | 32 | .. role:: xlarge 33 | :class: xlarge 34 | -------------------------------------------------------------------------------- /doc/py-modindex.rst: -------------------------------------------------------------------------------- 1 | .. This file is a placeholder and will be replaced 2 | 3 | Python Module Index 4 | ################### 5 | -------------------------------------------------------------------------------- /doc/requirements.txt: -------------------------------------------------------------------------------- 1 | -r ../requirements.txt 2 | 3 | pyTooling>=2.1.0 4 | 5 | # Enforce latest version on ReadTheDocs 6 | sphinx>=5.0.2 7 | 8 | # Sphinx Extenstions 9 | #sphinx.ext.coverage 10 | #sphinxcontrib-actdiag>=0.8.5 11 | sphinxcontrib-mermaid>=0.7.1 12 | #sphinxcontrib-seqdiag>=0.8.5 13 | #sphinxcontrib-textstyle>=0.2.1 14 | #sphinxcontrib-spelling>=2.2.0 15 | autoapi 16 | sphinx_fontawesome>=0.0.6 17 | sphinx_autodoc_typehints>=1.18.1 18 | # changelog>=0.3.5 19 | 20 | # BuildTheDocs Extensions (mostly patched Sphinx extensions) 21 | -------------------------------------------------------------------------------- /doc/shields.inc: -------------------------------------------------------------------------------- 1 | .. # Use http://b64.io/ to encode any image to base64. Then replace `/` with 2 | # `%2F` and `+` with `%2B` (or use http://meyerweb.com/eric/tools/dencoder/). 3 | # Beware that `?logo=data:image/png;base64,` must also be converted to 4 | # percent encoding so that the URL is properly parsed. 5 | 6 | .. # Sourcecode link to GitHub 7 | .. |SHIELD:svg:pyTooling-github| image:: https://img.shields.io/badge/pyTooling-pyTooling.TerminalUI-323131?logo=github&longCache=true 8 | :alt: Sourcecode on GitHub 9 | :height: 22 10 | :target: https://GitHub.com/pyTooling/pyTooling.TerminalUI 11 | .. |SHIELD:png:pyTooling-github| image:: https://raster.shields.io/badge/pyTooling-pyTooling.TerminalUI-323131?logo=github&longCache=true 12 | :alt: Sourcecode on GitHub 13 | :height: 22 14 | :target: https://GitHub.com/pyTooling/pyTooling.TerminalUI 15 | 16 | .. # Sourcecode license 17 | .. |SHIELD:svg:pyTooling-src-license| image:: https://img.shields.io/pypi/l/pyTooling.TerminalUI?logo=GitHub&label=code%20license 18 | :alt: Code license 19 | :height: 22 20 | :target: Code-License.html 21 | .. |SHIELD:png:pyTooling-src-license| image:: https://img.shields.io/pypi/l/pyTooling.TerminalUI?logo=GitHub&label=code%20license 22 | :alt: Code license 23 | :height: 22 24 | :target: https://GitHub.com/pyTooling/pyTooling.TerminalUI/blob/main/LICENSE.md 25 | 26 | .. # GitHub tag 27 | .. |SHIELD:svg:pyTooling-tag| image:: https://img.shields.io/github/v/tag/pyTooling/pyTooling.TerminalUI?logo=GitHub&include_prereleases 28 | :alt: GitHub tag (latest SemVer incl. pre-release 29 | :height: 22 30 | :target: https://GitHub.com/pyTooling/pyTooling.TerminalUI/tags 31 | .. |SHIELD:png:pyTooling-tag| image:: https://raster.shields.io/github/v/tag/pyTooling/pyTooling.TerminalUI?logo=GitHub&include_prereleases 32 | :alt: GitHub tag (latest SemVer incl. pre-release 33 | :height: 22 34 | :target: https://GitHub.com/pyTooling/pyTooling.TerminalUI/tags 35 | 36 | .. # GitHub release 37 | .. |SHIELD:svg:pyTooling-release| image:: https://img.shields.io/github/v/release/pyTooling/pyTooling.TerminalUI?logo=GitHub&include_prereleases 38 | :alt: GitHub release (latest SemVer incl. including pre-releases 39 | :height: 22 40 | :target: https://GitHub.com/pyTooling/pyTooling.TerminalUI/releases/latest 41 | .. |SHIELD:png:pyTooling-release| image:: https://raster.shields.io/github/v/release/pyTooling/pyTooling.TerminalUI?logo=GitHub&include_prereleases 42 | :alt: GitHub release (latest SemVer incl. including pre-releases 43 | :height: 22 44 | :target: https://GitHub.com/pyTooling/pyTooling.TerminalUI/releases/latest 45 | 46 | .. # GitHub release date 47 | .. |SHIELD:svg:pyTooling-date| image:: https://img.shields.io/github/release-date/pyTooling/pyTooling.TerminalUI?logo=GitHub 48 | :alt: GitHub release date 49 | :height: 22 50 | :target: https://GitHub.com/pyTooling/pyTooling.TerminalUI/releases 51 | .. |SHIELD:png:pyTooling-date| image:: https://raster.shields.io/github/release-date/pyTooling/pyTooling.TerminalUI?logo=GitHub 52 | :alt: GitHub release date 53 | :height: 22 54 | :target: https://GitHub.com/pyTooling/pyTooling.TerminalUI/releases 55 | 56 | .. # GitHub/Libraries dependent projects 57 | .. |SHIELD:svg:pyTooling-lib-dep| image:: https://img.shields.io/librariesio/dependent-repos/pypi/pyTooling.TerminalUI 58 | :alt: Dependent repos (via libraries.io) 59 | :height: 22 60 | :target: https://GitHub.com/pyTooling/pyTooling.TerminalUI/network/dependents 61 | .. |SHIELD:png:pyTooling-lib-dep| image:: https://raster.shields.io/librariesio/dependent-repos/pypi/pyTooling.TerminalUI 62 | :alt: Dependent repos (via libraries.io) 63 | :height: 22 64 | :target: https://GitHub.com/pyTooling/pyTooling.TerminalUI/network/dependents 65 | 66 | .. # GHA test and coverage 67 | .. |SHIELD:svg:pyTooling-gha-test| image:: https://img.shields.io/github/workflow/status/pyTooling/pyTooling.TerminalUI/Unit%20Testing,%20Coverage%20Collection,%20Package,%20Release,%20Documentation%20and%20Publish?label=Build%20and%20Test&logo=GitHub%20Actions&logoColor=FFFFFF 68 | :alt: GitHub Workflow - Build and Test Status 69 | :height: 22 70 | :target: https://GitHub.com/pyTooling/pyTooling.TerminalUI/actions?query=workflow%3A%22Unit%20Testing,%20Coverage%20Collection,%20Package,%20Release,%20Documentation%20and%20Publish%22 71 | .. |SHIELD:png:pyTooling-gha-test| image:: https://img.shields.io/github/workflow/status/pyTooling/pyTooling.TerminalUI/Unit%20Testing,%20Coverage%20Collection,%20Package,%20Release,%20Documentation%20and%20Publish?label=Build%20and%20Test&logo=GitHub%20Actions&logoColor=FFFFFF 72 | :alt: GitHub Workflow - Build and Test Status 73 | :height: 22 74 | :target: https://GitHub.com/pyTooling/pyTooling.TerminalUI/actions?query=workflow%3A%22Unit%20Testing,%20Coverage%20Collection,%20Package,%20Release,%20Documentation%20and%20Publish%22 75 | 76 | .. # Codacy - quality 77 | .. |SHIELD:svg:pyTooling-codacy-quality| image:: https://img.shields.io/codacy/grade/08ef744c0b70490289712b02a7a4cebe?logo=codacy 78 | :alt: Codacy - Quality 79 | :height: 22 80 | :target: https://www.codacy.com/gh/pyTooling/pyTooling.TerminalUI 81 | .. |SHIELD:png:pyTooling-codacy-quality| image:: https://raster.shields.io/codacy/grade/08ef744c0b70490289712b02a7a4cebe?logo=codacy 82 | :alt: Codacy - Quality 83 | :height: 22 84 | :target: https://www.codacy.com/gh/pyTooling/pyTooling.TerminalUI 85 | 86 | .. # Codacy - coverage 87 | .. |SHIELD:svg:pyTooling-codacy-coverage| image:: https://img.shields.io/codacy/coverage/08ef744c0b70490289712b02a7a4cebe?logo=codacy 88 | :alt: Codacy - Line Coverage 89 | :height: 22 90 | :target: https://www.codacy.com/gh/pyTooling/pyTooling.TerminalUI 91 | .. |SHIELD:png:pyTooling-codacy-coverage| image:: https://raster.shields.io/codacy/coverage/08ef744c0b70490289712b02a7a4cebe?logo=codacy 92 | :alt: Codacy - Line Coverage 93 | :height: 22 94 | :target: https://www.codacy.com/gh/pyTooling/pyTooling.TerminalUI 95 | 96 | .. # Codecov - coverage 97 | .. |SHIELD:svg:pyTooling-codecov-coverage| image:: https://img.shields.io/codecov/c/github/pyTooling/pyTooling.TerminalUI 98 | :alt: Codecov - Branch Coverage 99 | :height: 22 100 | :target: https://codecov.io/gh/pyTooling/pyTooling.TerminalUI 101 | .. |SHIELD:png:pyTooling-codecov-coverage| image:: https://raster.shields.io/codecov/c/github/pyTooling/pyTooling.TerminalUI 102 | :alt: Codecov - Branch Coverage 103 | :height: 22 104 | :target: https://codecov.io/gh/pyTooling/pyTooling.TerminalUI 105 | 106 | .. # Libraries - source rank 107 | .. |SHIELD:svg:pyTooling-lib-rank| image:: https://img.shields.io/librariesio/sourcerank/pypi/pyTooling.TerminalUI 108 | :alt: Libraries.io SourceRank 109 | :height: 22 110 | :target: https://libraries.io/github/pyTooling/pyTooling.TerminalUI/sourcerank 111 | .. |SHIELD:png:pyTooling-lib-rank| image:: https://raster.shields.io/librariesio/sourcerank/pypi/pyTooling.TerminalUI 112 | :alt: Libraries.io SourceRank 113 | :height: 22 114 | :target: https://libraries.io/github/pyTooling/pyTooling.TerminalUI/sourcerank 115 | 116 | .. # PyPI tag 117 | .. |SHIELD:svg:pyTooling-pypi-tag| image:: https://img.shields.io/pypi/v/pyTooling.TerminalUI?logo=PyPI 118 | :alt: PyPI - Tag 119 | :height: 22 120 | :target: https://pypi.org/project/pyTooling.TerminalUI/ 121 | .. |SHIELD:png:pyTooling-pypi-tag| image:: https://raster.shields.io/pypi/v/pyTooling.TerminalUI?logo=PyPI 122 | :alt: PyPI - Tag 123 | :height: 22 124 | :target: https://pypi.org/project/pyTooling.TerminalUI/ 125 | 126 | .. # PyPI project status 127 | .. |SHIELD:svg:pyTooling-pypi-status| image:: https://img.shields.io/pypi/status/pyTooling.TerminalUI?logo=PyPI 128 | :alt: PyPI - Status 129 | :height: 22 130 | .. |SHIELD:png:pyTooling-pypi-status| image:: https://raster.shields.io/pypi/status/pyTooling.TerminalUI?logo=PyPI 131 | :alt: PyPI - Status 132 | :height: 22 133 | 134 | .. # PyPI Python versions 135 | .. |SHIELD:svg:pyTooling-pypi-python| image:: https://img.shields.io/pypi/pyversions/pyTooling.TerminalUI?logo=PyPI 136 | :alt: PyPI - Python Version 137 | :height: 22 138 | .. |SHIELD:png:pyTooling-pypi-python| image:: https://raster.shields.io/pypi/pyversions/pyTooling.TerminalUI?logo=PyPI 139 | :alt: PyPI - Python Version 140 | :height: 22 141 | 142 | .. # Libraries - status 143 | .. |SHIELD:svg:pyTooling-lib-status| image:: https://img.shields.io/librariesio/release/pypi/pyTooling.TerminalUI 144 | :alt: Libraries.io status for latest release 145 | :height: 22 146 | :target: https://libraries.io/github/pyTooling/pyTooling.TerminalUI 147 | .. |SHIELD:png:pyTooling-lib-status| image:: https://raster.shields.io/librariesio/release/pypi/pyTooling.TerminalUI 148 | :alt: Libraries.io status for latest release 149 | :height: 22 150 | :target: https://libraries.io/github/pyTooling/pyTooling.TerminalUI 151 | 152 | .. # Requires - status 153 | .. |SHIELD:svg:pyTooling-req-status| image:: https://img.shields.io/requires/github/pyTooling/pyTooling.TerminalUI 154 | :alt: Requires.io 155 | :height: 22 156 | :target: https://requires.io/github/pyTooling/pyTooling.TerminalUI/requirements/?branch=main 157 | .. |SHIELD:png:pyTooling-req-status| image:: https://raster.shields.io/requires/github/pyTooling/pyTooling.TerminalUI 158 | :alt: Requires.io 159 | :height: 22 160 | :target: https://requires.io/github/pyTooling/pyTooling.TerminalUI/requirements/?branch=main 161 | 162 | .. # Documentation license 163 | .. |SHIELD:svg:pyTooling-doc-license| image:: https://img.shields.io/badge/doc%20license-CC--BY%204.0-green 164 | :alt: Documentation License 165 | :height: 22 166 | :target: Doc-License.html 167 | .. |SHIELD:png:pyTooling-doc-license| image:: https://img.shields.io/badge/doc%20license-CC--BY%204.0-green 168 | :alt: Documentation License 169 | :height: 22 170 | :target: https://GitHub.com/pyTooling/pyTooling.TerminalUI/blob/main/doc/Doc-License.rst 171 | 172 | .. # GHPages - read now 173 | .. |SHIELD:svg:pyTooling-ghp-doc| image:: https://img.shields.io/badge/doc-read%20now%20%E2%9E%9A-blueviolet 174 | :alt: Documentation - Read Now! 175 | :height: 22 176 | :target: https://pyTooling.GitHub.io/pyTooling.TerminalUI/ 177 | .. |SHIELD:png:pyTooling-ghp-doc| image:: https://img.shields.io/badge/doc-read%20now%20%E2%9E%9A-blueviolet 178 | :alt: Documentation - Read Now! 179 | :height: 22 180 | :target: https://pyTooling.GitHub.io/pyTooling.TerminalUI/ 181 | -------------------------------------------------------------------------------- /pyTooling/TerminalUI/__init__.py: -------------------------------------------------------------------------------- 1 | # ==================================================================================================================== # 2 | # _____ _ _ _____ _ _ _ _ ___ # 3 | # _ __ _ |_ _|__ ___ | (_)_ __ __ _|_ _|__ _ __ _ __ ___ (_)_ __ __ _| | | | |_ _| # 4 | # | '_ \| | | || |/ _ \ / _ \| | | '_ \ / _` | | |/ _ \ '__| '_ ` _ \| | '_ \ / _` | | | | || | # 5 | # | |_) | |_| || | (_) | (_) | | | | | | (_| |_| | __/ | | | | | | | | | | | (_| | | |_| || | # 6 | # | .__/ \__, ||_|\___/ \___/|_|_|_| |_|\__, (_)_|\___|_| |_| |_| |_|_|_| |_|\__,_|_|\___/|___| # 7 | # |_| |___/ |___/ # 8 | # ==================================================================================================================== # 9 | # Authors: # 10 | # Patrick Lehmann # 11 | # # 12 | # License: # 13 | # ==================================================================================================================== # 14 | # Copyright 2017-2022 Patrick Lehmann - Bötzingen, Germany # 15 | # # 16 | # Licensed under the Apache License, Version 2.0 (the "License"); # 17 | # you may not use this file except in compliance with the License. # 18 | # You may obtain a copy of the License at # 19 | # # 20 | # http://www.apache.org/licenses/LICENSE-2.0 # 21 | # # 22 | # Unless required by applicable law or agreed to in writing, software # 23 | # distributed under the License is distributed on an "AS IS" BASIS, # 24 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # 25 | # See the License for the specific language governing permissions and # 26 | # limitations under the License. # 27 | # # 28 | # SPDX-License-Identifier: Apache-2.0 # 29 | # ==================================================================================================================== # 30 | # 31 | """A set of helpers to implement a text user interface (TUI) in a terminal.""" 32 | __author__ = "Patrick Lehmann" 33 | __email__ = "Paebbels@gmail.com" 34 | __copyright__ = "2007-2022, Patrick Lehmann" 35 | __license__ = "Apache License, Version 2.0" 36 | __version__ = "1.5.9" 37 | __keywords__ = ["terminal", "shell", "text user interface", "TUI", "console", "message logging"] 38 | 39 | from enum import Enum, unique 40 | from platform import system as platform_system 41 | from typing import NoReturn, Tuple, Any 42 | 43 | from pyTooling.Decorators import export 44 | from pyTooling.MetaClasses import ExtendedType 45 | 46 | 47 | @export 48 | class Terminal: 49 | FATAL_EXIT_CODE = 255 50 | 51 | try: 52 | from colorama import Fore as Foreground 53 | Foreground = { 54 | "RED": Foreground.LIGHTRED_EX, 55 | "DARK_RED": Foreground.RED, 56 | "GREEN": Foreground.LIGHTGREEN_EX, 57 | "DARK_GREEN": Foreground.GREEN, 58 | "YELLOW": Foreground.LIGHTYELLOW_EX, 59 | "DARK_YELLOW": Foreground.YELLOW, 60 | "MAGENTA": Foreground.LIGHTMAGENTA_EX, 61 | "BLUE": Foreground.LIGHTBLUE_EX, 62 | "DARK_BLUE": Foreground.BLUE, 63 | "CYAN": Foreground.LIGHTCYAN_EX, 64 | "DARK_CYAN": Foreground.CYAN, 65 | "GRAY": Foreground.WHITE, 66 | "DARK_GRAY": Foreground.LIGHTBLACK_EX, 67 | "WHITE": Foreground.LIGHTWHITE_EX, 68 | "NOCOLOR": Foreground.RESET, 69 | 70 | "HEADLINE": Foreground.LIGHTMAGENTA_EX, 71 | "ERROR": Foreground.LIGHTRED_EX, 72 | "WARNING": Foreground.LIGHTYELLOW_EX 73 | } #: Terminal colors 74 | except: 75 | Foreground = { 76 | "RED": "", 77 | "DARK_RED": "", 78 | "GREEN": "", 79 | "DARK_GREEN": "", 80 | "YELLOW": "", 81 | "DARK_YELLOW": "", 82 | "MAGENTA": "", 83 | "BLUE": "", 84 | "DARK_BLUE": "", 85 | "CYAN": "", 86 | "DARK_CYAN": "", 87 | "GRAY": "", 88 | "DARK_GRAY": "", 89 | "WHITE": "", 90 | "NOCOLOR": "", 91 | 92 | "HEADLINE": "", 93 | "ERROR": "", 94 | "WARNING": "" 95 | } #: Terminal colors 96 | 97 | _width : int = None #: Terminal width in characters 98 | _height : int = None #: Terminal height in characters 99 | 100 | def __init__(self): 101 | """ 102 | Initialize a terminal. 103 | 104 | If the Python package `colorama `_ [#f_colorama]_ is available, then initialize it for colored outputs. 105 | 106 | .. [#f_colorama] Colorama on Github: https://GitHub.com/tartley/colorama 107 | """ 108 | 109 | self.initColors() 110 | (self._width, self._height) = self.GetTerminalSize() 111 | 112 | @classmethod 113 | def initColors(cls) -> None: 114 | """Initialize the terminal for color support by colorama.""" 115 | try: 116 | from colorama import init 117 | 118 | init()#strip=False) 119 | except: 120 | pass 121 | 122 | @classmethod 123 | def deinitColors(cls) -> None: 124 | """Uninitialize the terminal for color support by colorama.""" 125 | try: 126 | from colorama import deinit 127 | 128 | deinit() 129 | except: 130 | pass 131 | 132 | @classmethod 133 | def fatalExit(cls, returnCode:int =0) -> NoReturn: 134 | """Exit the terminal application by uninitializing color support and returning a fatal exit code.""" 135 | cls.exit(cls.FATAL_EXIT_CODE if returnCode == 0 else returnCode) 136 | 137 | @classmethod 138 | def exit(cls, returnCode:int =0) -> NoReturn: 139 | """Exit the terminal application by uninitializing color support and returning an exit code.""" 140 | cls.deinitColors() 141 | exit(returnCode) 142 | 143 | @classmethod 144 | def versionCheck(cls, version) -> None: 145 | """Check if the used Python interpreter fulfills the minimum version requirements.""" 146 | 147 | from sys import version_info 148 | 149 | if (version_info < version): 150 | cls.initColors() 151 | 152 | print("{RED}ERROR:{NOCOLOR} Used Python interpreter ({major}.{minor}.{micro}-{level}) is to old.".format( 153 | major=version_info.major, 154 | minor=version_info.minor, 155 | micro=version_info.micro, 156 | level=version_info.releaselevel, 157 | **cls.Foreground 158 | )) 159 | print(f" Minimal required Python version is {version[0]}.{version[1]}.{version[2]}") 160 | 161 | cls.exit(1) 162 | 163 | @classmethod 164 | def printException(cls, ex) -> NoReturn: 165 | """Prints an exception of type :exc:`Exception`.""" 166 | from traceback import print_tb, walk_tb 167 | 168 | cls.initColors() 169 | 170 | print("{RED}FATAL: An unknown or unhandled exception reached the topmost exception handler!{NOCOLOR}".format(**cls.Foreground)) 171 | print(" {YELLOW}Exception type:{NOCOLOR} {typename}".format(typename=ex.__class__.__name__, **cls.Foreground)) 172 | print(" {YELLOW}Exception message:{NOCOLOR} {message!s}".format(message=ex, **cls.Foreground)) 173 | frame, sourceLine = [x for x in walk_tb(ex.__traceback__)][-1] 174 | filename = frame.f_code.co_filename 175 | funcName = frame.f_code.co_name 176 | print(" {YELLOW}Caused in:{NOCOLOR} {function} in file '{filename}' at line {line}".format( 177 | function=funcName, 178 | filename=filename, 179 | line=sourceLine, 180 | **cls.Foreground 181 | )) 182 | if (ex.__cause__ is not None): 183 | print(" {DARK_YELLOW}Caused by type:{NOCOLOR} {typename}".format(typename=ex.__cause__.__class__.__name__, **cls.Foreground)) 184 | print(" {DARK_YELLOW}Caused by message:{NOCOLOR} {message!s}".format(message=ex.__cause__, **cls.Foreground)) 185 | print(("{RED}" + ("-" * 80) + "{NOCOLOR}").format(**cls.Foreground)) 186 | print_tb(ex.__traceback__) 187 | print(("{RED}" + ("-" * 80) + "{NOCOLOR}").format(**cls.Foreground)) 188 | print(("{RED}Please report this bug at GitHub: https://GitHub.com/pyTooling/pyTooling.TerminalUI/issues{NOCOLOR}").format(**cls.Foreground)) 189 | print(("{RED}" + ("-" * 80) + "{NOCOLOR}").format(**cls.Foreground)) 190 | 191 | cls.exit(1) 192 | 193 | @classmethod 194 | def printNotImplementedError(cls, ex) -> NoReturn: 195 | """Prints a not-implemented exception of type :exc:`NotImplementedError`.""" 196 | from traceback import walk_tb 197 | 198 | cls.initColors() 199 | 200 | frame, _ = [x for x in walk_tb(ex.__traceback__)][-1] 201 | filename = frame.f_code.co_filename 202 | funcName = frame.f_code.co_name 203 | print("{RED}NOT IMPLEMENTED:{NOCOLOR} {function} in file '{filename}': {message!s}".format( 204 | function=funcName, 205 | filename=filename, 206 | message=ex, 207 | **cls.Foreground 208 | )) 209 | print(("{RED}" + ("-" * 80) + "{NOCOLOR}").format(**cls.Foreground)) 210 | print(("{RED}Please report this bug at GitHub: https://GitHub.com/pyTooling/pyTooling.TerminalUI/issues{NOCOLOR}").format(**cls.Foreground)) 211 | print(("{RED}" + ("-" * 80) + "{NOCOLOR}").format(**cls.Foreground)) 212 | 213 | cls.exit(1) 214 | 215 | @classmethod 216 | def printExceptionBase(cls, ex) -> NoReturn: 217 | cls.initColors() 218 | 219 | print("{RED}FATAL: A known but unhandled exception reached the topmost exception handler!{NOCOLOR}".format(**cls.Foreground)) 220 | print("{RED}ERROR:{NOCOLOR} {message}".format(message=ex.message, **cls.Foreground)) 221 | print(("{RED}" + ("-" * 80) + "{NOCOLOR}").format(**cls.Foreground)) 222 | print(("{RED}Please report this bug at GitHub: https://GitHub.com/pyTooling/pyTooling.TerminalUI/issues{NOCOLOR}").format(**cls.Foreground)) 223 | print(("{RED}" + ("-" * 80) + "{NOCOLOR}").format(**cls.Foreground)) 224 | 225 | cls.exit(1) 226 | 227 | @property 228 | def Width(self) -> int: 229 | """Returns the current terminal window's width.""" 230 | return self._width 231 | 232 | @property 233 | def Height(self) -> int: 234 | """Returns the current terminal window's height.""" 235 | return self._height 236 | 237 | @staticmethod 238 | def GetTerminalSize() -> Tuple[int, int]: 239 | """Returns the terminal size as tuple (width, height) for Windows, Mac OS (Darwin), Linux, cygwin (Windows), MinGW32/64 (Windows).""" 240 | size = None 241 | 242 | platform = platform_system() 243 | if (platform == "Windows"): 244 | size = Terminal.__GetTerminalSizeOnWindows() 245 | elif ((platform in ["Linux", "Darwin"]) or 246 | platform.startswith("CYGWIN") or 247 | platform.startswith("MINGW32") or 248 | platform.startswith("MINGW64")): 249 | size = Terminal.__GetTerminalSizeOnLinux() 250 | 251 | if (size is None): 252 | size = (80, 25) # default size 253 | return size 254 | 255 | @staticmethod 256 | def __GetTerminalSizeOnWindows() -> Tuple[int, int]: 257 | """Returns the current terminal window's size for Windows.""" 258 | try: 259 | from ctypes import windll, create_string_buffer 260 | from struct import unpack as struct_unpack 261 | 262 | hStdError = windll.kernel32.GetStdHandle(-12) # stderr handle = -12 263 | stringBuffer = create_string_buffer(22) 264 | result = windll.kernel32.GetConsoleScreenBufferInfo(hStdError, stringBuffer) 265 | if result: 266 | (bufx, bufy, curx, cury, wattr, left, top, right, bottom, maxx, maxy) = struct_unpack("hhhhHhhhhhh", stringBuffer.raw) 267 | width = right - left + 1 268 | height = bottom - top + 1 269 | return (width, height) 270 | except: 271 | pass 272 | 273 | return Terminal.__GetTerminalSizeWithTPut() 274 | 275 | @staticmethod 276 | def __GetTerminalSizeWithTPut() -> Tuple[int, int]: 277 | from shlex import split as shlex_split 278 | from subprocess import check_output 279 | 280 | try: 281 | width = int(check_output(shlex_split('tput cols'))) 282 | height = int(check_output(shlex_split('tput lines'))) 283 | return (width, height) 284 | except: 285 | pass 286 | 287 | @staticmethod 288 | def __GetTerminalSizeOnLinux() -> Tuple[int, int]: 289 | """Returns the current terminal window's size for Linux.""" 290 | import os 291 | 292 | def ioctl_GWINSZ(fd): 293 | """GetWindowSize of file descriptor.""" 294 | try: 295 | from fcntl import ioctl as fcntl_ioctl 296 | from struct import unpack as struct_unpack 297 | from termios import TIOCGWINSZ 298 | 299 | return struct_unpack('hh', fcntl_ioctl(fd, TIOCGWINSZ, '1234')) 300 | except: 301 | pass 302 | 303 | # STDIN STDOUT STDERR 304 | cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2) 305 | if not cr: 306 | try: 307 | 308 | fd = os.open(os.ctermid(), os.O_RDONLY) 309 | cr = ioctl_GWINSZ(fd) 310 | os.close(fd) 311 | except: 312 | pass 313 | if not cr: 314 | try: 315 | cr = (os.environ['LINES'], os.environ['COLUMNS']) 316 | except: 317 | return None 318 | return (int(cr[1]), int(cr[0])) 319 | 320 | 321 | @export 322 | @unique 323 | class Severity(Enum): 324 | """Logging message severity levels.""" 325 | 326 | Fatal = 30 #: Fatal messages 327 | Error = 25 #: Error messages 328 | Quiet = 20 #: Always visible messages, even in quiet mode. 329 | Warning = 15 #: Warning messages 330 | Info = 10 #: Informative messages 331 | DryRun = 5 #: Messages visible in a dry-run 332 | Normal = 4 #: Normal messages 333 | Verbose = 2 #: Verbose messages 334 | Debug = 1 #: Debug messages 335 | All = 0 #: All messages 336 | 337 | def __hash__(self): 338 | return hash(self.name) 339 | 340 | def __eq__(self, other: Any): 341 | if isinstance(other, Severity): 342 | return self.value == other.value 343 | else: 344 | raise TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by == operator.") 345 | 346 | def __ne__(self, other: Any): 347 | if isinstance(other, Severity): 348 | return self.value != other.value 349 | else: 350 | raise TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by != operator.") 351 | 352 | def __lt__(self, other: Any): 353 | if isinstance(other, Severity): 354 | return self.value < other.value 355 | else: 356 | raise TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by < operator.") 357 | 358 | def __le__(self, other: Any): 359 | if isinstance(other, Severity): 360 | return self.value <= other.value 361 | else: 362 | raise TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by <= operator.") 363 | 364 | def __gt__(self, other: Any): 365 | if isinstance(other, Severity): 366 | return self.value > other.value 367 | else: 368 | raise TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by > operator.") 369 | 370 | def __ge__(self, other: Any): 371 | if isinstance(other, Severity): 372 | return self.value >= other.value 373 | else: 374 | raise TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by >= operator.") 375 | 376 | 377 | @export 378 | class Line: 379 | """Represents a single line message with a severity and indentation level.""" 380 | 381 | _LOG_MESSAGE_FORMAT__ = { 382 | Severity.Fatal: "FATAL: {message}", 383 | Severity.Error: "ERROR: {message}", 384 | Severity.Warning: "WARNING: {message}", 385 | Severity.Info: "INFO: {message}", 386 | Severity.Quiet: "{message}", 387 | Severity.Normal: "{message}", 388 | Severity.Verbose: "VERBOSE: {message}", 389 | Severity.Debug: "DEBUG: {message}", 390 | Severity.DryRun: "DRYRUN: {message}" 391 | } #: Terminal messages formatting rules 392 | 393 | def __init__(self, message, severity=Severity.Normal, indent=0, appendLinebreak=True): 394 | """Constructor for a new ``Line`` object.""" 395 | self._severity = severity 396 | self._message = message 397 | self._indent = indent 398 | self.AppendLinebreak = appendLinebreak 399 | 400 | 401 | @property 402 | def Severity(self) -> Severity: 403 | """Return the line's severity level.""" 404 | return self._severity 405 | 406 | @property 407 | def Indent(self) -> int: 408 | """Return the line's indentation level.""" 409 | return self._indent 410 | 411 | @property 412 | def Message(self) -> str: 413 | """Return the indented line.""" 414 | return (" " * self._indent) + self._message 415 | 416 | def IndentBy(self, indent) -> None: 417 | """Increase a line's indentation level.""" 418 | self._indent += indent 419 | 420 | def __str__(self) -> str: 421 | """Returns a formatted version of a ``Line`` objects as a string.""" 422 | return self._LOG_MESSAGE_FORMAT__[self._severity].format(message=self._message) 423 | 424 | 425 | @export 426 | class ILineTerminal: 427 | """A mixin class (interface) to provide class-local terminal writing methods.""" 428 | 429 | _terminal = None 430 | 431 | def __init__(self, terminal=None): 432 | """MixIn initializer.""" 433 | self._terminal = terminal 434 | 435 | # FIXME: Alter methods if a terminal is present or set dummy methods 436 | 437 | @property 438 | def Terminal(self) -> Terminal: 439 | """Return the local terminal instance.""" 440 | return self._terminal 441 | 442 | def WriteLine(self, line : Line, condition=True): 443 | """Write an entry to the local terminal.""" 444 | if ((self._terminal is not None) and condition): 445 | return self._terminal.WriteLine(line) 446 | return False 447 | 448 | # def _TryWriteLine(self, *args, condition=True, **kwargs): 449 | # if ((self._terminal is not None) and condition): 450 | # return self._terminal.TryWrite(*args, **kwargs) 451 | # return False 452 | 453 | def WriteFatal(self, *args, condition=True, **kwargs): 454 | """Write a fatal message if ``condition`` is true.""" 455 | if ((self._terminal is not None) and condition): 456 | return self._terminal.WriteFatal(*args, **kwargs) 457 | return False 458 | 459 | def WriteError(self, *args, condition=True, **kwargs): 460 | """Write an error message if ``condition`` is true.""" 461 | if ((self._terminal is not None) and condition): 462 | return self._terminal.WriteError(*args, **kwargs) 463 | return False 464 | 465 | def WriteWarning(self, *args, condition=True, **kwargs): 466 | """Write a warning message if ``condition`` is true.""" 467 | if ((self._terminal is not None) and condition): 468 | return self._terminal.WriteWarning(*args, **kwargs) 469 | return False 470 | 471 | def WriteInfo(self, *args, condition=True, **kwargs): 472 | """Write a info message if ``condition`` is true.""" 473 | if ((self._terminal is not None) and condition): 474 | return self._terminal.WriteInfo(*args, **kwargs) 475 | return False 476 | 477 | def WriteQuiet(self, *args, condition=True, **kwargs): 478 | """Write a message even in quiet mode if ``condition`` is true.""" 479 | if ((self._terminal is not None) and condition): 480 | return self._terminal.WriteQuiet(*args, **kwargs) 481 | return False 482 | 483 | def WriteNormal(self, *args, condition=True, **kwargs): 484 | """Write a *normal* message if ``condition`` is true.""" 485 | if ((self._terminal is not None) and condition): 486 | return self._terminal.WriteNormal(*args, **kwargs) 487 | return False 488 | 489 | def WriteVerbose(self, *args, condition=True, **kwargs): 490 | """Write a verbose message if ``condition`` is true.""" 491 | if ((self._terminal is not None) and condition): 492 | return self._terminal.WriteVerbose(*args, **kwargs) 493 | return False 494 | 495 | def WriteDebug(self, *args, condition=True, **kwargs): 496 | """Write a debug message if ``condition`` is true.""" 497 | if ((self._terminal is not None) and condition): 498 | return self._terminal.WriteDebug(*args, **kwargs) 499 | return False 500 | 501 | def WriteDryRun(self, *args, condition=True, **kwargs): 502 | """Write a dry-run message if ``condition`` is true.""" 503 | if ((self._terminal is not None) and condition): 504 | return self._terminal.WriteDryRun(*args, **kwargs) 505 | return False 506 | 507 | 508 | @export 509 | class LineTerminal(Terminal, ILineTerminal, metaclass=ExtendedType, singleton=True): 510 | def __init__(self, verbose=False, debug=False, quiet=False, writeToStdOut=True): 511 | """Initializer of a line based terminal interface.""" 512 | Terminal.__init__(self) 513 | ILineTerminal.__init__(self, self) 514 | 515 | self._verbose = True if debug else verbose 516 | self._debug = debug 517 | self._quiet = quiet 518 | 519 | if quiet: 520 | self._WriteLevel = Severity.Quiet 521 | elif debug: 522 | self._WriteLevel = Severity.Debug 523 | elif verbose: 524 | self._WriteLevel = Severity.Verbose 525 | else: 526 | self._WriteLevel = Severity.Normal 527 | 528 | self._writeToStdOut = writeToStdOut 529 | self._lines = [] 530 | self._baseIndent = 0 531 | 532 | self._errorCounter = 0 533 | self._warningCounter = 0 534 | 535 | @property 536 | def Verbose(self) -> bool: 537 | """Returns true, if verbose messages are enabled.""" 538 | return self._verbose 539 | 540 | @property 541 | def Debug(self) -> bool: 542 | """Returns true, if debug messages are enabled.""" 543 | return self._debug 544 | 545 | @property 546 | def Quiet(self) -> bool: 547 | """Returns true, if quiet mode is enabled.""" 548 | return self._quiet 549 | 550 | @property 551 | def LogLevel(self) -> Severity: 552 | """Return the current minimal severity level for writing.""" 553 | return self._WriteLevel 554 | @LogLevel.setter 555 | def LogLevel(self, value: Severity) -> None: 556 | """Set the minimal severity level for writing.""" 557 | self._WriteLevel = value 558 | 559 | @property 560 | def BaseIndent(self) -> int: 561 | return self._baseIndent 562 | @BaseIndent.setter 563 | def BaseIndent(self, value: int) -> None: 564 | self._baseIndent = value 565 | 566 | _LOG_MESSAGE_FORMAT__ = { 567 | Severity.Fatal: "{DARK_RED}[FATAL] {message}{NOCOLOR}", 568 | Severity.Error: "{RED}[ERROR] {message}{NOCOLOR}", 569 | Severity.Quiet: "{WHITE}{message}{NOCOLOR}", 570 | Severity.Warning: "{YELLOW}[WARNING]{message}{NOCOLOR}", 571 | Severity.Info: "{WHITE}{message}{NOCOLOR}", 572 | Severity.DryRun: "{DARK_CYAN}[DRY] {message}{NOCOLOR}", 573 | Severity.Normal: "{WHITE}{message}{NOCOLOR}", 574 | Severity.Verbose: "{GRAY}{message}{NOCOLOR}", 575 | Severity.Debug: "{DARK_GRAY}{message}{NOCOLOR}" 576 | } #: Message formatting rules. 577 | 578 | def ExitOnPreviousErrors(self) -> None: 579 | """Exit application if errors have been printed.""" 580 | if self._errorCounter > 0: 581 | self.WriteFatal("Too many errors in previous steps.") 582 | self.fatalExit() 583 | 584 | def ExitOnPreviousWarnings(self) -> None: 585 | """Exit application if warnings have been printed.""" 586 | if self._warningCounter > 0: 587 | self.WriteError("Too many warnings in previous steps.") 588 | self.exit() 589 | 590 | def WriteLine(self, line : Line): 591 | """Print a formatted line to the underlying terminal/console offered by the operating system.""" 592 | if (line.Severity >= self._WriteLevel): 593 | self._lines.append(line) 594 | if self._writeToStdOut: 595 | print(self._LOG_MESSAGE_FORMAT__[line.Severity].format(message=line.Message, **self.Foreground), end="\n" if line.AppendLinebreak else "") 596 | return True 597 | else: 598 | return False 599 | 600 | def TryWriteLine(self, line) -> bool: 601 | return (line.Severity >= self._WriteLevel) 602 | 603 | def WriteFatal(self, message, indent=0, appendLinebreak=True, immediateExit=True): 604 | ret = self.WriteLine(Line(message, Severity.Fatal, self._baseIndent + indent, appendLinebreak)) 605 | if immediateExit: 606 | self.fatalExit() 607 | return ret 608 | 609 | def WriteError(self, message, indent=0, appendLinebreak=True): 610 | self._errorCounter += 1 611 | return self.WriteLine(Line(message, Severity.Error, self._baseIndent + indent, appendLinebreak)) 612 | 613 | def WriteWarning(self, message, indent=0, appendLinebreak=True): 614 | self._warningCounter += 1 615 | return self.WriteLine(Line(message, Severity.Warning, self._baseIndent + indent, appendLinebreak)) 616 | 617 | def WriteInfo(self, message, indent=0, appendLinebreak=True): 618 | return self.WriteLine(Line(message, Severity.Info, self._baseIndent + indent, appendLinebreak)) 619 | 620 | def WriteQuiet(self, message, indent=0, appendLinebreak=True): 621 | return self.WriteLine(Line(message, Severity.Quiet, self._baseIndent + indent, appendLinebreak)) 622 | 623 | def WriteNormal(self, message, indent=0, appendLinebreak=True): 624 | return self.WriteLine(Line(message, Severity.Normal, self._baseIndent + indent, appendLinebreak)) 625 | 626 | def WriteVerbose(self, message, indent=1, appendLinebreak=True): 627 | return self.WriteLine(Line(message, Severity.Verbose, self._baseIndent + indent, appendLinebreak)) 628 | 629 | def WriteDebug(self, message, indent=2, appendLinebreak=True): 630 | return self.WriteLine(Line(message, Severity.Debug, self._baseIndent + indent, appendLinebreak)) 631 | 632 | def WriteDryRun(self, message, indent=2, appendLinebreak=True): 633 | return self.WriteLine(Line(message, Severity.DryRun, self._baseIndent + indent, appendLinebreak)) 634 | -------------------------------------------------------------------------------- /pyTooling/TerminalUI/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyTooling/pyTooling.TerminalUI/6a01c78dcbc6ac9a51cf67a79d18b89484a2547b/pyTooling/TerminalUI/py.typed -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = [ 3 | "pyTooling >= 2.1.0", 4 | "setuptools >= 60.9.3", 5 | "wheel >= 0.37.1" 6 | ] 7 | build-backend = "setuptools.build_meta" 8 | 9 | [tool.black] 10 | line-length = 120 11 | 12 | [tool.pytest.ini_options] 13 | # Don't set 'python_classes = *' otherwise, pytest doesn't search for classes 14 | # derived from unittest.Testcase 15 | python_files = "*" 16 | python_functions = "test_*" 17 | 18 | [tool.coverage.run] 19 | branch = true 20 | omit = [ 21 | "*site-packages*", 22 | "setup.py" 23 | ] 24 | 25 | [tool.coverage.report] 26 | skip_covered = true 27 | skip_empty = true 28 | exclude_lines = [ 29 | "raise NotImplementedError" 30 | ] 31 | 32 | [tool.coverage.html] 33 | directory = "report/coverage/html" 34 | 35 | [tool.coverage.xml] 36 | output = "report/coverage/coverage.xml" 37 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | colorama>=0.4.5 2 | 3 | pyTooling>=2.1.0 4 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # ============================================================================= 2 | # _____ _ _ _____ _ _ _ _ ___ 3 | # _ __ _ |_ _|__ ___ | (_)_ __ __ _|_ _|__ _ __ _ __ ___ (_)_ __ __ _| | | | |_ _| 4 | # | '_ \| | | || |/ _ \ / _ \| | | '_ \ / _` | | |/ _ \ '__| '_ ` _ \| | '_ \ / _` | | | | || | 5 | # | |_) | |_| || | (_) | (_) | | | | | | (_| |_| | __/ | | | | | | | | | | | (_| | | |_| || | 6 | # | .__/ \__, ||_|\___/ \___/|_|_|_| |_|\__, (_)_|\___|_| |_| |_| |_|_|_| |_|\__,_|_|\___/|___| 7 | # |_| |___/ |___/ 8 | # ============================================================================= 9 | # Authors: Patrick Lehmann 10 | # 11 | # Package installer: A set of helpers to implement a text user interface (TUI) in a terminal. 12 | # 13 | # License: 14 | # ============================================================================ 15 | # Copyright 2017-2022 Patrick Lehmann - Bötzingen, Germany 16 | # 17 | # Licensed under the Apache License, Version 2.0 (the "License"); 18 | # you may not use this file except in compliance with the License. 19 | # You may obtain a copy of the License at 20 | # 21 | # http://www.apache.org/licenses/LICENSE-2.0 22 | # 23 | # Unless required by applicable law or agreed to in writing, software 24 | # distributed under the License is distributed on an "AS IS" BASIS, 25 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 26 | # See the License for the specific language governing permissions and 27 | # limitations under the License. 28 | # 29 | # SPDX-License-Identifier: Apache-2.0 30 | # ============================================================================ 31 | # 32 | from pathlib import Path 33 | from pyTooling.Packaging import DescribePythonPackageHostedOnGitHub 34 | 35 | gitHubNamespace = "pyTooling" 36 | packageName = "pyTooling.TerminalUI" 37 | packageDirectory = packageName.replace(".", "/") 38 | packageInformationFile = Path(f"{packageDirectory}/__init__.py") 39 | 40 | DescribePythonPackageHostedOnGitHub( 41 | packageName=packageName, 42 | description="A set of helpers to implement a text user interface (TUI) in a terminal.", 43 | gitHubNamespace=gitHubNamespace, 44 | sourceFileWithVersion=packageInformationFile, 45 | dataFiles={ 46 | packageName: ["py.typed"] 47 | } 48 | ) 49 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | # ============================================================================= 2 | # _____ _ _ _ _ ___ 3 | # _ __ _ |_ _|__ _ __ _ __ ___ (_)_ __ __ _| | | | |_ _| 4 | # | '_ \| | | || |/ _ \ '__| '_ ` _ \| | '_ \ / _` | | | | || | 5 | # | |_) | |_| || | __/ | | | | | | | | | | | (_| | | |_| || | 6 | # | .__/ \__, ||_|\___|_| |_| |_| |_|_|_| |_|\__,_|_|\___/|___| 7 | # |_| |___/ 8 | # ============================================================================= 9 | # Authors: Patrick Lehmann 10 | # 11 | # Python unittest: Helper functions for unittests 12 | # 13 | # Description: 14 | # ------------------------------------ 15 | # TODO 16 | # 17 | # License: 18 | # ============================================================================ 19 | # Copyright 2017-2022 Patrick Lehmann - Bötzingen, Germany 20 | # Copyright 2007-2016 Patrick Lehmann - Dresden, Germany 21 | # 22 | # Licensed under the Apache License, Version 2.0 (the "License"); 23 | # you may not use this file except in compliance with the License. 24 | # You may obtain a copy of the License at 25 | # 26 | # http://www.apache.org/licenses/LICENSE-2.0 27 | # 28 | # Unless required by applicable law or agreed to in writing, software 29 | # distributed under the License is distributed on an "AS IS" BASIS, 30 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 31 | # See the License for the specific language governing permissions and 32 | # limitations under the License. 33 | # 34 | # SPDX-License-Identifier: Apache-2.0 35 | # ============================================================================ 36 | # 37 | """ 38 | Module containing test code written for `pyTest `__. 39 | 40 | :copyright: Copyright 2007-2022 Patrick Lehmann - Bötzingen, Germany 41 | :license: Apache License, Version 2.0 42 | """ 43 | -------------------------------------------------------------------------------- /tests/requirements.txt: -------------------------------------------------------------------------------- 1 | -r ../requirements.txt 2 | 3 | # Coverage collection 4 | Coverage>=6.3 5 | 6 | # Test Runner 7 | pytest>=7.1.2 8 | pytest-cov>=3.0.0 9 | 10 | # Static Type Checking 11 | mypy>=0.931 12 | lxml>=4.9 13 | -------------------------------------------------------------------------------- /tests/unit/TerminalUI.py: -------------------------------------------------------------------------------- 1 | # ============================================================================= 2 | # _____ _ _ _ _ ___ 3 | # _ __ _ |_ _|__ _ __ _ __ ___ (_)_ __ __ _| | | | |_ _| 4 | # | '_ \| | | || |/ _ \ '__| '_ ` _ \| | '_ \ / _` | | | | || | 5 | # | |_) | |_| || | __/ | | | | | | | | | | | (_| | | |_| || | 6 | # | .__/ \__, ||_|\___|_| |_| |_| |_|_|_| |_|\__,_|_|\___/|___| 7 | # |_| |___/ 8 | # ============================================================================= 9 | # Authors: Patrick Lehmann 10 | # 11 | # Python unittest: Testing the pyTooling.TerminalUI module 12 | # 13 | # License: 14 | # ============================================================================ 15 | # Copyright 2017-2022 Patrick Lehmann - Bötzingen, Germany 16 | # Copyright 2007-2016 Patrick Lehmann - Dresden, Germany 17 | # 18 | # Licensed under the Apache License, Version 2.0 (the "License"); 19 | # you may not use this file except in compliance with the License. 20 | # You may obtain a copy of the License at 21 | # 22 | # http://www.apache.org/licenses/LICENSE-2.0 23 | # 24 | # Unless required by applicable law or agreed to in writing, software 25 | # distributed under the License is distributed on an "AS IS" BASIS, 26 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 27 | # See the License for the specific language governing permissions and 28 | # limitations under the License. 29 | # 30 | # SPDX-License-Identifier: Apache-2.0 31 | # ============================================================================ 32 | # 33 | """ 34 | pyTooling.TerminalUI 35 | #################### 36 | 37 | :copyright: Copyright 2007-2022 Patrick Lehmann - Bötzingen, Germany 38 | :license: Apache License, Version 2.0 39 | """ 40 | from unittest import TestCase 41 | 42 | from pyTooling.TerminalUI import LineTerminal 43 | 44 | 45 | if __name__ == "__main__": # pragma: no cover 46 | print("ERROR: you called a testcase declaration file as an executable module.") 47 | print("Use: 'python -m unitest '") 48 | exit(1) 49 | 50 | 51 | class Application(LineTerminal): 52 | def __init__(self): 53 | super().__init__(verbose=True, debug=True, quiet=False) 54 | 55 | LineTerminal.FATAL_EXIT_CODE = 0 56 | 57 | 58 | class Terminal(TestCase): 59 | app : Application 60 | 61 | def setUp(self) -> None: 62 | self.app = Application() 63 | 64 | def test_Version(self) -> None: 65 | Application.versionCheck((3, 6, 0)) 66 | 67 | def test_Write(self) -> None: 68 | self.app.WriteQuiet("This is a quiet message.") 69 | self.app.WriteNormal("This is a normal message.") 70 | self.app.WriteInfo("This is a info message.") 71 | self.app.WriteDebug("This is a debug message.") 72 | self.app.WriteWarning("This is a warning message.") 73 | self.app.WriteError("This is an error message.") 74 | self.app.WriteFatal("This is a fatal message.", immediateExit=False) 75 | -------------------------------------------------------------------------------- /tests/unit/__init__.py: -------------------------------------------------------------------------------- 1 | # ============================================================================= 2 | # _____ _ _ _ _ ___ 3 | # _ __ _ |_ _|__ _ __ _ __ ___ (_)_ __ __ _| | | | |_ _| 4 | # | '_ \| | | || |/ _ \ '__| '_ ` _ \| | '_ \ / _` | | | | || | 5 | # | |_) | |_| || | __/ | | | | | | | | | | | (_| | | |_| || | 6 | # | .__/ \__, ||_|\___|_| |_| |_| |_|_|_| |_|\__,_|_|\___/|___| 7 | # |_| |___/ 8 | # ============================================================================= 9 | # Authors: Patrick Lehmann 10 | # 11 | # Python unittest: Helper functions for unittests 12 | # 13 | # License: 14 | # ============================================================================ 15 | # Copyright 2017-2022 Patrick Lehmann - Bötzingen, Germany 16 | # Copyright 2007-2016 Patrick Lehmann - Dresden, Germany 17 | # 18 | # Licensed under the Apache License, Version 2.0 (the "License"); 19 | # you may not use this file except in compliance with the License. 20 | # You may obtain a copy of the License at 21 | # 22 | # http://www.apache.org/licenses/LICENSE-2.0 23 | # 24 | # Unless required by applicable law or agreed to in writing, software 25 | # distributed under the License is distributed on an "AS IS" BASIS, 26 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 27 | # See the License for the specific language governing permissions and 28 | # limitations under the License. 29 | # 30 | # SPDX-License-Identifier: Apache-2.0 31 | # ============================================================================ 32 | # 33 | """ 34 | pyTooling.TerminalUI 35 | #################### 36 | 37 | :copyright: Copyright 2007-2022 Patrick Lehmann - Bötzingen, Germany 38 | :license: Apache License, Version 2.0 39 | """ 40 | --------------------------------------------------------------------------------