From e55be747c5843e6e7fc9c39e3e0d0687ce3f0325 Mon Sep 17 00:00:00 2001 From: KOLANICH Date: Sun, 22 Jan 2017 23:33:00 +0300 Subject: [PATCH] Initial commit --- .ci/pythonPackagesToInstallFromGit.txt | 3 + .editorconfig | 12 + .github/.templateMarker | 1 + .github/dependabot.yml | 8 + .github/workflows/CI.yml | 15 + .gitignore | 7 + .gitlab-ci.yml | 59 ++ Code_Of_Conduct.md | 1 + MANIFEST.in | 4 + ReadMe.md | 51 ++ RichConsole.hx | 867 +++++++++++++++++++++++++ RichConsole.py | 782 ++++++++++++++++++++++ Tutorial.ipynb | 397 +++++++++++ UNLICENSE | 24 + pyproject.toml | 45 ++ tests/tests.py | 92 +++ 16 files changed, 2368 insertions(+) create mode 100644 .ci/pythonPackagesToInstallFromGit.txt create mode 100755 .editorconfig create mode 100644 .github/.templateMarker create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/CI.yml create mode 100644 .gitignore create mode 100644 .gitlab-ci.yml create mode 100644 Code_Of_Conduct.md create mode 100644 MANIFEST.in create mode 100644 ReadMe.md create mode 100644 RichConsole.hx create mode 100755 RichConsole.py create mode 100644 Tutorial.ipynb create mode 100644 UNLICENSE create mode 100644 pyproject.toml create mode 100755 tests/tests.py diff --git a/.ci/pythonPackagesToInstallFromGit.txt b/.ci/pythonPackagesToInstallFromGit.txt new file mode 100644 index 0000000..756f3bd --- /dev/null +++ b/.ci/pythonPackagesToInstallFromGit.txt @@ -0,0 +1,3 @@ +https://github.com/tartley/colorama.git +https://github.com/tomerfiliba/plumbum.git +https://gitlab.com/dslackw/colored.git diff --git a/.editorconfig b/.editorconfig new file mode 100755 index 0000000..c9162b9 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +charset = utf-8 +indent_style = tab +indent_size = 4 +insert_final_newline = true +end_of_line = lf + +[*.{yml,yaml}] +indent_style = space +indent_size = 2 diff --git a/.github/.templateMarker b/.github/.templateMarker new file mode 100644 index 0000000..5e3a3e0 --- /dev/null +++ b/.github/.templateMarker @@ -0,0 +1 @@ +KOLANICH/python_project_boilerplate.py diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..89ff339 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,8 @@ +version: 2 +updates: + - package-ecosystem: "pip" + directory: "/" + schedule: + interval: "daily" + allow: + - dependency-type: "all" diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml new file mode 100644 index 0000000..7fe33b3 --- /dev/null +++ b/.github/workflows/CI.yml @@ -0,0 +1,15 @@ +name: CI +on: + push: + branches: [master] + pull_request: + branches: [master] + +jobs: + build: + runs-on: ubuntu-22.04 + steps: + - name: typical python workflow + uses: KOLANICH-GHActions/typical-python-workflow@master + with: + github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..606eb48 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +__pycache__ +*.pyc +*.pyo +/*.egg-info +/build +/dist +/.eggs diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..e080673 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,59 @@ +image: pypy:latest +#image: registry.gitlab.com/kolanich-subgroups/docker-images/fixed_python:latest +#image: registry.gitlab.com/kolanich-subgroups/docker-images/python_graal_docker:latest + + +variables: + DOCKER_DRIVER: overlay2 + SAST_ANALYZER_IMAGE_TAG: latest + SAST_DISABLE_DIND: "true" + SAST_CONFIDENCE_LEVEL: 5 + CODECLIMATE_VERSION: latest + +include: + - template: SAST.gitlab-ci.yml + - template: Code-Quality.gitlab-ci.yml + +build: + tags: + - shared + - linux + stage: build + variables: + GIT_DEPTH: "1" + PYTHONUSERBASE: ${CI_PROJECT_DIR}/python_user_packages + + before_script: + - export PATH="$PATH:$PYTHONUSERBASE/bin" # don't move into `variables` + - git clone https://github.com/pypa/setuptools.git + - cd setuptools + - pypy3 ./bootstrap.py + - pypy3 ./setup.py bdist_wheel + - pip install --upgrade --pre ./dist/*.whl + - cd .. + - rm -rf ./setuptools + - pip3 install --user --pre --upgrade git+https://github.com/pypa/setuptools_scm.git + - pip3 install --user --pre --upgrade git+https://github.com/pypa/pip.git git+https://github.com/pypa/wheel.git + - pip3 install --user --pre --upgrade pep517 + - pip3 install --pre --upgrade --user git+https://github.com/pytest-dev/pytest.git + + script: + - pypy3 -m pep517.build . + #- pypy3 ./setup.py bdist_wheel + - mv ./dist/*.whl ./dist/RichConsole-0.CI-py3-none-any.whl + - pip3 install --user --upgrade --pre "./dist/RichConsole-0.CI-py3-none-any.whl[colorama,plumbum,colored]" + - coverage run -a --branch --source=RichConsole -m pytest --junitxml=./rspec.xml ./tests/tests.py + - coverage run -a --branch --source=RichConsole -m RichConsole + - coverage xml + + cache: + paths: + - /usr/local/site-packages + - /usr/local/lib/python*/site-packages + + artifacts: + paths: + - dist + reports: + junit: ./rspec.xml + cobertura: ./coverage.xml diff --git a/Code_Of_Conduct.md b/Code_Of_Conduct.md new file mode 100644 index 0000000..bcaa2bf --- /dev/null +++ b/Code_Of_Conduct.md @@ -0,0 +1 @@ +No codes of conduct! \ No newline at end of file diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..20f0fa8 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,4 @@ +include UNLICENSE +include *.md +include tests +include .editorconfig diff --git a/ReadMe.md b/ReadMe.md new file mode 100644 index 0000000..191d072 --- /dev/null +++ b/ReadMe.md @@ -0,0 +1,51 @@ +RichConsole.py [![Unlicensed work](https://raw.githubusercontent.com/unlicense/unlicense.org/master/static/favicon.png)](https://unlicense.org/) +=============== +~~[wheel (GHA via `nightly.link`)](https://nightly.link/KOLANICH-libs/RichConsole.py/workflows/CI/master/RichConsole-0.CI-py3-none-any.whl)~~ +~~[![GitLab Build Status](https://gitlab.com/KOLANICH/RichConsole.py/badges/master/pipeline.svg)]( https://gitlab.com/KOLANICH/RichConsole.py/pipelines/master/latest)~~ +~~![GitLab Coverage](https://gitlab.com/KOLANICH/RichConsole.py/badges/master/coverage.svg)~~ +~~[![GitHub Actions](https://github.com/KOLANICH-libs/RichConsole.py/workflows/CI/badge.svg)](https://github.com/KOLANICH-libs/RichConsole.py/actions/)~~ +![N∅ hard dependencies](https://shields.io/badge/-N∅_Ъ_deps!-0F0) +[![Libraries.io Status](https://img.shields.io/librariesio/github/KOLANICH-libs/RichConsole.py.svg)](https://libraries.io/github/KOLANICH-libs/RichConsole.py) +[![Code style: antiflash](https://img.shields.io/badge/code%20style-antiflash-FFF.svg)](https://github.com/KOLANICH-tools/antiflash.py) + +>Yo dawg so we heard you like text styles so we put styles in your styles so you can style while you styling. + +This is a tool to output "poor" (because it is limited by standardized control codes, which are very limited) rich text into a console. When dealing with control codes there is a problem with nesting styles because you have to restore the state, and the state you have to restore depends on the style of the level much distant from the one you are in. This library solves this problem. + +You create a [directed acyclic graph](https://en.wikipedia.org/wiki/Directed_acyclic_graph) structure `RichStr` where each piece of string has its own [style`Sheet`](https://en.wikipedia.org/wiki/Style_sheet_(desktop_publishing)). After you have finished forming the output message you convert it into a string. The library does the rest. + +How does it work? +----------------- + +The algorithm is damn simple: it just traverses the directed acyclic graph in depth-first way, determines exact style of each string, computes differences between them and emits control codes to apply them. + +Requirements +------------ +* A terminal supporting color codes. + * Any Linux distro usually has one + * Windows: + * Windows 10 [has built-in support](https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences) + * [ansicon](https://github.com/adoxa/ansicon) or [ConEmu](https://github.com/Maximus5/ConEmu) or [MinTTY](https://github.com/mintty/mintty) for older Windows + * you can call [colorama.init()](https://github.com/tartley/colorama) to enable filtering the output with python, but this is VERY glitchy. It raises exceptions even on simple strings. The good thing in it that it supports more codes than `ansicon`. + +Optional requirements +--------------------- +This library automatically imports colors and other control codes from the following libraries: +* [`colorama`](https://github.com/tartley/colorama/) + [![PyPi Status](https://img.shields.io/pypi/v/colorama.svg)](https://pypi.org/pypi/colorama) + [![Build Status](https://github.com/tartley/colorama/actions/workflows/test.yml/badge.svg)](https://github.com/tartley/colorama/actions/workflows/test.yml) + ![License](https://img.shields.io/github/license/tartley/colorama.svg) + +* [`plumbum.colorlib`](https://github.com/tomerfiliba/plumbum/) + [![PyPi Status](https://img.shields.io/pypi/v/plumbum.svg)](https://pypi.org/pypi/plumbum) + [![Build Status](https://github.com/tomerfiliba/plumbum/workflows/CI/badge.svg)](https://github.com/tomerfiliba/plumbum/actions) + [![Docs](https://readthedocs.org/projects/plumbum/badge/)](https://plumbum.readthedocs.io/en/latest/) + ![License](https://img.shields.io/github/license/tomerfiliba/plumbum.svg) + +* [`colored`](https://gitlab.com/dslackw/colored/) + [![PyPi Status](https://img.shields.io/pypi/v/colored.svg)](https://pypi.org/pypi/colored) + [![GitLab Build Status](https://gitlab.com/dslackw/colored/badges/master/pipeline.svg)](https://gitlab.com/dslackw/colored/pipelines/master/latest) + +Tutorial +-------- +See [`Tutorial.ipynb`](./Tutorial.ipynb) ([![NBViewer](https://nbviewer.org/static/ico/ipynb_icon_16x16.png)](https://nbviewer.jupyter.org/github/KOLANICH-libs/RichConsole.py/blob/master/Tutorial.ipynb)). diff --git a/RichConsole.hx b/RichConsole.hx new file mode 100644 index 0000000..4ee16e5 --- /dev/null +++ b/RichConsole.hx @@ -0,0 +1,867 @@ + /* + __all__ = ("ControlCodes", "Style", "Color", "BasicColor", "IndexedColor", "RGBColor", "StyleGroup", "groups", "Sheet", "RichStr", "rsjoin", "neutral", "neutralGroup", "neutralSheet") + + */ + +//import IterTools; +import haxe.rtti.Meta; +import python.Syntax; +import python.lib.Builtins; +import python.internal.ArrayImpl; +import python.Dict; +import python.Tuple; +import python.Exceptions; + +class Meta{ + public static final __author__: String="KOLANICH"; + public static final __license__: String="Unlicense"; + public static final __copyright__: String=" + This is free and unencumbered software released into the public domain. + + Anyone is free to copy, modify, publish, use, compile, sell, or + distribute this software, either in source code form or as a compiled + binary, for any purpose, commercial or non-commercial, and by any + means. + + In jurisdictions that recognize copyright laws, the author or authors + of this software dedicate any and all copyright interest in the + software to the public domain. We make this dedication for the benefit + of the public at large and to the detriment of our heirs and + successors. We intend this dedication to be an overt act of + relinquishment in perpetuity of all present and future rights to this + software under copyright law. + + THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + + For more information, please refer to + "; + +} + +extern class Object { + public var __dict__ : Dict; + public var __class__ : Type; +} +extern class PythonType extends Type { + public function mro(): Array; +} + +@doc("Represents a sequence of control codes") +class ControlCodes extends Object{ + public var codes : Tuple; + public function new(codes:Dynamic){ + if(!Std.is(codes, Iterable)){ + if(Std.is(codes, UInt)){ + codes = [codes]; + }else{ + throw new Builtins.TypeError("The argument must be either tuple of ints or an int"); + } + } + this.codes=Tuple(codes); + } + + @:to + public function toString():String{ + if(this.codes){ + return ["\x1b[",[for (c in this.codes) Std.string(c) ].join(";"),"m"].join(""); + }else{ + return ""; + } + } + + @:to + public function __repr__():String{ + return [Type.getClassName(Type.getClass(this)), "(", Builtins.repr(this.codes), ")"].join(""); + } + + @:op(A == B) + public function __eq__(other){ + return this.codes==other.codes; + } + public function __hash__(){ + return Builtins.hash(this.codes); + } + + @:op(A + B) + public function __add__(other:ControlCodes){ + //if( Type.getClass(this) == Type.getClass(other)){ + // this.codes+=other.codes + // return Type.createInstance(Type.getClass(this), [this.codes+other.codes]) + //}else + //return __class__(this.codes+other.codes); + return new ControlCodes(this.codes+other.codes); + + } +} + +@doc("Represents a style of a string. Its groups contain groups of active styles") +class Style extends ControlCodes{ + public var name : String; + public var group : StyleGroup; + public function new(name:String, codes:Iterable, group:StyleGroup=null){ + super(codes); + this.name=name; + this.group=group; + } + public function toCSSProperty(){ + return {}; + } + public function __repr__(){ + return "".join( + [ + Std.string(this), + (if (this.group && this.group.name) (this.group.name+":") else ""), + this.name, + (if (this.group && this.group.reset) Std.string(this.group.reset) else "\x1b[0m") + ] + ); + } + + @:op(A + B) + public function __add__(other:Style){ + if(this.group == other.group){ + //return __class__(this.name+"&"+other.name, this.codes+other.codes); + return new Style(this.name+"&"+other.name, this.codes+other.codes); + }else{ + //return (__class__.mro()[1])(this.codes+other.codes); + return new ControlCodes(this.codes+other.codes); + } + } + public function __call__(strs:Tuple){ + return new RichStr(strs, this); + } +} + +@doc("Represents a storage allowing access by both . and [] notation") +class Storage extends Object implements haxe.Constraints.IMap{ + public function new(val=null){ + if( val == null ){ + val=new Map(); + } + if( Std.is(val, haxe.Constraints.IMap) ){ + this.__dict__=Type.createInstance(Type.getClass(this.__dict__), [val]); + } + } + public function iterator(){ + return Builtins.iter(this.__dict__); + } + + @:arrayAccess + public function get(key:String):Dynamic{ + return this.__dict__[key]; + } + + @:arrayAccess + public function set(key:String, val:Dynamic){ + this.__dict__[key]=val; + } + + + public function __delitem__(key:String){ + Builtins.del(this.__dict__[key]); + } + + public function __hasitem__(item){ + return item in this.__dict__; + } + + public function __len__(){ + return Builtins.len(this.__dict__); + } + public function values(){ + return this.__dict__.values(); + } + public function keys(){ + return this.__dict__.keys(); + } + public function __repr__(){ + return Type.getClassName(Type.getClass(this))+"("+Builtins.repr(this.__dict__)+")"; + } +} + +@doc("Represent a group of mutually exclusive styles. ```reset``` == dedicated style returning style to default") +class StyleGroup extends Storage{ + public var reset:ControlCodes; + public var name:String; + public function new(name:String, styles:Iterable